用于处理空字符串的数据注释

bit*_*ift 4 c# ef-power-tools entity-framework-core

我使用 EF Core Powertools 将数据库中的一组表逆向工程转换为 EF 数据模型。

在表中创建记录时,一个或多个 varchar 字段被标记为允许 null。

如果我更新这些字段上的实体属性以使用

[Required(AllowEmptyStrings = true)]
Run Code Online (Sandbox Code Playgroud)

这将使空字符串通过,但是在查询表中已有的实体列表(其中一个或多个字段包含空值)时,不会因为该属性而失败吗?例如,它不仅是空的,而且是空的。

那么如何修改实体定义以既允许保存空字符串,又允许查询结果中存在空值呢?

Ger*_*old 7

tl;dr
该属性对数据验证没有帮助。它甚至会给您带来麻烦,因为它限制了您的查询选项。

首先,与 EF6 相反,EF core 不进行任何数据验证。因此,任何属性都无法实现阻止空字符串进入可为空的数据库字段的良好意图。

更糟糕的是,该属性会给你带来麻烦。SQL 查询生成受是否需要属性的影响。让我演示一下使用这个小类:

public class Product
{   
    public int ID { get; set; }

    [Required(AllowEmptyStrings = true)] // or false
    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这个查询...

string name = "a";
var products = db.Products
    .Where(x => x.Name == name)
    .ToList();
Run Code Online (Sandbox Code Playgroud)

...with [Required]( AllowEmptyStringsfalse 或 true) 生成此WHERE子句:

WHERE ([p].[Name] = @__name_0) AND @__name_0 IS NOT NULL
Run Code Online (Sandbox Code Playgroud)

如果没有该属性,无非是:

WHERE (([p].[Name] = @__name_0) AND ([p].[Name] IS NOT NULL AND @__name_0 IS NOT NULL))
    OR ([p].[Name] IS NULL AND @__name_0 IS NULL)
Run Code Online (Sandbox Code Playgroud)

EF 这样做是为了获得null与 C# 中相同的语义。在 SQL 中,只是[p].[Name] = @__name_0不确定何时@__name_0NULL,并且查询不会返回任何记录,甚至不会返回为Namenull 的记录。C# 中的相同 LINQ 查询也可以。第二个子句中额外的 null 检查考虑了和WHERE都为 null 的情况,在 C# 中将被视为相等。顺便说一句,如果需要,这些空语义可以替换为数据库空语义。[Name]@__name_0

当需要该字段时,EF 假定该字段值永远不为空,并且忽略此额外条件。这种假设会给你带来麻烦。即使显式查询它们,查询也永远不会返回名称为空的记录。string name = null;不会返回名称为空的记录。

但现在最糟糕的是,EF 甚至不允许您null通过附加条件显式查询值:

var products = db.Products
    .Where(x => x.Name == name || x.Name == null)
    .ToList();
Run Code Online (Sandbox Code Playgroud)

EF 只是忽略该name == null条件。可以肯定该字段不能包含空值,因此该查询...

var products = db.Products
    .Where(x => x.Name == null)
    .ToList();
Run Code Online (Sandbox Code Playgroud)

产生这个 SQL 谓词:

WHERE CAST(0 AS bit) = CAST(1 AS bit)
Run Code Online (Sandbox Code Playgroud)

打扰一下?我可以查询空值吗?现在,您背负着遗留数据库,试图通过从现在开始将字段设置为必填字段来尽最大努力,但 EF 使这几乎不可能。在 EF6 中也是如此。

长话短说:不要使用该属性。它只会有害而无益。

###备择方案

  • 自己进行这些验证。博客有一些建议。
  • 如果使用 ASP.Net,请使用带有属性的 DTO/视图模型对象来获得早期数据输入验证。但这不能取代保存时的所有数据验证。
  • (首选)通过将空字符串转换为有用的内容来修复遗留数据,并使用该属性,因为它对查询有(现在有益的)影响,并且对其他框架(如 ASP.Net)中的模型验证有影响。