Entity Framework 4.1+多对多关系更改跟踪

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

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

这里是如何find所有改变的多对多关系。 我已经实现了代码作为扩展方法:

 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中将多对多关系表示为独立协会或独立协会。 这是因为关系的外键没有暴露在对象模型的任何地方。 在数据库中,FK位于连接表中,并且该连接表对于对象模型是隐藏的。

在EF使用“关系条目”跟踪IAs。 这些类似于从DbContext.Entry获得的DbEntityEntry对象,只不过它们表示两个实体之间的关系,而不是实体本身。 关系条目不公开在DbContext API中,因此您需要下拉到ObjectContext来访问它们。

当创build两个实体之间的新关系时(例如,通过向Company.Employees集合添加一个Employee),将创build一个新的关系条目。 这种关系处于新增状态。

同样,当两个实体之间的关系被移除时,则关系条目被置于“已删除”状态。

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

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

(是的,这太可怕了,请不要怪我 – 这是在我的时间之前。)

CurrentValues / OriginalValues的不同之处在于我们将委托传递给GetRelationships私有方法。

一旦我们有了EntityKey对象,我们就可以使用GetObjectByKey来获取实际的实体实例。 我们把它们作为元组返回,在那里。

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

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

我不能给你准确的代码,但是我可以告诉你,在员工与公司之间有一个加工者表,你的情况将被简化十倍,以分解多对多的关系。