c# – 使用域驱动设计与实体框架聚合根

前端之家收集整理的这篇文章主要介绍了c# – 使用域驱动设计与实体框架聚合根前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在使用使用Entity Framework的Domain Driven Design构建应用程序.

我的目标是允许我的域模型(通过EF持久化)包含一些逻辑.

开箱即用,实体框架对于如何将实体添加到图形然后保持不变非常不受限制.

举例来说,我的域名为POCO(没有逻辑):

  1. public class Organization
  2. {
  3. private ICollection<Person> _people = new List<Person>();
  4.  
  5. public int ID { get; set; }
  6.  
  7. public string CompanyName { get; set; }
  8.  
  9. public virtual ICollection<Person> People { get { return _people; } protected set { _people = value; } }
  10. }
  11.  
  12. public class Person
  13. {
  14. public int ID { get; set; }
  15.  
  16. public string FirstName { get; set; }
  17. public string LastName { get; set; }
  18.  
  19. public virtual Organization Organization { get; protected set; }
  20. }
  21.  
  22. public class OrganizationConfiguration : EntityTypeConfiguration<Organization>
  23. {
  24. public OrganizationConfiguration()
  25. {
  26. HasMany(o => o.People).Withrequired(p => p.Organization); //.Map(m => m.MapKey("OrganizationID"));
  27. }
  28. }
  29.  
  30. public class PersonConfiguration : EntityTypeConfiguration<Person>
  31. {
  32. public PersonConfiguration()
  33. {
  34. Hasrequired(p => p.Organization).WithMany(o => o.People); //.Map(m => m.MapKey("OrganizationID"));
  35. }
  36. }
  37.  
  38. public class MyDbContext : DbContext
  39. {
  40. public MyDbContext()
  41. : base(@"Data Source=(localdb)\v11.0;Initial Catalog=stackoverflow;Integrated Security=true")
  42. {
  43. }
  44.  
  45. protected override void OnModelCreating(DbModelBuilder modelBuilder)
  46. {
  47. modelBuilder.Configurations.Add(new PersonConfiguration());
  48. modelBuilder.Configurations.Add(new OrganizationConfiguration());
  49. }
  50.  
  51. public IDbSet<Organization> Organizations { get; set; }
  52. public IDbSet<Person> People { get; set; }
  53. }

我的示例域名是组织可以有很多人.一个人只能属于一个组织.

创建组织并向其添加人员非常简单:

  1. using (var context = new MyDbContext())
  2. {
  3. var organization = new Organization
  4. {
  5. CompanyName = "Matthew's Widget Factory"
  6. };
  7.  
  8. organization.People.Add(new Person {FirstName = "Steve",LastName = "McQueen"});
  9. organization.People.Add(new Person {FirstName = "Bob",LastName = "Marley"});
  10. organization.People.Add(new Person {FirstName = "Bob",LastName = "Dylan" });
  11. organization.People.Add(new Person {FirstName = "Jennifer",LastName = "Lawrence" });
  12.  
  13. context.Organizations.Add(organization);
  14.  
  15. context.SaveChanges();
  16. }

我的测试查询是.

  1. var organizationsWithSteve = context.Organizations.Where(o => o.People.Any(p => p.FirstName == "Steve"));

上面的类布局不符合域的工作方式.例如,所有人都属于组织为组合根的组织.能够做context.People.Add(…)并没有意义,因为这不是域的工作方式.

如果我们想要在组织模型中添加一些逻辑来限制该组织中可以有多少人,我们就可以实现一种方法.

  1. public Person AddPerson(string firstName,string lastName)
  2. {
  3. if (People.Count() >= 5)
  4. {
  5. throw new InvalidOperationException("Your organization already at max capacity");
  6. }
  7.  
  8. var person = new Person(firstName,lastName);
  9. this.People.Add(person);
  10. return person;
  11. }

但是,对于类的当前布局,我可以通过调用organization.Persons.Add(…)来绕过AddPerson逻辑,或者通过执行context.Persons.Add(…)来完全忽略聚合根,这两者都不是我的想做.

我提出的解决方案(不起作用,也就是我在这里发布的原因)是:

  1. public class Organization
  2. {
  3. private List<Person> _people = new List<Person>();
  4.  
  5. // ...
  6.  
  7. protected virtual List<Person> WritablePeople
  8. {
  9. get { return _people; }
  10. set { _people = value; }
  11. }
  12.  
  13. public virtual IReadOnlyCollection<Person> People { get { return People.AsReadOnly(); } }
  14.  
  15. public void AddPerson(string firstName,string lastName)
  16. {
  17. // do domain logic / validation
  18.  
  19. WriteablePeople.Add(...);
  20. }
  21. }

这不适用于映射代码HasMany(o => o.People).Withrequired(p => p.Organization);不编译,因为HasMany期望ICollection< TEntity>而不是IReadOnlyCollection.我可以暴露ICollection本身,但我想避免使用Add / Remove方法.

我可以“忽略”People属性,但我仍然希望能够针对它编写Linq查询.

我的第二个问题是我不希望我的上下文暴露直接添加/删除人的可能性.

在上下文中我想要:

  1. public IQueryable<Person> People { get; set; }

但是,即使IDbSet实现了IQueryable,EF也不会填充我的上下文的People属性.我能想到的唯一解决方案就是在MyDbContext上编写一个外观,它暴露了我想要的功能.对于只读数据集来说,似乎过度杀伤和大量维护.

如何在使用Entity Framework时实现干净的DDD模型?

编辑
我正在使用Entity-Framework v5

解决方法

正如您所注意到的,持久性基础结构(EF)对类结构施加了一些要求,因此不会像您期望的那样“干净”.我担心与之斗争会最终导致无休止的斗争和脑力颠簸.

我建议另一种方法,一个完全干净的域模型和一个单独的持久性模型在较低层.您可能需要在这两者之间使用转换机制,AutoMapper会很好.

这将完全解除您的顾虑.没有办法“削减”只是因为EF使事情变得必要并且域层不能提供上下文,因为它只是来自“另一个世界”,它不属于域.

我已经看到人们制作部分模型(又名“有界背景”)或只是创建一个普通的EF poco结构并假装这个IS DDD,但它可能不是,你的担忧正好在头脑中.

猜你在找的C#相关文章