DbContext - 使用断开连接的实体时保存子集合的最佳实践

时间:2013-08-22 20:44:40

标签: c# entity-framework dbcontext n-tier-architecture

我正在尝试将我的DbContext与我目前正在使用的winforms应用程序分开,以更好地支持多用户环境以及即将推出的网站。在做了一些研究之后,我将为winforms app / website实现一个数据访问层(DAL),以便连接到最终用户并使其与断开连接的实体一起工作。我的问题是关于在更新子集合中的某个实体时保存实体更新的最佳方法。

例如,如果我有以下结构(简化)

public class Company
{
    public int CompanyID { get; set; }
    public string CompanyName { get; set; }
    public ICollection<Employee> Employees { get; set; }  // Non-virtual as we aren't lazy-loading
}

public class Employee
{
    public int CompanyID { get; set; }
    public int EmployeeID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public ICollection<Claim> Claims { get; set; }
}

public class Claim
{
    public DateTime ClaimDate { get; set; }
    public ICollection Documentation { get; set; }
}

public class Document
{
    public byte[] DocumentImage { get; set; }
    public string Name { get; set; }
    public DateTime CreateDate { get; set; }
}

在winforms应用程序中,我有多个Binding Source的设置来显示员工的信息

例如:

employeeBinding.DataSource = typeof(Employee);   // Eventually set to an IEnumerable<Employee>
claimBinding.DataSource = employeeBinding;
claimBinding.DataMember = "Claims";
documentationBinding.DataSource = claimBinding;
documentationBinding.DataMember = "Documentation";

然而,通过这样的设置,我无法调用每个绑定源的“CurrentChanged”事件来保存每个实体,因为它已经更改(除非我将引用存储到表单中的前一个实体) 。所以我想到的是在DAL中类似于下面的内容,并遍历每个子集合。

public void UpdateEmployee(Employee employee) 
{
   using (myContext context = new myContext())
   {
     context.Employees.Attach(employee);
     context.Entry<Employee>(employee).State = EntityState.Modified;

     foreach(var claim in employee.Claims) 
     {
       context.Entry<Claim>(claim).State = EntityState.Modified;
       foreach(var doc in claim.Documentation)
       {
         context.Entry<Document>(doc).State = EntityState.Modified;
       }
     }
     context.SaveChanges(); 
   }
}

然而,我觉得这条路线可能会因一些更复杂的实体和关系而变得丑陋。有人可以帮我指出处理这个问题的最佳途径,还是应该引用代码中的当前实体,这样当“CurrentChanged”事件触发时我可以更新每个实体?

非常感谢。

2 个答案:

答案 0 :(得分:0)

当您使用Entity Framework时,您拥有ChangeTracker,即使您正在使用此“Disconected entities”,您也可以让ChangeTracker跟踪实体,只需将它们附加到上下文中,然后再调用你调用SaveChanges .DetectCHanges()你真的不需要这个特定的代码,你可以使用泛型:

public void Update<TEntity>(TEntity entity) 
{
   using (myContext context = new myContext())
   {
     context.Set<TEntity>.Attach(entity);
     context.ChangeTracker.DetectChanges();
     context.SaveChanges(); 
   }
}

对方法的调用将是:

Update<Employee>(employees);

另外我认为最好使用BindingSouce作为DataSource,并将BindingSource的DataSource设置为List而不是typeof(Employee)

答案 1 :(得分:0)

我可能错了,但我不相信DetectChanges将能够确定已对断开的实体进行了更改。当附加实体时,它将具有&#34;未更改的实体状态&#34;所以DbContext不会对它做任何事情,直到你把它标记为&#34;修改&#34;。此外,如以下URL所示,&#34; DetectChanges&#34;无论如何都会调用许多方法(包括&#34;附加&#34;),并且不需要显式调用。

http://msdn.microsoft.com/en-us/data/jj556205.aspx

对于BindingSource,我说明了BindingSource将被设置为typeof(Employee),好像我在加载事件之前在构造函数中设置我的代码,在那里我将实际获取我的数据并设置它#39;从DAL调用获取IEnumerable的数据源。如果我没有这样做,我会在试图绑定到&#34; DataMember&#34;时遇到问题。作为其他BindingSources的属性将无法找到所指示的属性。

我不相信您作为示例提供的代码可以解决我遇到的有关正在更新的子集合的问题。在使用LinqPad进行测试时,如果父实体也发生了更改,则会更新它们,但如果父实体没有更改,则不会更新。这就是我为什么要遍历所有子集合并将其标记为&#34; Modified&#34;。