最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - Why does EF Core scaffold initialize non nullable nvarchar to "null!"? - Stack Overflow

programmeradmin2浏览0评论

In my database, I have a table:

CREATE TABLE [dbo].[Invitation]
(
    [Id] INT NOT NULL IDENTITY(1, 1),
    [Name] NVARCHAR(100) NOT NULL,
    [Subject] NVARCHAR(100) NOT NULL,
    [Body] NVARCHAR(max) NOT NULL,
    
    CONSTRAINT PK_Invitation PRIMARY KEY CLUSTERED ([Id] ASC),
)

I'm using EF Core 8.0.8. When I run dotnet ef dbcontext scaffold it creates the following C# class:

public partial class StudyInvitation
{
    public int Id { get; set; }

    public string Name { get; set; } = null!;

    public string Subject { get; set; } = null!;

    public string Body { get; set; } = null!;
}

Why does it set the strings to null!? It seems like string.Empty would be a better choice for NOT NULL nvarchar. In code, setting a string to null and setting a string to null! will both set the string to null. So I don't understand why they chose to do this. I'd like to know so that when I'm working with my models I know if initializing them to null! is better than initializing them the string.Empty for some reason. Thanks!

In my database, I have a table:

CREATE TABLE [dbo].[Invitation]
(
    [Id] INT NOT NULL IDENTITY(1, 1),
    [Name] NVARCHAR(100) NOT NULL,
    [Subject] NVARCHAR(100) NOT NULL,
    [Body] NVARCHAR(max) NOT NULL,
    
    CONSTRAINT PK_Invitation PRIMARY KEY CLUSTERED ([Id] ASC),
)

I'm using EF Core 8.0.8. When I run dotnet ef dbcontext scaffold it creates the following C# class:

public partial class StudyInvitation
{
    public int Id { get; set; }

    public string Name { get; set; } = null!;

    public string Subject { get; set; } = null!;

    public string Body { get; set; } = null!;
}

Why does it set the strings to null!? It seems like string.Empty would be a better choice for NOT NULL nvarchar. In code, setting a string to null and setting a string to null! will both set the string to null. So I don't understand why they chose to do this. I'd like to know so that when I'm working with my models I know if initializing them to null! is better than initializing them the string.Empty for some reason. Thanks!

Share Improve this question edited Feb 7 at 16:26 mmcfly asked Feb 7 at 0:55 mmcflymmcfly 8789 silver badges12 bronze badges 5
  • 2 Do those columns have a DEFAULT constraint? Also just to be sure, please edit to include the EF Core version. – Peter B Commented Feb 7 at 1:05
  • 3 What would you expect? By using null! you explicitly said: even if the property is supposed to be non-nullable, it's okay to assign null to it. This is a null-forgiving operator, it tells you that null is all right to use. See also another answer: What does null! statement mean?. – Sergey A Kryukov Commented Feb 7 at 1:17
  • string.Empty would be a better choice for NOT NULL nvarchar that would be a very bad idea because it would hide missing values, bypassing the database constraint. Consider what would happen if you forgot to set Name in your code. You'd end up with a lot of invitations to `` – Panagiotis Kanavos Commented Feb 7 at 10:33
  • 1 What's the actual EF Core version? The only alternative to =null! is to make the property required, ie public required string Name { get; set; }. That causes its own problems though, because you'd have to set all required properties during initialization. This can be a problem eg in a New Invitation form whose controls bind to a new StudyInvitation object. – Panagiotis Kanavos Commented Feb 7 at 10:40
  • 1 In short the scaffold command uses =null! because right now that's the most generic option. Other options have implications. You can customize the code-generation templates if you want, to eg use constructor initialization or the required parameter or set the default you want. – Panagiotis Kanavos Commented Feb 7 at 10:48
Add a comment  | 

1 Answer 1

Reset to default 4

If it did not use the null forgiveness you will end up with compiler warnings for uninitialized non-nullable reference types. (string) An alternative would have been to surround each of the properties with a warning disable:

#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
    public string Name { get; set; }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.

IMO this is a "noisy" option. The compiler warning can be completely disabled, or applied to the entire class but that negates the value it can have to avoid #null related bugs getting introduced.

Alternatively they could have used a [Required] null-able property:

    [Required]
    public string? Name { get; set; }

Personally not a fan of this option as when looking at values in Intelisense the null-ability of a property has an implied meaning of optional-ity.

string.Empty or default might be a arguable default, though they likely wanted a consistent behavior across all reference types. Entities will generally be populated from the database by EF when reading rows, but still need to be constructed and comply with compiler rules & checks. Defaulting a string to string.Empty could hide potential problems until runtime when code forgets to set a non-null-able string. The DB will just receive an empty string which will wait until a tester, or worse, a user notices that records are being created without a required piece of information.

My preferred solution is that my entities are normally declared in a DDD fashion for any code that needs to construct them which would have:

public partial class StudyInvitation
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; protected set; }

    public string Name { get; set; }

    public string Subject { get; set; }

    public string Body { get; set; }

    public StudyInvitation(string name, string subject, string body)
    {
        ArgumentException.ThrowIfNullOrEmpty(name, nameof(name));
        ArgumentException.ThrowIfNullOrEmpty(subject, nameof(subject));
        ArgumentException.ThrowIfNullOrEmpty(body, nameof(body));
        
        Name = name;
        Subject = subject;
        Body = body;
   }
}

Where I'll usually avoid public setters entirely for required fields and use modifier methods to ensure "updating" a record has any related/required info provided and validated explicitly, no chance of partial state changes. This is what you might use for a normal class but EF won't always like parameterized constructors, so I also add:

#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
    // Constructor for EF.
    protected StudyInvitation()
    { }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.

For a non-DDD where you want to just rely on public setters you can use the same approach with a public default constructor to shut the compiler warning up about non-nullable reference type properties. The scaffolding just opted for the #null forgiveness. The database will still reject rows inserted without the required string.

发布评论

评论列表(0)

  1. 暂无评论