Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EntityFramework and ReadOnlyCollection

I use EntityFramewotk and code first approach. So, I describe my model like this:

class Person
{
    public long Id { get;set; }
    public string Name { get;set; }
    public ICollection<Person> Parents { get;set; }
}

But, my domain logic don't allow to modify Parents collection (add, delete), it must be readonly (just for example). EntityFramework requires all Collections have ICollection<T> interface, and it has Add method (to materialize results) and Remove method, and others. I can create my own collection with explicit implementation of 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
}

This hides Add and Remove methods, but does not protect at all. Because I can always cast to ICollection and call prohibited method.

So, my question is:

  • Is there a way to work with read-only collections in EntityFramework?
like image 582
Backs Avatar asked Aug 16 '15 04:08

Backs


People also ask

What is include in EntityFramework?

Entity Framework Classic Include The Include method lets you add related entities to the query result. In EF Classic, the Include method no longer returns an IQueryable but instead an IncludeDbQuery that allows you to chain multiple related objects to the query result by using the AlsoInclude and ThenInclude methods.

What is EntityFramework in asp net?

Entity Framework (EF) is an object-relational mapper that enables . NET developers to work with relational data using domain-specific objects. It eliminates the need for most of the data-access code that developers usually need to write. Get it: Add this to your project and start taking advantage of this powerful O/RM.

What is ReadOnlyCollection C#?

ReadOnlyCollection makes an array or List read-only. With this type from System. Collections. ObjectModel, we provide a collection of elements that cannot be changed.

Is EntityFramework open source?

Entity Framework (EF) Core is a lightweight, extensible, open source and cross-platform version of the popular Entity Framework data access technology. EF Core can serve as an object-relational mapper (O/RM), which: Enables . NET developers to work with a database using .


2 Answers

In EF Core, you can encapsulate collections and achieve true domain modeling by using backing fields. So, you can define your collection as a private field and expose it as a public readonly property like below as _parents and 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); 
    }
}

As you can see, Parents is a read-only collection and consumers are not allowed to modify it.

Note that _parents is discovered as a backing-field by ef core's convention.

like image 193
Ehsan Mirsaeedi Avatar answered Sep 20 '22 04:09

Ehsan Mirsaeedi


You can expose private collection properties to EF, allowing for mapping and querying, while still keeping your domain object's members and relationships properly encapsulated. It's a bit messy, but it works:

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; }
    }
}

Mapping then uses:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    modelBuilder.Entity<Customer>().HasMany(Customer.OrderMapping);
}

This approach is described further here: http://ardalis.com/exposing-private-collection-properties-to-entity-framework

like image 40
ssmith Avatar answered Sep 23 '22 04:09

ssmith