Document readonly/IEnumerable navigation patterns.
Hello,
I want to ask about collection in EF Core which are used to relations. For example I have entity Invoice and InvoiceItem. Invoice implements public ICollection<InvoiceItem> Items {get; set;} property.
But ICollection exposes Add method so client can call invoice.Items.Add(new InvoiceItem(...)); It is violation of domain model rule that only aggregate root can manage entities inside aggregate.
When I change implementation to IEnumerable(IReadOnlyCollection)<InvoiceItem> there is not Add method so client cannot add new InvoiceItem to Invoice. But Invoice entity cannot too. There would have to be Add method on Invoice:
public void AddItem(int price)
{
var oldItems = new List<InvoiceItem>(Items);
oldItems.Add(new InvoiceItem(price));
Items = oldItems.;
}
Question is how EF tracker will be worked that collection is new object.
I can add private field private List<InvoiceItem> items = new List<InvoiceItems>(); Then I change public IEnumerable<InvoiceItem> Items => items;
There are two question:
- How to map backing field for it?
- How will work Include method ...
dbContext.Invoices.Include(k=>Items)?
Second problem is that I have public setter on InvoiceItem. Can EF work with private set method on navigation property?
Is there any possibility how to work with collections to satisfy domain model rule that entities in aggregate can manage only root entity? Thank you.
@Zefek You can do something like this:
public class Blog
{
private readonly List<Post> _posts = new();
public int Id { get; set; }
public IEnumerable<Post> Posts => _posts;
public void AddPost(Post post) => _posts.Add(post);
}
The backing field _posts is found automatically. Include works fine--the navigation is still Posts, but EF uses the backing field to interact with it.
See https://blog.oneunicorn.com/2016/10/28/collection-navigation-properties-and-fields-in-ef-core-1-1/ for more information.
@ajcvickers Thank you very much. This is exactly what I needed to know :)