.EFCore - 是否可以根据数据库中的列最大大小创建数据注释验证?
.EFCore - Is it possible to create data annotation validation based on the column max size in the database?
我不想查看数据库列定义,而是希望根据数据库中的列大小进行 maxlength
验证,这可能吗?
我问是因为我的一些专栏是 varchar
,有些是 nvarchar
,这有时会造成混淆,因为对于 varchar
如果 max
是 100 那么您可以插入 100 个字符,但如果它是最大为 100 的 nvarchar,则只能插入 50 个字符。
我想停止这种混淆,这可能吗?
您可以使用 EF 运行 自定义 SQL 查询来读取有关 table 的元信息。 SQL 查询取决于您使用的实际数据库。对于 MySQL,我使用 EXPLAIN table
来获取有关 table 的元信息。此信息将通过您正在使用的 EF 上下文在名为 ExplainInfo
的实体中读取。
对于验证,您可以创建一个新的验证 class/attribute,它从 ValidationAttribute
扩展并覆盖 IsValid(object value, ValidationContext validationContext)
方法。 ValidationContext
参数很重要,因为我们必须将当前打开的上下文注入验证器,并获取有关实体类型和 属性 名称的信息以进行验证。我们用它来发送一个查询,该查询获取有关列的信息并根据它验证当前值。
作为概念验证,解决方案可能如下所示:
(作为余数,EXPLAIN table
的结果如下所示:)
mysql> EXPLAIN Author;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| Id | int | NO | PRI | NULL | auto_increment |
| Name | varchar(30) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
首先我们为 EXPLAIN
查询的结果创建一个实体:
public class ExplainInfo
{
[Key]
public string Field {get; set;}
public string Type {get; set;}
}
要使用它,我们必须像往常一样在上下文中添加一个新的 DbSet
条目:
public DbSet<ExplainInfo> ExplainInfo {get; set;}
然后我们编写新的验证属性:
public class LimitValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
MyContext context = (MyContext)validationContext.Items["context"];
string entityName = validationContext.ObjectType.Name;
string propertyName = validationContext.MemberName;
IList<ExplainInfo> metaInfo = context.ExplainInfo.FromSqlRaw($"EXPLAIN {entityName}").ToList();
ExplainInfo singleExplainInfo = metaInfo.Single(it => it.Field == propertyName);
string databaseType = singleExplainInfo.Type.ToLowerInvariant();
if (databaseType.StartsWith("varchar("))
{
string regexStr = @"varchar\((\d+)\)";
Regex regex = new Regex(regexStr);
Match match = regex.Match(databaseType);
string limitAsString = match.Groups[1].Value;
int limit = int.Parse(limitAsString);
string valueAsString = (string)value;
if (valueAsString.Length > limit) {
return new ValidationResult("The given value for the property "+propertyName+" exceed the limit of "+limit+" characters defined in the database");
}
}
return ValidationResult.Success;
}
}
我们将在我们的实体中使用此验证属性:
public class Author
{
// [...]
[LimitValidation]
public string Name {get; set;}
// [...]
}
最后我们将触发验证:
using (MyContext context = new MyContext())
{
Author author = context.Author.First();
ValidationContext validationContext = new ValidationContext(author, new Dictionary<object, object> {
{"context", context}
});
author.Name += "dummy text which will exceed the limit in the database";
Validator.ValidateObject(author, validationContext, true);
context.SaveChanges();
}
这将生成以下验证异常:
The given value for the property Name exceed the limit of 30 characters defined in the database
您必须针对 VARCHAR
和 NVARCHAR
类型以及 query to get the table meta info.
调整 IsValid()
方法
我不想查看数据库列定义,而是希望根据数据库中的列大小进行 maxlength
验证,这可能吗?
我问是因为我的一些专栏是 varchar
,有些是 nvarchar
,这有时会造成混淆,因为对于 varchar
如果 max
是 100 那么您可以插入 100 个字符,但如果它是最大为 100 的 nvarchar,则只能插入 50 个字符。
我想停止这种混淆,这可能吗?
您可以使用 EF 运行 自定义 SQL 查询来读取有关 table 的元信息。 SQL 查询取决于您使用的实际数据库。对于 MySQL,我使用 EXPLAIN table
来获取有关 table 的元信息。此信息将通过您正在使用的 EF 上下文在名为 ExplainInfo
的实体中读取。
对于验证,您可以创建一个新的验证 class/attribute,它从 ValidationAttribute
扩展并覆盖 IsValid(object value, ValidationContext validationContext)
方法。 ValidationContext
参数很重要,因为我们必须将当前打开的上下文注入验证器,并获取有关实体类型和 属性 名称的信息以进行验证。我们用它来发送一个查询,该查询获取有关列的信息并根据它验证当前值。
作为概念验证,解决方案可能如下所示:
(作为余数,EXPLAIN table
的结果如下所示:)
mysql> EXPLAIN Author;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| Id | int | NO | PRI | NULL | auto_increment |
| Name | varchar(30) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
首先我们为 EXPLAIN
查询的结果创建一个实体:
public class ExplainInfo
{
[Key]
public string Field {get; set;}
public string Type {get; set;}
}
要使用它,我们必须像往常一样在上下文中添加一个新的 DbSet
条目:
public DbSet<ExplainInfo> ExplainInfo {get; set;}
然后我们编写新的验证属性:
public class LimitValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
MyContext context = (MyContext)validationContext.Items["context"];
string entityName = validationContext.ObjectType.Name;
string propertyName = validationContext.MemberName;
IList<ExplainInfo> metaInfo = context.ExplainInfo.FromSqlRaw($"EXPLAIN {entityName}").ToList();
ExplainInfo singleExplainInfo = metaInfo.Single(it => it.Field == propertyName);
string databaseType = singleExplainInfo.Type.ToLowerInvariant();
if (databaseType.StartsWith("varchar("))
{
string regexStr = @"varchar\((\d+)\)";
Regex regex = new Regex(regexStr);
Match match = regex.Match(databaseType);
string limitAsString = match.Groups[1].Value;
int limit = int.Parse(limitAsString);
string valueAsString = (string)value;
if (valueAsString.Length > limit) {
return new ValidationResult("The given value for the property "+propertyName+" exceed the limit of "+limit+" characters defined in the database");
}
}
return ValidationResult.Success;
}
}
我们将在我们的实体中使用此验证属性:
public class Author
{
// [...]
[LimitValidation]
public string Name {get; set;}
// [...]
}
最后我们将触发验证:
using (MyContext context = new MyContext())
{
Author author = context.Author.First();
ValidationContext validationContext = new ValidationContext(author, new Dictionary<object, object> {
{"context", context}
});
author.Name += "dummy text which will exceed the limit in the database";
Validator.ValidateObject(author, validationContext, true);
context.SaveChanges();
}
这将生成以下验证异常:
The given value for the property Name exceed the limit of 30 characters defined in the database
您必须针对 VARCHAR
和 NVARCHAR
类型以及 query to get the table meta info.
IsValid()
方法