实体框架6:如何覆盖SQL生成器?

时间:2014-12-07 18:50:26

标签: entity-framework ef-code-first entity-framework-6 ef-migrations sql-generation

我希望在生成数据库架构(DDL)时修改由EF:CF生成的SQL,如suggested by the Entity Framework team

如何做到这一点?

我无法通过Google找到合适的内容。

1 个答案:

答案 0 :(得分:18)

您可以通过调用DbMigrationsConfiguration类的构造函数中的MigrationSqlGenerator方法来覆盖Entity Framework使用的DbMigrationsConfiguration.SetSqlGenerator(),并传递数据库提供程序名称(例如{{1} (对于SQL Server),以及用于该数据库提供程序的"System.Data.SqlClient"实例。

考虑您链接到the work item的示例:

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"); } } 生成任何内容。

要让EF传达最小长度要求,您可以specify an attribute-to-column annotation convention。正如该文档页面中所提到的,默认SQL生成器会忽略任何AnnotationValues

在DbContext的OnModelCreating()覆盖中,添加以下内容:

MinLengthAttribute

添加后,您可以通过运行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" }, }); } } 值是否大于minLength(在这种情况下为5)。

您可以首先创建扩展Name的自定义MigrationSqlGenerator并调用SetSqlGenerator()来安装自定义SqlServerMigrationSqlGenerator

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) { //... } } 会覆盖Generate(AddColumnOperation)方法,但只是调用基本实现。

如果查看the documentation of AddColumnOperation,您会看到两个重要属性,CustomSqlServerMigrationSqlGeneratorColumnTable是由{(1}}中的lambda创建的ColumnModel

在Generate()方法中,您可以通过Column的{​​{1}}属性访问自定义c => c.String(nullable: false, annotations: ...)

要生成添加约束的DDL,您需要生成SQL并调用Statement()方法。例如:

AnnotationValues

如果您运行Annotations,您会看到ColumnModel生成的异常:

minLength 5 specified for dbo.MyEntity.Name, but the default value, '', does not satisfy this requirement.

要解决此问题,请在Up()方法中指定一个长度超过最小长度的defaultValue(例如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,您将看到添加列的"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") }, })); } 语句:

ALTER TABLE [dbo].[MyEntity] ADD [Name] [nvarchar](max) NOT NULL DEFAULT 'unknown'
ALTER TABLE [dbo].[MyEntity] ADD CONSTRAINT [ML_dbo.MyEntity_Name] CHECK (LEN(LTRIM(RTRIM([Name]))) > 5)

另请参阅:EF6: Writing Your Own Code First Migration Operations,其中显示了如何实施自定义迁移操作。