EntityFramework和ReadOnlyCollection

时间:2015-08-16 04:52:42

标签: c# entity-framework

我使用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
}

这会隐藏AddRemove方法,但根本不会保护。因为我总是可以转换为ICollection并调用禁止的方法。

所以,我的问题是:

  • 有没有办法在EntityFramework中使用只读集合?

3 个答案:

答案 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的约定发现为背景字段。