Entity framework 首先使用EF代码在数据库级别停止空字符串
首先考虑实体框架代码的以下POCO实体:Entity framework 首先使用EF代码在数据库级别停止空字符串,entity-framework,null,ef-code-first,constraints,Entity Framework,Null,Ef Code First,Constraints,首先考虑实体框架代码的以下POCO实体: public class Foo { public int Id { get; set; } [Required, StringLength(100)] public string Name { get; set; } } 这将生成下表: CREATE TABLE [dbo].[Foo] ( [Id] INT IDENTITY (1, 1) NOT NULL, [Name]
public class Foo
{
public int Id { get; set; }
[Required, StringLength(100)]
public string Name { get; set; }
}
这将生成下表:
CREATE TABLE [dbo].[Foo] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (100) NOT NULL,
CONSTRAINT [PK_dbo.Foo] PRIMARY KEY CLUSTERED ([Id] ASC)
);
现在,我了解到EF的默认行为是将空字符串转换为null。所以,即使我显式地给它一个空字符串,我也会得到一个验证异常,这是完美的。以下代码将抛出一个DbEntityValidationException
:
var f = new Foo { Name = "" };
context.Foos.Add(f);
context.SaveChanges();
但是,问题是如果我有一个直接访问数据库的外部应用程序,我可以执行以下查询并成功:
最好的解决方案可以说是不允许任何人直接连接到数据库并强制他们通过业务层。然而,在现实中,这并不总是可能的。特别是如果我自己通过SSIS包导入外部数据
我的理解是,应用程序应该设置为在尽可能低的级别拒绝尽可能多的坏数据。在这种情况下,这将意味着在数据库级别。因此,如果我们以老式的方式创建数据库,我会添加一个约束来检查(名称“”)
,并阻止脏数据被插入第一个位置
是否有一种方法可以让EF代码首先为我生成此约束,或者有其他方法可以让它在数据库级别强制执行非空字符串(最小长度1)——最好使用属性?或者是我在迁移中手动添加约束的唯一方法?有
MinLength
属性,但它不会在数据库级别强制执行约束,我认为应该使用迁移添加此约束
public partial class test : DbMigration
{
public override void Up()
{
Sql("ALTER TABLE [dbo].[YOUR_TABLE] ADD CONSTRAINT " +
"[MinLengthConstraint] CHECK (DATALENGTH([your_column]) > 0)");
}
public override void Down()
{
Sql("ALTER TABLE [dbo].[YOUR_TABLE] DROP CONSTRAINT [MinLengthConstraint]");
}
}
您可以为EF添加sql代码生成器来为MinLength
属性生成这些代码,我在这里给您一个简化的提示:
MinLength
public class Test
{
public int Id { get; set; }
[MinLength(1)]
public string Name { get; set; }
}
minlenghttribute
添加到约定中,并提供值,即Length
:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Add(
new AttributeToColumnAnnotationConvention<MinLengthAttribute, int>(
"MinLength",
(property, attributes) => attributes.Single().Length));
}
上面的代码包含AddColumn
和CreateTable
操作的生成。您还必须为AlterColumn
、DropTable
和DropColumns
添加代码internal sealed class Configuration : DbMigrationsConfiguration<TestContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
SetSqlGenerator("System.Data.SqlClient", new ExtendedSqlGenerator());
}
}
内部密封类配置:dbmigtorinsconfiguration
{
公共配置()
{
AutomaticMiggerationsEnabled=真;
SetSqlGenerator(“System.Data.SqlClient”,新扩展的sqlgenerator());
}
}
这就是自定义列注释的工作原理。这听起来很有希望,而且比必须手动将代码添加到
Up()
和Down()
要好得多——更糟糕的是,我把它放在那里了。什么是“迁移”?它在你的上下文中有什么特别的含义吗?@joedotn我不是说EF迁移。
CreateTable(
"dbo.Tests",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(
annotations: new Dictionary<string, AnnotationValues>
{
{
"MinLength",
new AnnotationValues(oldValue: null, newValue: "1")
},
}),
})
.PrimaryKey(t => t.Id);
public class ExtendedSqlGenerator : SqlServerMigrationSqlGenerator
{
protected override void Generate(AddColumnOperation addColumnOperation)
{
base.Generate(addColumnOperation);
AddConstraint(addColumnOperation.Column, addColumnOperation.Table);
}
protected override void Generate(CreateTableOperation createTableOperation)
{
base.Generate(createTableOperation);
foreach (var col in createTableOperation.Columns)
AddConstraint(col, createTableOperation.Name);
}
private void AddConstraint(ColumnModel column, string tableName)
{
AnnotationValues values;
if (column.Annotations.TryGetValue("MinLength", out values))
{
var sql = string.Format("ALTER TABLE {0} ADD CONSTRAINT " +
"[MinLengthConstraint] CHECK (DATALENGTH([{1}]) >= {2})"
,tableName, column.Name, values.NewValue);
Generate(new SqlOperation(sql));
}
}
}
internal sealed class Configuration : DbMigrationsConfiguration<TestContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
SetSqlGenerator("System.Data.SqlClient", new ExtendedSqlGenerator());
}
}