Summary
When configuring EF Core to lazily resolve navigation properties of entities, the navigation property - and any backing field it might have - are null
until the property is first accessed. This makes sense.
However, this can be problematic if you need to access the backing field within the entity itself before the navigation property has been accessed. In this case, the field will be null
and a NullReferenceException
will be thrown.
In some cases it's necessary to perform operations using the backing field instead of the property. In these case, I'm wondering if there is a clean way to ensure that backing fields get loaded correctly without having to either:
- Sacrifice good encapsulation of the entity
- Litter the entity with code that's only there to let it play well with the underlying persistence logic
Example
A common pattern with collection navigations is to have a backing field of a mutable collection type and to expose it as a read-only collection. For example, consider an entity like this:
public class ProductConsultant
{
private readonly List<Discretionary> _discretionaries = null!;
protected ProductConsultant()
{
// Required by EF
}
// Public constructor goes here
public virtual IReadOnlyCollection<Discretionary> Discretionaries => _discretionaries.AsReadOnly();
public void RemoveDiscretionaryWithId(int discretionaryId)
{
_discretionaries.RemoveAll(x => x.Id == discretionaryId);
}
}
If I were to call RemoveDiscretionaryWithId
before Discretionaries
was first accessed, the code would throw a NullReferenceException
. I can't directly use Discretionaries
within that method because I need to mutate the collection, and the read-only wrapper prevents that.
To work around this, I'd apparently need to do something really hacky like this:
public class ProductConsultant
{
private readonly List<Discretionary> _discretionaries = null!;
protected ProductConsultant()
{
// Required by EF
}
// Public constructor goes here
private List<Discretionary> PrivateDiscretionaries
{
get
{
Discretionaries.ToList();
return _discretionaries;
}
}
public virtual IReadOnlyCollection<Discretionary> Discretionaries => _discretionaries.AsReadOnly();
public void RemoveDiscretionaryWithId(int discretionaryId)
{
PrivateDiscretionaries.RemoveAll(x => x.Id == discretionaryId);
}
}
But I'd be very reluctant to have to do that for every collection navigation of all my entities.
I've seen this question already, but it doesn't address this particular issue as the property in that scenario is exposed with a mutable collection type.