Entity Framework 6 Code first 默认值覆盖 MigrationCodeGenerator

Entity Framework 6 Code first Default value overriding the MigrationCodeGenerator

我发现的所有关于声明默认值的方法都会在 Sql 脚本中生成默认值,而不是在迁移代码中。

我最喜欢的是使用属性:

[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));

然后自定义Sql生成器。但我不喜欢这样,因为在生成迁移时我看不到一切是否如我所愿。

为此,我修改了 MigrationCodeGenerator,如下所示:

在自定义 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 或任何其他注释。

是否可以获取此注释?

你没有说明你是如何注册你的 ExtendedMigrationCodeGenerator 的,你可以在 Configuration class 在 Configuration.cs 例如:

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;
    }
}

您不会看到生成的输出的另一种情况是,如果约定和注释已经应用在先前生成的迁移中,该迁移是在您配置自定义 之前[=13] =].

调试提示:

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

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

It is better to attach in a constructor rather than near the code you want to debug because we know the constructor should be executed under normal conditions, but the reason your code isn't working might be due to the method or code branch not being executed at all, if it's not being executed, then the Launch() command also will not be executed.

如果您使用此方法调试迁移,但没有出现调试附加对话框,则要么没有检测到迁移,要么您的 ExtendedMigrationCodeGenerator 未正确注册。