我正在使用使用实体框架的域驱动设计构建应用程序。
我的目标是允许我的域模型(使用 EF 持久化)在其中包含一些逻辑。
开箱即用,实体框架对于实体如何添加到图表然后持久化是非常无限制的。
例如,我的域为 POCO(没有逻辑):
public class Organization
{
private ICollection<Person> _people = new List<Person>();
public int ID { get; set; }
public string CompanyName { get; set; }
public virtual ICollection<Person> People { get { return _people; } protected set { _people = value; } }
}
public class Person
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual Organization Organization { get; protected set; }
}
public class OrganizationConfiguration : EntityTypeConfiguration<Organization>
{
public OrganizationConfiguration()
{
HasMany(o => o.People).WithRequired(p => p.Organization); //.Map(m => m.MapKey("OrganizationID"));
}
}
public class PersonConfiguration : EntityTypeConfiguration<Person>
{
public PersonConfiguration()
{
HasRequired(p => p.Organization).WithMany(o => o.People); //.Map(m => m.MapKey("OrganizationID"));
}
}
public class MyDbContext : DbContext
{
public MyDbContext()
: base(@"Data Source=(localdb)\v11.0;Initial Catalog=stackoverflow;Integrated Security=true")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new PersonConfiguration());
modelBuilder.Configurations.Add(new OrganizationConfiguration());
}
public IDbSet<Organization> Organizations { get; set; }
public IDbSet<Person> People { get; set; }
}
我的示例域是一个组织可以有很多人。一个人只能属于一个组织。
创建组织并向其中添加人员非常简单:
using (var context = new MyDbContext())
{
var organization = new Organization
{
CompanyName = "Matthew's Widget Factory"
};
organization.People.Add(new Person {FirstName = "Steve", LastName = "McQueen"});
organization.People.Add(new Person {FirstName = "Bob", LastName = "Marley"});
organization.People.Add(new Person {FirstName = "Bob", LastName = "Dylan" });
organization.People.Add(new Person {FirstName = "Jennifer", LastName = "Lawrence" });
context.Organizations.Add(organization);
context.SaveChanges();
}
我的测试查询是。
var organizationsWithSteve = context.Organizations.Where(o => o.People.Any(p => p.FirstName == "Steve"));
上述类的布局不符合域的工作方式。例如,所有人都属于一个组织,组织是聚合根。能够这样做是没有意义的,context.People.Add(...)
因为这不是域的工作方式。
如果我们想在Organization
模型中添加一些逻辑来限制该组织中可以有多少人,我们可以实现一个方法。
public Person AddPerson(string firstName, string lastName)
{
if (People.Count() >= 5)
{
throw new InvalidOperationException("Your organization already at max capacity");
}
var person = new Person(firstName, lastName);
this.People.Add(person);
return person;
}
但是,使用当前的类布局,我可以AddPerson
通过调用organization.Persons.Add(...)
或完全忽略聚合根来规避逻辑context.Persons.Add(...)
,我不想这样做。
我提出的解决方案(这不起作用,这就是我在这里发布它的原因)是:
public class Organization
{
private List<Person> _people = new List<Person>();
// ...
protected virtual List<Person> WritablePeople
{
get { return _people; }
set { _people = value; }
}
public virtual IReadOnlyCollection<Person> People { get { return People.AsReadOnly(); } }
public void AddPerson(string firstName, string lastName)
{
// do domain logic / validation
WriteablePeople.Add(...);
}
}
这不起作用,因为映射代码HasMany(o => o.People).WithRequired(p => p.Organization);
没有按HasMany
预期编译 anICollection<TEntity>
和 not IReadOnlyCollection
。我可以公开一个ICollection
本身,但我想避免拥有Add
/Remove
方法。
我可以“忽略”该People
属性,但我仍然希望能够针对它编写 Linq 查询。
我的第二个问题是我不希望我的上下文暴露直接添加/删除人员的可能性。
在我想要的上下文中:
public IQueryable<Person> People { get; set; }
但是,EF 不会填充People
我的上下文的属性,即使IDbSet
实现了IQueryable
. 我能想到的唯一解决方案是编写一个MyDbContext
暴露我想要的功能的外观。对于只读数据集来说,似乎有点矫枉过正和大量维护。
如何在使用实体框架时实现干净的 DDD 模型?
编辑
我正在使用实体框架 v5