我正在使用使用实体框架的域驱动设计构建应用程序。使用域驱动设计与实体框架聚合根
我的目标是让我的领域模型(即持续使用EF)在其中包含一些逻辑。
开箱即用,entity-framework对于实体如何添加到图中然后保持不变是非常有限的。
举个例子,我作为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;
}
然而,随着类的当前布局我可以通过调用organization.Persons.Add(...)
规避AddPerson
逻辑或通过执行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
期望一个ICollection<TEntity>
和不是IReadOnlyCollection
。我可以公开一个ICollection
本身,但我想避免使用Add
/Remove
方法。
我可以“忽略”People
属性,但我仍然希望能够针对它编写Linq查询。
我的第二个问题是我不希望我的上下文暴露直接添加/删除人员的可能性。
在上下文我会想:
public IQueryable<Person> People { get; set; }
然而,EF不会填充我的上下文的People
财产,即使IDbSet
实现IQueryable
。我可以想出的唯一解决方案是通过MyDbContext
写出一个正面,它揭示了我想要的功能。似乎矫枉过正和大量的只读数据集的维护。
如何在使用实体框架时实现干净的DDD模型?
编辑
我使用实体框架V5
您需要将其标记为虚拟以便自动填充。 – user1496062