Entity Framework 4.1+ 多对多关系变更跟踪 [英] Entity Framework 4.1+ many-to-many relationships change tracking

查看:24
本文介绍了Entity Framework 4.1+ 多对多关系变更跟踪的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何检测 ICollection<> 属性(多对多关系)的变化?

How can I detect changes of ICollection<> properties (many-to-many relationships)?

public class Company
{
    ...

    public virtual ICollection<Employee> Employees { get; set; }
}

using (DataContext context = new DataContext(Properties.Settings.Default.ConnectionString))
{
    Company company = context.Companies.First();
    company.Employees.Add(context.Employees.First());

    context.SaveChanges();
}

public class DataContext : DbContext
{
    public override int SaveChanges()
    {
        return base.SaveChanges();

        // Company's entity state is "Unchanged" in this.ChangeTracker
    }
}

推荐答案

这里是如何找到所有改变的多对多关系.我已经将代码实现为扩展方法:

Here is how to find all the changed many-to-many relationships. I've implemented the code as extension methods:

public static class IaExtensions
{
    public static IEnumerable<Tuple<object, object>> GetAddedRelationships(
        this DbContext context)
    {
        return GetRelationships(context, EntityState.Added, (e, i) => e.CurrentValues[i]);
    }

    public static IEnumerable<Tuple<object, object>> GetDeletedRelationships(
        this DbContext context)
    {
        return GetRelationships(context, EntityState.Deleted, (e, i) => e.OriginalValues[i]);
    }

    private static IEnumerable<Tuple<object, object>> GetRelationships(
        this DbContext context,
        EntityState relationshipState,
        Func<ObjectStateEntry, int, object> getValue)
    {
        context.ChangeTracker.DetectChanges();
        var objectContext = ((IObjectContextAdapter)context).ObjectContext;

        return objectContext
            .ObjectStateManager
            .GetObjectStateEntries(relationshipState)
            .Where(e => e.IsRelationship)
            .Select(
                e => Tuple.Create(
                    objectContext.GetObjectByKey((EntityKey)getValue(e, 0)),
                    objectContext.GetObjectByKey((EntityKey)getValue(e, 1))));
    }
}

一些解释.多对多关系在 EF 中表示为独立关联或 IA.这是因为关系的外键没有在对象模型的任何地方公开.在数据库中,FK 位于连接表中,并且该连接表对对象模型是隐藏的.

Some explanation. Many-to-many relationships are represented in EF as Independent Associations, or IAs. This is because the foreign keys for the relationship are not exposed anywhere in the object model. In the database the FKs are in a join table, and this join table is hidden from the object model.

使用关系条目"在 EF 中跟踪 IA.它们类似于您从 DbContext.Entry 获得的 DbEntityEntry 对象,不同之处在于它们表示两个实体之间的关系而不是实体本身.关系条目未在 DbContext API 中公开,因此您需要下拉到 ObjectContext 才能访问它们.

IAs are tracked in EF using "relationship entries". These are similar to the DbEntityEntry objects you get from the DbContext.Entry except that they represent a relationship between two entities rather than an entity itself. Relationship entries are not exposed in the DbContext API, so you need to drop down to ObjectContext to access them.

当创建两个实体之间的新关系时,就会创建一个新关系条目,例如通过将 Employee 添加到 Company.Employees 集合.此关系处于已添加状态.

A new relationship entry is created when a new relationship between two entities is created, for example by adding an Employee to the Company.Employees collection. This relationship is in the Added state.

同样,当两个实体之间的关系被删除时,关系条目就会被置于已删除状态.

Likewise, when a relationship between two entities is removed, then the relationship entry is put into the Deleted state.

这意味着要查找更改的多对多关系(或实际上任何更改的 IA),我们需要查找添加和删除的关系条目.这就是 GetAddedRelationships 和 GetDeletedRelationships 的作用.

