根据 Entity Framework team 的建议,我想修改 EF:CF 在生成数据库模式 (DDL) 时生成的 SQL 。
如何才能做到这一点?
我无法通过谷歌找到任何合适的东西。
您可以通过在类的构造函数中调用DbMigrationsConfiguration.SetSqlGenerator()方法、传递数据库提供程序名称(例如,对于 SQL Server)和用于该数据库提供程序的实例来覆盖MigrationSqlGenerator
Entity Framework使用的那个。DbMigrationsConfiguration
"System.Data.SqlClient"
MigrationSqlGenerator
考虑您链接到的工作项中的示例:
public class MyEntity
{
public int Id { get; set; }
[Required]
[MinLength(5)]
public string Name { get; set; }
}
假设MyEntity
已经生成了表,并且使用Add-Migration
命令添加了Name
字段。
默认情况下,脚手架迁移是:
public partial class AddMyEntity_Name : DbMigration
{
public override void Up()
{
AddColumn("dbo.MyEntity", "Name", c => c.String(nullable: false));
}
public override void Down()
{
DropColumn("dbo.MyEntity", "Name");
}
}
请注意,脚手架没有为MinLengthAttribute
.
要让 EF 传达最小长度要求,您可以指定属性到列的注释约定。如该文档页面AnnotationValues
所述,默认 SQL 生成器会忽略任何内容。
在 DbContext 的 OnModelCreating() 覆盖中,添加以下内容:
modelBuilder.Conventions.Add(new AttributeToColumnAnnotationConvention<MinLengthAttribute, Int32>("minLength", (property, attributes) => attributes.Single().Length));
添加后,您可以通过运行重新生成脚手架迁移Add-Migration -Force AddMyEntity_Name
。现在脚手架迁移是:
public partial class AddMyEntity_Name : DbMigration
{
public override void Up()
{
AddColumn("dbo.MyEntity", "Name", c => c.String(nullable: false,
annotations: new Dictionary<string, AnnotationValues>
{
{
"minLength",
new AnnotationValues(oldValue: null, newValue: "5")
},
}));
}
public override void Down()
{
DropColumn("dbo.MyEntity", "Name",
removedAnnotations: new Dictionary<string, object>
{
{ "minLength", "5" },
});
}
}
假设在链接的工作项中,您希望生成一个约束来检查修剪后的Name
值是否大于 minLength(在本例中为 5)。
您可以首先创建一个自定义MigrationSqlGenerator
扩展SqlServerMigrationSqlGenerator
并调用 SetSqlGenerator() 来安装自定义MigrationSqlGenerator
:
internal class CustomSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
protected override void Generate(AddColumnOperation addColumnOperation)
{
base.Generate(addColumnOperation);
}
}
internal sealed class Configuration : DbMigrationsConfiguration<DataContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
SetSqlGenerator("System.Data.SqlClient", new CustomSqlServerMigrationSqlGenerator());
}
protected override void Seed(DataContext context)
{
//...
}
}
现在,这CustomSqlServerMigrationSqlGenerator
覆盖了 Generate(AddColumnOperation) 方法,但只是调用了基本实现。
如果您查看的文档AddColumnOperation
,您将看到两个重要的属性,Column
以及Table
. Column
是ColumnModel
由 Up(), 中的 lambda 创建的c => c.String(nullable: false, annotations: ...)
。
在 Generate() 方法中,您可以AnnotationValues
通过Annotations
.ColumnModel
要生成添加约束的 DDL,您需要生成 SQL 并调用 Statement() 方法。例如:
internal class CustomSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
protected override void Generate(AddColumnOperation addColumnOperation)
{
base.Generate(addColumnOperation);
var column = addColumnOperation.Column;
if (column.Type == System.Data.Entity.Core.Metadata.Edm.PrimitiveTypeKind.String)
{
var annotations = column.Annotations;
AnnotationValues minLengthValues;
if (annotations.TryGetValue("minLength", out minLengthValues))
{
var minLength = Convert.ToInt32(minLengthValues.NewValue);
if (minLength > 0)
{
if (Convert.ToString(column.DefaultValue).Trim().Length < minLength)
{
throw new ArgumentException(String.Format("minLength {0} specified for {1}.{2}, but the default value, '{3}', does not satisfy this requirement.", minLength, addColumnOperation.Table, column.Name, column.DefaultValue));
}
using (var writer = new StringWriter())
{
writer.Write("ALTER TABLE ");
writer.Write(Name(addColumnOperation.Table));
writer.Write(" ADD CONSTRAINT ");
writer.Write(Quote("ML_" + addColumnOperation.Table + "_" + column.Name));
writer.Write(" CHECK (LEN(LTRIM(RTRIM({0}))) > {1})", Quote(column.Name), minLength);
Statement(writer.ToString());
}
}
}
}
}
}
如果您运行Update-Database -Verbose
,您将看到由以下原因生成的异常CustomSqlServerMigrationSqlGenerator
:
为 dbo.MyEntity.Name 指定了 minLength 5,但默认值 '' 不满足此要求。
要解决此问题,请在 Up() 方法中指定一个比最小长度更长的 defaultValue(例如"unknown"
):
public override void Up()
{
AddColumn("dbo.MyEntity", "Name", c => c.String(nullable: false, defaultValue: "unknown",
annotations: new Dictionary<string, AnnotationValues>
{
{
"minLength",
new AnnotationValues(oldValue: null, newValue: "5")
},
}));
}
现在,如果您重新运行Update-Database -Verbose
,您将看到ALTER TABLE
添加列的ALTER TABLE
语句和添加约束的语句:
ALTER TABLE [dbo].[MyEntity] ADD [Name] [nvarchar](max) NOT NULL DEFAULT 'unknown' ALTER TABLE [dbo].[MyEntity] 添加约束 [ML_dbo.MyEntity_Name] 检查 (LEN(LTRIM(RTRIM([Name]))) > 5)
另请参阅:EF6:编写您自己的代码优先迁移操作,其中展示了如何实现自定义迁移操作。