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

轴距

正如实体框架团队建议的那样,我想在生成数据库架构(DDL)时修改EF:CF所生成的SQL

如何才能做到这一点?

我无法通过Google找到任何合适的方法。

丹尼尔·特雷比恩

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

考虑您链接到的工作项的示例

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,将会看到两个重要的属性ColumnTableColumnColumnModel由Lambda在Up()中创建的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默认'未知' 
ALTER TABLE [dbo]。[MyEntity] ADD CONSTRAINT [ML_dbo.MyEntity_Name] CHECK(LEN(LTRIM(RTRIM([名称])))> 5)

另请参阅:EF6:编写自己的代码优先迁移操作,它显示了如何实现自定义迁移操作。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章