EntityFramework和ReadOnlyCollection

Bac*_*cks 9 c# entity-framework

我使用EntityFramewotk和代码第一种方法.所以,我这样描述我的模型:

class Person
{
    public long Id { get;set; }
    public string Name { get;set; }
    public ICollection<Person> Parents { get;set; }
}
Run Code Online (Sandbox Code Playgroud)

但是,我的域逻辑不允许修改Parents集合(添加,删除),它必须是readonly(仅举例).EntityFramework要求所有集合都有ICollection<T>接口,并且它具有Add方法(实现结果)和Remove方法等.我可以通过显式实现接口来创建自己的集合:

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
}
Run Code Online (Sandbox Code Playgroud)

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

所以,我的问题是:

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

Ehs*_*edi 14

在 EF Core 中,您可以通过使用支持字段来封装集合并实现真正的域建模。所以,你可以定义你的集合作为一个私人领域和揭露它作为一个公共只读属性,像下面的_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); 
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,Parents 是一个只读集合,不允许消费者对其进行修改。

请注意, _parents 被 ef core 的约定发现为支持字段。

  • 这似乎是完美的解决方案,值得更多投票。不可能在类之外改变集合(即使通过强制转换)。并且仍然可以使用“Include(p =&gt; p.Parents)”进行预加载。PS:我还必须在“DbContext.OnModelCreating”中添加一些代码才能满足我的特殊需求=&gt;“modelBuilder.Entity&lt;Person&gt;().HasMany(g =&gt; g.Parents)” (2认同)

ssm*_*ith 7

您可以将私有集合属性公开给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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

映射然后使用:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    modelBuilder.Entity<Customer>().HasMany(Customer.OrderMapping);
}
Run Code Online (Sandbox Code Playgroud)

这里进一步描述了这种方法:http: //ardalis.com/exposing-private-collection-properties-to-entity-framework


Mat*_*zer 1

简短的回答:。奇怪的是,您可以这样做(嗯,NHibernate 可以设置私有类字段,这意味着您可以使用将字段封装为只读集合的​​公共属性来公开它......好吧,您可以在 EF 中解决这种情况也是:实体框架通过包含对象实现多对多。顺便说一句,我不会建议您使用这种方法,因为如果它是私有财产,您如何添加新的父级?

无论如何,我认为域对象应该是读写的,因为归根结底,域对象描述了域内的实体,您应该能够访问和修改它。

另一种解决方案是设计一个接口来公开Parentsas IReadOnlyList<Person>,以及一个IPerson包含除 之外的所有Person成员的接口Parents,并返回PersonIPerson

public interface IHasParents
{
    IReadOnlyList<Person> Parents { get; }
}

public interface IPerson : IHasParents
{
    long Id { get; set; }
    string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

IPerson隐式实现,Person除非显式Parents实现。当您需要返回某个地方时,您可以返回而不是:PersonIPersonPerson

public IPerson CreatePerson(string name, IEnumerable<Persons> parents)
{
    Person person = new Person { Name = name, Parents = parents };

    // Persistence stuff

    return person;
}
Run Code Online (Sandbox Code Playgroud)

您可以争辩说您可能能够向下转型IPersonPerson,但此时我会告诉您,您需要遵循自己的编码约定:如果您定义了您永远不会返回,PersonIPerson我会在整个代码中这样做base,如果您需要可写的属性Parents,则返回Person(并且稍后可以避免强制转换!)。


归档时间:

查看次数:

4135 次

最近记录:

6 年,4 月 前