This means that to find changed many-to-many relationships (or actually any changed IA) we need to find added and deleted relationship entries. This is what the GetAddedRelationships and GetDeletedRelationships do.

一旦我们有了关系条目,我们就需要理解它们.为此,您需要了解一些内幕知识.添加(或未更改)关系条目的 CurrentValues 属性包含两个值,它们是关系两端实体的 EntityKey 对象.同样,但令人讨厌的是略有不同,已删除关系条目的 OriginalValues 属性包含已删除关系两端实体的 EntityKey 对象.

Once we have relationship entries, we need to make sense of them. For this you need to know a piece of insider knowledge. The CurrentValues property of an Added (or Unchanged) relationship entry contains two values which are the EntityKey objects of the entities at either end of the relationship. Likewise, but annoyingly slightly different, the OriginalValues property of a Deleted relationship entry contains the EntityKey objects for the entities at either end of the deleted relationship.

(而且,是的,这太可怕了.请不要怪我——它早在我的时代之前.)

(And, yes, this is horrible. Please don’t blame me—it is from well before my time.)

CurrentValues/OriginalValues 的不同是我们将委托传递给 GetRelationships 私有方法的原因.

The CurrentValues/OriginalValues difference is why we pass a delegate into the GetRelationships private method.

一旦我们有了 EntityKey 对象,我们就可以使用 GetObjectByKey 来获取实际的实体实例.我们将这些作为元组返回,您就拥有了.

Once we have the EntityKey objects we can use GetObjectByKey to get the actual entity instances. We return these as tuples and there you have it.

这里有一些实体、一个上下文和一个初始化器,我用来测试这个.(注意——测试并不广泛.)

Here’s some entities, a context, and an initializer, I used to test this. (Note—testing was not extensive.)

public class Company
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Employee> Employees { get; set; }

    public override string ToString()
    {
        return "Company " + Name;
    }
}

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Company> Companies { get; set; }

    public override string ToString()
    {
        return "Employee " + Name;
    }
}

public class DataContext : DbContext
{
    static DataContext()
    {
        Database.SetInitializer(new DataContextInitializer());
    }

    public DbSet<Company> Companies { get; set; }
    public DbSet<Employee> Employees { get; set; }

    public override int SaveChanges()
    {
        foreach (var relationship in this.GetAddedRelationships())
        {
            Console.WriteLine(
                "Relationship added between {0} and {1}",
                relationship.Item1,
                relationship.Item2);
        }

        foreach (var relationship in this.GetDeletedRelationships())
        {
            Console.WriteLine(
                "Relationship removed between {0} and {1}",
                relationship.Item1,
                relationship.Item2);
        }

        return base.SaveChanges();
    }

}

public class DataContextInitializer : DropCreateDatabaseAlways<DataContext>
{
    protected override void Seed(DataContext context)
    {
        var newMonics = new Company { Name = "NewMonics", Employees = new List<Employee>() };
        var microsoft = new Company { Name = "Microsoft", Employees = new List<Employee>() };

        var jim = new Employee { Name = "Jim" };
        var arthur = new Employee { Name = "Arthur" };
        var rowan = new Employee { Name = "Rowan" };

        newMonics.Employees.Add(jim);
        newMonics.Employees.Add(arthur);
        microsoft.Employees.Add(arthur);
        microsoft.Employees.Add(rowan);

        context.Companies.Add(newMonics);
        context.Companies.Add(microsoft);
    }
}

这是一个使用它的例子:

Here’s an example of using it:

using (var context = new DataContext())
{
    var microsoft = context.Companies.Single(c => c.Name == "Microsoft");
    microsoft.Employees.Add(context.Employees.Single(e => e.Name == "Jim"));

    var newMonics = context.Companies.Single(c => c.Name == "NewMonics");
    newMonics.Employees.Remove(context.Employees.Single(e => e.Name == "Arthur"));

    context.SaveChanges();
} 

这篇关于Entity Framework 4.1+ 多对多关系变更跟踪的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