实体框架4.1+多对多关系改变跟踪 [英] Entity Framework 4.1+ many-to-many relationships change tracking

查看:161
本文介绍了实体框架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中表示为独立关联或IAs。这是因为关系中的外键不会暴露在对象模型中的任何位置。在数据库中,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中使用关系条目跟踪IAs。这些类似于您从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.

当两个实体之间的新关系创建一个新的关系条目是创建的,例如通过向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);
    }
}

以下是使用它的示例:

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

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

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