Entity Framework, nullable, and no warnings or hacks

David Thielen 2,281 Reputation points
2023-03-19T18:39:22.8866667+00:00

I am trying to get my model constructors written so that nullable works, with no warnings, and no having to assign default! - in other words, to do it all right.

BTW, if I'm wrong on any of this, please let me know.

I'm mostly there. What's left is collections. It's not seeing that I am setting a required property in the constructor.

public class Campaign
{
    private string _name;

    public int Id { get; private set; }

    public required string Name
    {
        get => _name;
        set => _name = value.Trim();
    }

    public required State State { get; set; }

    public required ICollection<Event> Events { get; set; }

    public required ICollection<User> Followers { get; set; }

    public bool Enabled { get; set; }

    public bool Closed { get; set; }

    public bool Deleted { get; set; }

    public DateTime Created { get; private set; }

    protected Campaign(int id, string name, DateTime created)
    {
        _name = name;
        Id = id;
        Created = created;
    }

    public Campaign()
    {
        _name = string.Empty;
        Events = new List<Event>();
        Followers = new List<User>();
        Enabled = true;
        Created = DateTime.UtcNow;
    }
}

In my xUnit test it requires that I set Events & Followers:

var campaign = new Campaign
    {
        Name = "Test Campaign",
        State = state,
        // compile error without the following two lines
        Followers = new List<User>(),
        Events = new List<Event>()
    };

Why is it requiring this? Is the trick to get rid of the required in the declaration for those two properties? And if so, is there any downside to that?

If I get rid of the required, then in the protected constructor I need to set those collection properties to default! which I am not wild on (trying to not use that construct). But if the only pace I do that is a constructor used only be EF, I can live with that.

The protected (I read somewhere that private won't work for EF in some cases) constructor is for EF only.

Update: As I try different things, I am leaning toward the following.

First, each model has 2 constructors. A protected one for EF and a public one that has no parameters. Why no parameters? Because the only use cases I see for my (as opposed to EF) creating a model object is I am going to build one up to insert in the DB. In this case my approach is to create object, and then populate it.

In the public/no params constructor, I set all non-nullable strings to Empty and all collections to empty lists. So everything is in the correct NRT state.

I then remove the required keyword from those properties because each is set either in my constructor or by EF. So there's no need to remind/enforce setting it.

If I'm missing something or there's a better way, please let me know.

Entity Framework Core
Entity Framework Core
A lightweight, extensible, open-source, and cross-platform version of the Entity Framework data access technology.
698 questions
{count} votes