我使用EntityFramewotk和代码第一种方法。所以,我这样描述我的模型:
class Person
{
public long Id { get;set; }
public string Name { get;set; }
public ICollection<Person> Parents { get;set; }
}
但是,我的域逻辑不允许修改Parents集合(添加,删除),它必须是readonly(仅举例)。 EntityFramework要求所有集合都有ICollection<T>
接口,并且它具有Add
方法(实现结果)和Remove
方法等。
我可以通过显式实现interface来创建我自己的集合:
public class ParentsCollection : ICollection<Person>
{
private readonly HashSet<Person> _collection = new HashSet<Person>();
void ICollection<Person>.Add(Person item)
{
_collection.Add(item);
}
bool ICollection<Person>.Remove(Person item)
{
return _collection.Remove(item);
}
//...and others
}
这会隐藏Add
和Remove
方法,但根本不会保护。因为我总是可以转换为ICollection并调用禁止的方法。
所以,我的问题是:
答案 0 :(得分:6)
您可以将私有集合属性公开给EF,允许映射和查询,同时仍然保持域对象的成员和关系被正确封装。它有点乱,但它有效:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<Order> Orders
{
get { return _orders.AsEnumerable(); }
}
private List<Order> _orders { get; set; }
public Customer()
{
_orders = new List<Order>();
}
public static Expression<Func<Customer, ICollection<Order>>> OrderMapping
{
get { return c => c._orders; }
}
}
映射然后使用:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Customer>().HasMany(Customer.OrderMapping);
}
此方法在此处进一步描述: http://ardalis.com/exposing-private-collection-properties-to-entity-framework
答案 1 :(得分:1)
嗯,有办法。它并不漂亮,因为它在您的域模型上添加了额外的东西,但我只是检查它是否有效。
所有学分归Owen Craig所有。
http://owencraig.com/mapping-but-not-exposing-icollections-in-entity-framework/
坚果壳中的示例
想象一下,我们现有的模型有Organization =&gt;员工已经成立。
要应用此技术,我们需要稍微更改组织模型:
// this is the main collection that will be persisted, mark it as protected
protected virtual ICollection<Employee> EmployeesInternal { get; private set; } = new List<Employee>();
// this will expose collection contents to public, seemingly unneccessary `Skip` statement will prevent casting back to Collection
public IEnumerable<Employee> Employees => EmployeesInternal.Skip(0);
// this is property accessor that will be used to define model and/or in `Include` statements, could be marked as internal if your domain/persistance/services are in the same assembly
public static Expression<Func<Organization, ICollection<Employee>>> EmployeeAccessor = f => f.EmployeesInternal;
在数据库上下文中更改fluent配置:
modelBuilder.Entity<Organization>().HasMany(Organization.EmployeeAccessor).WithRequired();
如果您不使用LazyLoading
,请更改任何Include语句var organizations = organizationRepository.GetAll().Include(Organization.EmployeeAccessor)
快乐DDD!
答案 2 :(得分:1)
在EF Core中,您可以使用backing fields封装集合并实现真正的域建模。 因此,您可以将集合定义为私有字段,并将其公开为公共只读属性,如下所示: _parents 和 Parents 。
class Person
{
public long Id { get;set; }
public string Name { get;set; }
private List<Person> _parents = new List<Person>();
public IReadOnlyCollection<Person> Parents => _parents.AsReadOnly();
public void AddParent(Parent parent){
_parents.Add(parent);
}
}
如您所见,Parents是一个只读集合,不允许消费者修改它。
请注意_parents被ef core的约定发现为背景字段。