Creating Edges with OrientDB-NET.binary in a transaction

不打扰是莪最后的温柔 提交于 2020-06-01 01:50:28

问题


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 Dependents 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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!