关于更新断开实体图的思考

时间:2014-02-07 14:58:51

标签: c# entity-framework domain-driven-design repository-pattern

我正在尝试设置一个项目来使用断开连接的实体并将我的DbContext封装在“Repository”类中。每个断开连接的实体都有一个本地属性来远程跟踪它们的状态,并在传入时通过存储库进行解析以相应地设置EntityState属性。

我的问题与更新对象图中的子对象有关。我是否应该使用更新方法设置存储库,这些方法可以接收通常更新的实体,或者我应该在对象图中为父对象设置单个更新方法并附加/更新它。

例如

public class Company {
   public int CompanyId { get; set; }
   public string CompanyName { get; set; }
   public ICollection<Department> { get; set; }
   public LocalEntityState LocalEntityState { get; set; }  // Enum for tracking disconnected changes 
}

public class Department {
   public int DepartmentId { get; set; }
   public int CompanyId { get; set; }
   public string DepartmentName { get; set; }
   public Company Company { get; set; }
   public ICollection<Employee> Employees { get; set; }
   public LocalEntityState LocalEntityState { get; set; }  
}

public class Employee {
   public int EmployeeId { get; set; }
   public int DepartmentId { get; set; }
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public Department Department { get; set; }
   public LocalEntityState LocalEntityState { get; set; }  
}

如果我使用单一方法更新处于插入/更新状态的实体的整个对象图,我可以使用类似下面的界面。但有一点需要注意的是,如果我要对员工进行更新,我需要始终加载公司和部门的实例。

public interface ICompanyRepository {
   void InsertOrUpdate(Company company)

   // Other methods for deletion, retrieval, etc..
}

或者我可以使用这样的东西,我在为DbContext中更改的“主”对象提供方法。这样,如果我想编辑/插入它们,我只需要在内存中有一个Employee实例。

public interface ICompanyRepository {
   void InsertOrUpdate(Company company);
   void InsertOrUpdate(Department department);
   void InsertOrUpdate(Employee employee);
}

我只想让别人对此有所了解,以及避免一些常见陷阱的最佳途径。对于我的实际项目,我试图将较大的DbContext拆分为“Bounded Context”的较小子集(来自Julie Lerman的“企业中的实体框架”),以便每个存储库将使用一组相关实体。但我不确定更新这些实体的最佳方法。谢谢你的帮助。

1 个答案:

答案 0 :(得分:2)

在我看来,处理对象图并不是存储库的责任。这是一个类似工作的对象。

根据Fowler

的存储库
  

使用类似集合的接口访问域对象,在域和数据映射层之间进行调解。

这些单词类似集合的界面至关重要。您将存储库作为对象集合进行访问。所以它有简单的方法来获取和添加对象,就是这样。在过去,我没有很好地区分存储库和服务,所以我总是在责任上挣扎。现在我很清楚,服务(域服务)处理用例和存储库只能以可预测的通用方式交换数据。另一方面,服务可以暴露出代表业务流程的各种非常不同的方法。

存储库不实现业务逻辑。因此,OrderRepository没有类似GetOpenOrders的方法,因为它没有“开放”的概念。同样,它没有处理对象图的方法,因为对象之间的关联也是业务概念。 (我的意思是他们没有方法在他们的签名中接受或返回除了他们代表集合的对象之外的对象。他们可能返回包含其他对象的对象,但这不是他们的责任。)

说实话,在熟悉linq-to sql和后来的Entity Framework之前,我并没有完全掌握这些概念。这些ORM都提供了通用存储库模式的完美实现,L2S中的Table<T>和EF中的DbSet<T>(或稍微过时的ObjectSet)。当我意识到这些存储库不能保存数据时,我感到非常惊讶。

但等等,他们应该“在域名和数据之间进行调解”,不应该吗?实际上,不,他们没有。 Fowler说“在域和数据映射层之间”。唉唉。这是一个很大的不同。数据映射层是执行与数据存储之间的实际数据传输的层。

因此,存储库可以包含SaveUpdate等方法,但这些方法除了告诉映射层应该将对象标记为新的或更新之外什么都不做。他们将对象添加到某些上下文中,这些上下文可能会(或可能不会)在存储库早已消失时将对象保存到数据存储中。在NHibernate中,此上下文为Session,在EF中为DbContext

Soo ...长话短说。您的第一个实现(仅限InsertOrUpdate公司)可能是存储库,第二个不是。

存储对象图是一种服务方法任务,可能涉及多个存储库,通常是一个上下文或工作单元。之前已经说过,DbSet是一个存储库实现,DbContext是一个工作单元。您可能认为甚至不需要在这些上面添加额外的存储库/ UoW层。