5

I'm using EF Core, and I have this model:

public class Article
{
    public int Id { get; set; }
    public User Author { get; set; } // <- CS8618
    public int AuthorId { get; set; }
}

As pointed out in the above code comment, I get an annoying nullable warning. I'm using the Fluent API to annotate my model, and obviously Author is not nullable as AuthorId is not nullable.

I feel that using User? Author instead to make the warning go away is incorrect, as the property is obviously not nullable, and it would be a lie, and also a hint to people using my model that it is nullable.

What is the proper way to deal with this situation? Suppress the warning?

HelloWorld
  • 3,381
  • 5
  • 32
  • 58
  • If you create a new Article the Author property will be null, you might set it during initialization but that is optional. Also if you query for it without including the Author it will be null. So there are several of cases of it being null as I see it. – Karl-Johan Sjögren Jun 04 '22 at 16:24
  • 4
    Make sure to [read the docs](https://learn.microsoft.com/en-us/ef/core/miscellaneous/nullable-reference-types) that cover in detail using entity framework with NRTs. – Kirk Woll Jun 04 '22 at 16:30
  • 1
    Did you try to add `[Required]` attribute? – Bartłomiej Stasiak Jun 04 '22 at 19:39
  • Does this answer your question? [How to use C# 8.0 Nullable Reference Types with Entity Framework Core models?](https://stackoverflow.com/questions/58171942/how-to-use-c-sharp-8-0-nullable-reference-types-with-entity-framework-core-model) – Ray Jun 08 '22 at 19:50

5 Answers5

7

Since C#11 (.NET 7.0) you can add a required modifier.

public class Article
{
    public int Id { get; set; }
    public required User Author { get; set; }
    public int AuthorId { get; set; }
}

And with the required modifier, you won't be able to use var article= new Article(); to create an object instance. So it's great protection for the not nullable property User.

Now to create an object instance you must do something like:

var article= new Article()
{
   User = new();
};

Originally answer was posted on: What is best way to create .NET6 class with many non-nullable properties?

More detailed info: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/required

Yoro
  • 805
  • 8
  • 17
6

You'd find a lot of different answers on this question

But in this particular case, I would ask myself the question: who generates instances of this class?

1a

If you are 100% relying on EF Core to generate instances of this class for you by querying the database, and you are 100% sure that you ALWAYS use the proper Include(), I would safely set the Author field to null!, which means it assigns the default value null on construction and automatically suppresses the nullability error.

public class Article
{
    public int Id { get; set; }
    public User Author { get; set; } = null!;
    public int AuthorId { get; set; }
}

1b

You could also use the above solution with a private constructor to protect that no-one else but EF Core will generate instances of this class.

public class Article
{
    private Article()
    {
        Author = null!;
    }

    public int Id { get; set; }
    public User Author { get; set; }
    public int AuthorId { get; set; }
}

2

If you generate instances of this class in any other parts of your code, you would need a constructor to ensure that Author is never null after construction.

public class Article
{
    public Article()
    {
        Author = new User();
    }

    public int Id { get; set; }
    public User Author { get; set; }
    public int AuthorId { get; set; }
}

3

If you do not allow a default User object, it would make sense to expect a null value for Author but mark it Required to ensure that it will never be null when written to database.

public class Article
{
    public int Id { get; set; }

    [Required]
    public User? Author { get; set; }
    public int AuthorId { get; set; }
}
joakimriedel
  • 1,801
  • 12
  • 27
  • 2
    Not me that downvoted :-) Anyways, I ended up with just disabling the null checking feature. Haven't had a NullReferenceException since 1995 or thereabouts, so not really an issue. They just added that stupid feature to be equally cool as Kotlin. – HelloWorld Jun 08 '22 at 13:06
  • @HelloWorld you could still get NRE, usage of NRT is just a compile time feature. But I really appreciate the opportunity of explicitly explaining my **intent** with my code by properly flagging possible null or not-null. It is definitely worth it in the long run when the code base grows. – joakimriedel Jun 08 '22 at 19:35
  • EF Core also detects and uses constructors with parameters, so you can change the constructor to require a (non-nullable) `User` instance. – Ray Jun 08 '22 at 19:43
  • @Ray I wish this was true, but it does not work with navigation references. See https://learn.microsoft.com/en-us/ef/core/modeling/constructors (the note: _EF Core cannot set navigation properties (such as Blog or Posts above) using a constructor_) – joakimriedel Jun 08 '22 at 19:45
  • I see, nevermind then. – Ray Jun 08 '22 at 19:46
0

Common solution for domain entities is: use private default constructor for EF and public parameterized (keyless) one for explicit object creation. And there are two ways:

  1. Mark props with reference type by "promises" (= null!;)
  2. Disable warning on EF-constructor:
public class MyEntity {
    public int Id { get; [UsedImplicitly] private set; }
    public string Name { get; private set; }

    public MyEntity(string name) {
        Name = string.IsNullOrWhiteSpace(name)
            ? name
            : throw new ArgumentException(nameof(name));
    }

#pragma warning disable CS8618
    [UsedImplicitly]
    private MyEntity(){} // For EF only, allows to fill an object via setters
#pragma warning restore CS8618
}

Second approach is much better because checking still works with parameterized constructor. Private constructor makes minimal impact for type safety and doesn't require manual support.

Free_ze
  • 97
  • 2
-1

Just put "#nullable disable" above the namespace.

kddinev18
  • 76
  • 4
  • 3
    Disabling null safety for all fields without justification is a bad practice. – joakimriedel Jun 07 '22 at 18:09
  • @joakimriedel: If you use database first, then there is no problem, because the database defines the nullability. – Poul Bak Jun 08 '22 at 18:09
  • @PoulBak not related. You would still get null if you forget to include the navigation reference in a query, and users of this _code contract_ would get confused. – joakimriedel Jun 08 '22 at 19:37
-1

If it is possible Author to be null make the User property nullable:

public User? Author { get; set; }

If not do this :

 public User Author { get; set; } = default!;