在事务中使用OrientDB-NET.binary创建边

时间:2014-11-06 22:47:35

标签: c# orientdb

我正在尝试使用OrientDB-NET.binary,扩展它以提高开发团队的易用性。我正在尝试创建类似于的语法,就像我们用于MSSQL和MySQL连接的那样。这应该使我们的开发团队更容易在两种技术之间进行交换。

我也喜欢我的扩展功能以进行交易。

这是我现在的Insert扩展程序:

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是一个抽象类,旨在由我的应用程序中的所有模型类进行扩展。 OEdgeAttribute是一个属性,可让我指定属性是否代表&#39; in&#39;边缘或者是一个&#39; out&#39;在顶点上的边缘。 ORID_DEFAULT等于new ORID()

上面的扩展方法效果很好......主要是。使用以下模型类:

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; }
}

然后我进口&#39;通过映射表中另一组条目映射数百Dependent

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();

运行此代码后,我的数据库包含273个从属记录(扩展名为V的Person)及其属性的正确值,546地址记录(扩展V)及其属性的正确值,273个ResidenceAddress记录(扩展PropertyAddress)使用正确的out和in rids扩展E),并使用正确的out和in rids扩展273 ShippingAddress记录(扩展PropertyAddress)。

但是,没有任何依赖项或地址可以看到连接它们的边。 traverse * #29:0提出第一个受抚养人,但没有提出他的住所或送货地址。 select expand(out('ResidenceAddress')) from Dependent返回空结果集。

经过一些修补后,似乎原因是因为OTransaction.Add在提交交易时基本上执行了insert into {0},而不是create vertex {0}create edge {0}

我当然可以将我的代码更改为使用ODatabase.Create.Vertex()ODatabase.Create.Edge()而不是OTransaction.Add(),但在这种情况下我不必提交事务,而是调用OSqlCreateEdge.Run()OSqlCreateVertex.Run(),它立即处理记录创建,回滚不是一个选项。

在使用交易时,有没有办法正确创建边缘(而不是简单地插入记录)?

2 个答案:

答案 0 :(得分:2)

通过事务创建繁重的边缘您需要在文档之间设置适当的链接

看这里的例子

        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();

答案 1 :(得分:1)

我最好把我的手作为OrientDBNet-Binary中OTransaction课程的作者,并尽我所能提供帮助。

首先,出于性能原因,您肯定希望尝试使用OTransaction而不是ODatabase.Create.Vertex等 - 这就是为什么我首先添加了事务支持,因为它是10次以上比通过单个插入添加数据更快。

其次,编写OTransaction类是为了支持我正在处理的生产项目,所以当我达到我自己的代码所需的功能级别时,我没有进一步推进它。这意味着以您正在进行的方式创建自定义边缘对象不是我在编写OTransaction类时设计或测试的。

至于前进的想法 -

  • 如果您没有在边缘使用自定义属性,则无需首先创建边缘对象,只需将链接两端顶点上的ORID设置为相互指向(基本上使用轻量级边缘)
  • 如果您觉得能够对OrientDBNet-Binary项目进行代码更改,请随意这样做并提交带有更改的拉取请求。