问题
I am trying to use OrientDB-NET.binary, extending it to improve ease-of-use among my development team. I am trying to create syntax which is similar to dapper, as that's what we're using for our MSSQL and MySQL connections. This should make it easier on our development team swapping between the two technologies.
I'd also like my extensions to function with transactions.
Here's my Insert
extension as it is now:
public static void Insert<T>(this ODatabase db, T model,
OTransaction transaction) where T : ABaseModel, new()
{
InsertHelper(db, model, transaction, new List<object>());
}
private static void InsertHelper<T>(ODatabase db, T model,
OTransaction transaction, ICollection<object> exclude, ORID parent = null)
where T : ABaseModel, new()
{
// Avoid following loops into a stack overflow
if (exclude.Contains(model)) return;
exclude.Add(model);
ODocument record = new ODocument();
record.OClassName = model.GetType().Name;
PropertyInfo[] properties = model.GetType().GetProperties(
BindingFlags.Public | BindingFlags.Instance |
BindingFlags.SetProperty | BindingFlags.GetProperty);
ICollection<PropertyInfo> linkableProperties = new List<PropertyInfo>();
foreach (PropertyInfo prop in properties)
{
if (reservedProperties.Contains(prop.Name)) continue;
OProperty aliasProperty = prop.GetCustomAttributes(typeof(OProperty))
.Where(attr => ((OProperty)attr).Alias != null)
.FirstOrDefault() as OProperty;
string name = aliasProperty == null ? prop.Name : aliasProperty.Alias;
// Record properties of model, but store properties linking to other
// vertex classes for later
if (typeof(ABaseModel).IsAssignableFrom(prop.PropertyType))
{
linkableProperties.Add(prop);
}
else
{
record[name] = prop.GetValue(model);
}
}
transaction.Add(record);
model.ORID = record.ORID;
foreach (PropertyInfo prop in linkableProperties)
{
ORID outV, inV;
ABaseModel propValue = prop.GetValue(model) as ABaseModel;
if (!exclude.Select(ex => ex is ABaseModel ? ((ABaseModel)ex).ORID :
ORID_DEFAULT).Contains(propValue.ORID))
{
MethodInfo insertMethod = typeof(DatabaseExtensions)
.GetMethod("InsertHelper", BindingFlags.NonPublic |
BindingFlags.Static).MakeGenericMethod(propValue.GetType());
insertMethod.Invoke(null,
new object[] {
db, propValue, transaction, exclude, model.ORID
});
}
outV = model.ORID;
inV = propValue.ORID;
OEdgeAttribute edgeType =
prop.GetCustomAttributes(typeof(OEdgeAttribute))
.FirstOrDefault() as OEdgeAttribute;
OProperty propertyAlias = prop.GetCustomAttributes(typeof(OProperty))
.Where(p => ((OProperty)p).Alias != null)
.FirstOrDefault() as OProperty;
string alias = propertyAlias == null ? prop.Name : propertyAlias.Alias;
if (edgeType != null)
{
OEdge link = new OEdge();
link.OClassName = alias;
link["out"] = outV;
link["in"] = inV;
if(edgeType.IsInV)
{
ORID tmp = link.OutV;
link["out"] = link.InV;
link["in"] = tmp;
}
// Do not create an edge if there is an edge already
// connecting these vertices
IEnumerable<Tuple<ORID,ORID>> excludedLinks = exclude
.Select(ex => ex is OEdge ?
new Tuple<ORID,ORID>(((OEdge)ex).OutV,((OEdge)ex).InV) :
new Tuple<ORID,ORID>(ORID_DEFAULT, ORID_DEFAULT));
if (excludedLinks.Contains(
new Tuple<ORID, ORID>(link.OutV, link.InV))) continue;
exclude.Add(link);
transaction.Add(link);
}
}
}
ABaseModel
is an abstract class intended to be extended by all model classes in my application. OEdgeAttribute
is an attribute which lets me specify if a property is representing an 'in' edge or an 'out' edge on a vertex. ORID_DEFAULT
is equal to new ORID()
The above extension method works just fine... mostly. Using the following model classes:
public class Person : ABaseModel
{
[OProperty(Alias = "ResidenceAddress")]
[OEdge(IsOutV = true)]
public Address Residence { get; set; }
[OProperty(Alias = "ShippingAddress")]
[OEdge(IsOutV = true)]
public Address Shipping { get; set; }
}
public class Dependent : Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
// etc...
}
public class Address : ABaseModel
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
// etc...
[OProperty(Alias = "PropertyAddress")]
public Person Resident { get; set; }
}
I then 'import' several hundred Dependent
s by mapping from another set of entries in the table:
OClient.CreateDatabasePool("127.0.0.1", 2424, "persephone", ODatabaseType.Graph,
"admin", "admin", 10, "Persephone");
// PersephoneConnection has access to ODatabase and OTransaction,
// and some methods which dispatch to them
IPersephoneConnection persephone = new PersephoneConnection();
persephone.BeginTransaction();
// Traverse is a functioning extension method which essentially calls
// `traverse * from {0}` and maps the result to model objects
IEnumerable<tmpED> eds = persephone.Connection.Traverse<tmpED>("tmpED");
foreach (tmpED model in eds)
{
Dependent dep = new Dependent
{
FirstName = model.firstname,
LastName = model.lastname,
// etc...
};
Address residence = new Address
{
AddressLine1 = model.addres_line1,
AddressLine2 = model.address_line2,
// etc...
Resident = dep
};
dep.Residence = residence;
Address shipping = new Address
{
AddressLine1 = model.shippingaddress_line1,
AddressLine2 = model.shippingaddres_line2,
// etc...
Resident = dep
};
dep.Shipping = shipping;
persephone.Connection.Insert(dep, persephone.Transaction);
}
persephone.Commit();
persephone.Dispose();
After running this code, my database contains 273 Dependent records (extends Person which extends V) with the correct values for their properties, 546 Address records (extends V) with the correct values for their properties, 273 ResidenceAddress records (extends PropertyAddress which extends E) with the correct out and in rids, and 273 ShippingAddress records (extends PropertyAddress) with the correct out and in rids.
However, none of the Dependents or Addresses can see the edges which connect them. traverse * #29:0
brings up the first Dependent, but doesn't bring up his residence or shipping address. select expand(out('ResidenceAddress')) from Dependent
returns an empty result set.
After some tinkering, it appears that the cause of this is because OTransaction.Add
is essentially performing an insert into {0}
when the transaction is committed, rather than a create vertex {0}
or create edge {0}
.
I can certainly change my code to use ODatabase.Create.Vertex()
and ODatabase.Create.Edge()
instead of OTransaction.Add()
, but instead of committing a transaction in that case, I have to call OSqlCreateEdge.Run()
or OSqlCreateVertex.Run()
, which processes the record creation immediately, and a rollback isn't an option.
Is there any way to get my edges created properly (as opposed to simply inserted as records) while still using transactions?
回答1:
To Create heavy edges via Transaction You need proper set Links between documents
Look example here
var v1 = new ODocument { OClassName = "TestVertex" };
v1.SetField("Name", "First");
v1.SetField("Bar", 1);
var v2 = new ODocument { OClassName = "TestVertex" };
v2.SetField("Name", "Second");
v2.SetField("Bar", 2);
var e1 = new ODocument { OClassName = "TestEdge" };
e1.SetField("Weight", 1.3f);
// Add records to the transaction
_database.Transaction.Add(v1);
_database.Transaction.Add(v2);
_database.Transaction.Add(e1);
// link records
v1.SetField("in_TestEdge", e1.ORID);
v2.SetField("out_TestEdge", e1.ORID);
e1.SetField("in", v1.ORID);
e1.SetField("out", v2.ORID);
_database.Transaction.Commit();
回答2:
I'd better put my hand up as author of the OTransaction
class in the OrientDBNet-Binary and give what assistance I can.
First off, you definitely want to try to use OTransaction
rather than ODatabase.Create.Vertex
etc for performance reasons - that's why I added transaction support in the first place as it is 10+ times faster than adding data via individual inserts.
Secondly, the OTransaction
class was written to support a production project I am working on, so when I reached the functionality level that I needed for my own code I didn't push it forward any further. This means that the creation of custom edge objects in the way you are doing is not something I designed for or tested for when writing the OTransaction
class.
As for ideas to move forward -
- If you are not using custom properties on your edges, then there is no need to create the edge objects in the first place, just set the ORIDs on the vertices at each end of the link to point to each other (basically using Lightweight edges)
- If you feel able to make the code changes to the OrientDBNet-Binary project then feel free to do so and submit a pull request with the changes.
来源:https://stackoverflow.com/questions/26790891/creating-edges-with-orientdb-net-binary-in-a-transaction