43

I am enabling C# 8.0 Nullable Reference Types on a .NET Core 3.0 project. The project uses Entity Framework Core 3.0 to access database.

The following is a data model whose Title should not be null.

public class Vehicle
{
    public int Id { get; private set; } 

    public string Title { get; private set; }

    // Entity Framework Core is instructed to bind to the private _drivers field in a configuration builder
    private readonly List<Driver> _drivers = new List<Driver>();
    public IReadOnlyCollection<Driver> Drivers => _drivers.AsReadOnly();

    private Vehicle() 
    {
    }

    public Vehicle(string title) 
    {
        this.Title = title;
    }

    public void AddDriver(string name)
    {
         this._drivers.Add(new Driver(name));
    }
 }

// A foreign column is defined in a configuration builder
public class Driver
{
    public int Id { get; private set; } 

    public string Name { get; private set; }

    private Driver() 
    {
    }

    public Driver(string name) 
    {
        this.Name = name;
    }
 }

Own code is supposed to use the public constructors only while the private constructors are there just to allow Entity Framework Core and (potentially also) serialization to bind values from database to these classes/models. The public constructor might have different structure, list and types of arguments than which properties the model has (for example, it might also contains arguments for the first required child, it might have some arguments optional etc.).

However, the compiler generates CS8618 Non-nullable field is uninitialized. Consider declaring as nullable. on the private constructors.

I am able to disable CS8616 for the private constructors by #pragma warning disable CS8618 but I do not consider that as a good idea.

How it is supposed to use C# 8.0 Nullable Reference Types in this scenario? Or is my model bogus or violates best practices - how to do it properly?

Unfortunatelly, I have found not relevant docs or guidance.

1

3 Answers 3

16

There is no proper way to handle non-nullable navigational properties.

  1. Documentation suggests two ways and both are not type safe. Use a backing field and throw InvalidOperationException. It is unclear how it differs from doing nothing and have a NullReferenceException
  2. Suppress it with null forgiving operator

Official documentation link: https://learn.microsoft.com/en-us/ef/core/miscellaneous/nullable-reference-types#non-nullable-properties-and-initialization

Sign up to request clarification or add additional context in comments.

Comments

10

I agree with you- pragma blocks are ugly. Instead, I would assign the null forgiving operator to the non-nullable reference type inside the default constructor, like so:

private Vehicle() 
{
    Title = null!;
}

This is much cleaner and more expressive than initializing the property with said operator inline like so:

public string Title { get; private set; } = null!;

The latter solution reads as "I know Title is not null regardless of scenario" which effectively negates the benefit of non-nullable reference types since you lose all of your design time checks. The former reads as "I know Title is not null in this specific scenario", so compiler warnings continue to be issued elsewhere if you miss an assignment.

1 Comment

You may want to use the default keyword instead of null. public string Title { get; private set; } = default!;. This way, you can follow the same pattern for value types.
2

From the MS Docs for Entity types with constructors

When EF Core creates instances of these types, such as for the results of a query, it will first call the default parameterless constructor and then set each property to the value from the database. However, if EF Core finds a parameterized constructor with parameter names and types that match those of mapped properties, then it will instead call the parameterized constructor with values for those properties and will not set each property explicitly.

Perhaps it is worth creating a private ctor with the parameter needed for those properties and see if the Framework will then call that and work?

Also disabling warnings is not a good idea unless you are fully 100% confident that it is ok to disable it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.