0

I use Entity Framework Core 7. When I declare an entity that is guaranteed to have a reference property, I declare it as non-nullable:

public class Employee {
    public Employer Employer { get; set; } // no such thing as employee without an employer.
}

However, Employer property is not guaranteed to be loaded by Entity Framework (I don't use lazy loading). I have a piece of code that needs to check if employee.Employer is null.

Since Employer is non-nullable, naturally, I get "we can replace employee.Employer is null with true". Which isn't always the case.

The underlying issue is that entity is used to represent a domain object (where Employer is always assumed to exist), and a DTO for loading data.

Most of the code is in the "domain" segment of the application, so I would prefer to keep Employer as non-nullable; even though it is more correct to mark it as nullable - I don't want to have to check for null every time I touch it (I'd rather have NRE to indicate that somebody forgot to call .Include(e => e.Employer).

I can use a comment to suppress a warning, but want to check if there is an idiomatic way to handle this.

Progman
  • 16,827
  • 6
  • 33
  • 48
THX-1138
  • 21,316
  • 26
  • 96
  • 160
  • Would it be possible to mark the entity as nullable, leave the dto as non-nullable and handle the null value in the mapper of the data layer? So you could set a default DTO when mapping from a null entity and your domain layer would need no null checks. – Sebastian S. Feb 25 '23 at 00:02
  • 1
    why do you think Employer is non-nullable? It's most certainly nullable, like all objects. Only primitives can be non-null, and if it couldn't be null, you wouldn't have to check if it's null or not. Note: That "we can replace the check with true" is telling you that at that time, the value is ALWAYS null. – John Lord Mar 07 '23 at 16:41
  • @JohnLord I'm referring to nullability in the context of "Nullable reference types" feature of C#. – THX-1138 Mar 09 '23 at 19:56
  • You've reached the edge of the map of ORM capabilities and are about to enter into the far lands. which is more dear to you, keeping your domain objects pure or use EF without layers to patch these incompatibilities. If you never use these objects to save data using EF you could implement a getter that throws an exception when the private property is not set. Then EF never has to read it. I assume it is a bug to read the Employer property when it is not loaded during the initial query. Alternatively load the EmployerId as property and make the relation more explicit. – Menno van Lavieren May 15 '23 at 13:45

1 Answers1

1

You hit the nail on the head:

The underlying issue is that entity is used to represent a domain object ... and a DTO

It's a double-edged sword that EF can be configured to use existing classes as entities, because it usually leads to the kinds of problems you're having. Bespoke entities should be thought of as declarative configuration for database tables. As such, they don't follow the normal rules of C# or OOP in general.

The conventional approach is to insert a service layer between EF and your domain logic to map your entities to more idiomatic domain models. For all my criticism of AutoMapper, I'm still using it (with a thin wrapper to work around this issue.) If you've decided it's wrong to ever load an Employee with a null Employer, the your service layer can Include the related entities and map your Employee entity to your Employee model.

I previously suggested using Automapper's ProjectTo, but recently discovered why my project was producing unexpected results. ProjectTo is oblivious to IIncludableQueryable and eagerly loads all relations.

Kevin Krumwiede
  • 9,868
  • 4
  • 34
  • 82