Entity Framework 6 Code First:如何覆盖默认值MigrationCodeGenerator?

5
我发现的所有有关声明默认值的方法都是在 SQL 脚本中生成默认值,而不是在迁移代码中生成。
我最喜欢的方法是使用属性:https://dev59.com/nWIk5IYBdhLWcg3wI7AF#34894274
[SqlDefaultValue(DefaultValue = "getutcdate()")]
public DateTime CreatedDateUtc { get; set; }

属性定义

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class SqlDefaultValueAttribute : Attribute
{
    public string DefaultValue { get; set; }
}

在上下文的"OnModelCreating"中添加
modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue));

然后是定制SqlGenerator。但我不喜欢这个方式,因为我无法确定生成的迁移是否符合我的期望。

为了解决这个问题,我修改了MigrationCodeGenerator如下:https://dev59.com/ZWoy5IYBdhLWcg3wYc8l#21024108

在自定义的MigrationCodeGenerator中

public class ExtendedMigrationCodeGenerator : MigrationCodeGenerator
{
    public override ScaffoldedMigration Generate(string migrationId, IEnumerable<MigrationOperation> operations, string sourceModel, string targetModel, string @namespace, string className)
    {
        foreach (MigrationOperation operation in operations)
        {
            if (operation is CreateTableOperation)
            {
                foreach (var column in ((CreateTableOperation)operation).Columns)
                {
                    System.Data.Entity.Infrastructure.Annotations.AnnotationValues values;
                    if (column.Annotations.TryGetValue("SqlDefaultValue", out values))
                    {
                        column.DefaultValueSql = (string)values.NewValue;
                    }
                }
            }
            else if (operation is AddColumnOperation)
            {
                ColumnModel column = ((AddColumnOperation)operation).Column;

                System.Data.Entity.Infrastructure.Annotations.AnnotationValues values;
                if (column.Annotations.TryGetValue("SqlDefaultValue", out values))
                {
                    column.DefaultValueSql = (string)values.NewValue;
                }

            }
        }

        CSharpMigrationCodeGenerator generator = new CSharpMigrationCodeGenerator();

        return generator.Generate(migrationId, operations, sourceModel, targetModel, @namespace, className);
    }
}

但是在使用ScaffoldedMigration方法时,我无法获取我的自定义注释SqlDefaultValue或任何其他注释。

是否有可能获取这个注释?


你发布的代码按预期工作(在迁移中的 CreateTable 调用中,我得到了 CreatedDateUtc = c.DateTime(nullable: false, defaultValueSql: "getutcdate()"))。你确定你的自定义迁移代码生成器被调用了吗?例如,在 Configuration 类中设置它为 CodeGenerator 的那一行? - Ivan Stoev
1个回答

0

您还没有说明如何将您的ExtendedMigrationCodeGenerator注册以供使用,例如您可以在Configuration.cs中的Configuration类的构造函数中完成:

// 注册代码生成器
this.CodeGeneratorTypes.Add(typeof(ExtendedMigrationCodeGenerator));

public Configuration()
{
    AutomaticMigrationsEnabled = false;
    AutomaticMigrationDataLossAllowed = false;
    // Register the Customized Migration Generator to use
    CodeGenerator = new ExtendedMigrationCodeGenerator();
}

但也不要忘记 AlterColumnOperation,如果你将其应用于现有架构,这可能是你最大的问题。
else if (operation is AlterColumnOperation alterColumnOp)
{
    ColumnModel column = alterColumnOp.Column;

    System.Data.Entity.Infrastructure.Annotations.AnnotationValues values;
    if (column.Annotations.TryGetValue("SqlDefaultValue", out values))
    {
        column.DefaultValueSql = (string)values.NewValue;
    }
}

另一种情况是,如果约定和注释已经应用于之前生成的迁移(在您配置自定义ExtendedMigrationCodeGenerator之前),则不会看到生成的输出。

调试提示:

调试自定义迁移逻辑并不像设置断点那样简单,因为它通常由外部进程(如Migration.exe)执行。因此,在断点起作用之前,我们需要调用调试器,我们可以通过在要调试的位置或迁移代码生成器类的构造函数中插入以下代码来实现:

if (!System.Diagnostics.Debugger.IsAttached)
    System.Diagnostics.Debugger.Launch();

最好在构造函数中附加,而不是在您想要调试的代码附近,因为我们知道构造函数应该在正常情况下执行,但您的代码不起作用的原因可能是由于方法或代码分支根本没有被执行,如果它没有被执行,那么Launch()命令也不会被执行。
如果您使用此方法调试迁移,并且您没有收到调试附加对话框,则表示未检测到任何迁移,或者您的ExtendedMigrationCodeGenerator未正确注册。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接