tl;dr
该属性不能帮助您进行数据验证。它甚至会给您带来麻烦,因为它限制了您的查询选项。
首先,与 EF6 不同,EF 核心不进行任何数据验证。因此,任何属性都无法满足您阻止空字符串进入可为空的数据库字段的良好意图。
更糟糕的是,该属性会给你带来麻烦。SQL 查询生成受是否需要属性的影响。让我演示一下使用这个小类:
public class Product
{
public int ID { get; set; }
[Required(AllowEmptyStrings = true)] // or false
public string Name { get; set; }
}
这个查询...
string name = "a";
var products = db.Products
.Where(x => x.Name == name)
.ToList();
...with [Required]
(AllowEmptyStrings
false 或 true)生成此WHERE
子句:
WHERE ([p].[Name] = @__name_0) AND @__name_0 IS NOT NULL
没有属性不外乎:
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)
EF 这样做是为了获得与null
C# 中相同的语义。在 SQL 中,只是[p].[Name] = @__name_0
不确定何时@__name_0
是NULL
并且查询不会返回任何记录,即使Name
是 null 的记录也不返回。C# 中的相同 LINQ 查询会。第二WHERE
个子句中额外的空检查说明了两者都为空的情况[Name]
,@__name_0
这在 C# 中将被视为相等。顺便说一句,如果需要,这些空语义可以用数据库空语义代替。
当需要该字段时,EF 假定该字段值永远不会为空,并省略此额外条件。这个假设是给你带来麻烦的原因。即使显式查询它们,查询也永远不会返回具有空名称的记录。没有带有string name = null;
空名称的记录将被返回。
但是现在对于总的无赖,EF 甚至不允许您null
通过附加条件显式查询值:
var products = db.Products
.Where(x => x.Name == name || x.Name == null)
.ToList();
EF 只是忽略了这个name == null
条件。可以确定该字段不能包含此查询的空值...
var products = db.Products
.Where(x => x.Name == null)
.ToList();
产生这个 SQL 谓词:
WHERE CAST(0 AS bit) = CAST(1 AS bit)
打扰一下?请问我可以查询空值吗?你在那里,背负着一个遗留数据库,试图通过从现在开始制作必填字段来尽你所能,但 EF 使它几乎不可能。在 EF6 中也是如此。
长话短说:不要使用该属性。它只会有害无益。
备择方案
- 自己进行这些验证。这个博客有一些建议。
- 如果使用 ASP.Net,请使用具有属性的 DTO/视图模型对象来获得早期数据输入验证。但是,这不能代替保存时的所有数据验证。
- (首选)通过将空字符串转换为有用的东西来修复遗留数据并使用该属性,因为它(现在是有益的)对查询的影响以及它对其他框架(如 ASP.Net)中的模型验证的影响。