我需要在表中添加列,此列不存在.原因:在某些数据库中我们有这样的列,而在某些数据库中则没有.我已经为我的模型添加了字段并创建了迁移:
public override void Up() { AddColumn("dbo.NavFilters", "Promo", c => c.String(maxLength: 100, nullable:true)); } public override void Down() { DropColumn("dbo.NavFilters", "Promo"); }
我可以检查现有的列吗?
我一直致力于创建自定义迁移方法AddColumnIfNotExists
您需要一个自定义MigrationOperation类:
public class AddColumnIfNotExistsOperation : MigrationOperation { public readonly string Table; public readonly string Name; public readonly ColumnModel ColumnModel; public AddColumnIfNotExistsOperation(string table, string name, FunccolumnAction, object anonymousArguments) : base(anonymousArguments) { ArgumentValidator.CheckForEmptyArgument(table, nameof(table)); ArgumentValidator.CheckForEmptyArgument(name, nameof(name)); ArgumentValidator.CheckForNullArgument(columnAction, nameof(columnAction)); Table = table; Name = name; ColumnModel = columnAction(new ColumnBuilder()); ColumnModel.Name = name; } public override bool IsDestructiveChange => false; public override MigrationOperation Inverse => new DropColumnOperation(Table, Name, removedAnnotations: ColumnModel.Annotations.ToDictionary(s => s.Key,s => (object)s.Value) , anonymousArguments: null); }
您还需要一个自定义SqlGenerator类:
public class AddColumnIfNotExistsSqlGenerator : SqlServerMigrationSqlGenerator { protected override void Generate(MigrationOperation migrationOperation) { var operation = migrationOperation as AddColumnIfNotExistsOperation; if (operation == null) return; using (var writer = Writer()) { writer.WriteLine("IF NOT EXISTS(SELECT 1 FROM sys.columns"); writer.WriteLine($"WHERE Name = N'{operation.Name}' AND Object_ID = Object_ID(N'{Name(operation.Table)}'))"); writer.WriteLine("BEGIN"); writer.WriteLine("ALTER TABLE "); writer.WriteLine(Name(operation.Table)); writer.Write(" ADD "); var column = operation.ColumnModel; Generate(column, writer); if (column.IsNullable != null && !column.IsNullable.Value && (column.DefaultValue == null) && (string.IsNullOrWhiteSpace(column.DefaultValueSql)) && !column.IsIdentity && !column.IsTimestamp && !column.StoreType.EqualsIgnoreCase("rowversion") && !column.StoreType.EqualsIgnoreCase("timestamp")) { writer.Write(" DEFAULT "); if (column.Type == PrimitiveTypeKind.DateTime) { writer.Write(Generate(DateTime.Parse("1900-01-01 00:00:00", CultureInfo.InvariantCulture))); } else { writer.Write(Generate((dynamic)column.ClrDefaultValue)); } } writer.WriteLine("END"); Statement(writer); } } }
和一个扩展方法,为您提供"AddColumnIfNotExists"功能:
public static class MigrationExtensions { public static void AddColumnIfNotExists(this DbMigration migration, string table, string name, FunccolumnAction, object anonymousArguments = null) { ((IDbMigration)migration) .AddOperation(new AddColumnIfNotExistsOperation(table, name, columnAction, anonymousArguments)); } }
在EF迁移配置文件中,您需要注册自定义SQL生成器:
[ExcludeFromCodeCoverage] internal sealed class Configuration : DbMigrationsConfiguration{ public Configuration() { AutomaticMigrationsEnabled = false; // Register our custom generator SetSqlGenerator("System.Data.SqlClient", new AddColumnIfNotExistsSqlGenerator()); } }
然后你应该可以用它代替AddColum(注意这个关键字):
[ExcludeFromCodeCoverage] public partial class AddVersionAndChangeActivity : DbMigration { public override void Up() { this.AddColumnIfNotExists("dbo.Action", "VersionId", c => c.Guid(nullable: false)); AlterColumn("dbo.Action", "Activity", c => c.String(nullable: false, maxLength: 8000, unicode: false)); } public override void Down() { AlterColumn("dbo.Action", "Activity", c => c.String(nullable: false, maxLength: 50)); DropColumn("dbo.Action", "VersionId"); } }
当然,您需要对操作进行一些测试:
[TestClass] public class AddColumnIfNotExistsOperationTests { [TestMethod] public void Can_get_and_set_table_and_column_info() { Funcaction = c => c.Decimal(name: "T"); var addColumnOperation = new AddColumnIfNotExistsOperation("T", "C", action, null); Assert.AreEqual("T", addColumnOperation.Table); Assert.AreEqual("C", addColumnOperation.Name); } [TestMethod] public void Inverse_should_produce_drop_column_operation() { Func action = c => c.Decimal(name: "C", annotations: new Dictionary { { "A1", new AnnotationValues(null, "V1") } }); var addColumnOperation = new AddColumnIfNotExistsOperation("T", "C", action, null); var dropColumnOperation = (DropColumnOperation)addColumnOperation.Inverse; Assert.AreEqual("C", dropColumnOperation.Name); Assert.AreEqual("T", dropColumnOperation.Table); Assert.AreEqual("V1", ((AnnotationValues)dropColumnOperation.RemovedAnnotations["A1"]).NewValue); Assert.IsNull(((AnnotationValues)dropColumnOperation.RemovedAnnotations["A1"]).OldValue); } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void Ctor_should_validate_preconditions_tableName() { Func action = c => c.Decimal(name: "T"); // ReSharper disable once ObjectCreationAsStatement new AddColumnIfNotExistsOperation(null, "T", action, null); } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void Ctor_should_validate_preconditions_columnName() { Func action = c => c.Decimal(); // ReSharper disable once ObjectCreationAsStatement new AddColumnIfNotExistsOperation("T", null, action, null); } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void Ctor_should_validate_preconditions_columnAction() { // ReSharper disable once ObjectCreationAsStatement new AddColumnIfNotExistsOperation("T", "C", null, null); } }
并测试SQL Generator:
[TestClass] public class AddColumnIfNotExistsSqlGeneratorTests { [TestMethod] public void AddColumnIfNotExistsSqlGenerator_Generate_can_output_add_column_statement_for_GUID_and_uses_newid() { var migrationSqlGenerator = new AddColumnIfNotExistsSqlGenerator(); Funcaction = c => c.Guid(nullable: false, identity: true, name: "Bar"); var addColumnOperation = new AddColumnIfNotExistsOperation("Foo", "bar", action, null); var sql = string.Join(Environment.NewLine, migrationSqlGenerator.Generate(new[] {addColumnOperation}, "2005") .Select(s => s.Sql)); Assert.IsTrue(sql.Contains("IF NOT EXISTS(SELECT 1 FROM sys.columns")); Assert.IsTrue(sql.Contains("WHERE Name = N\'bar\' AND Object_ID = Object_ID(N\'[Foo]\'))")); Assert.IsTrue(sql.Contains("BEGIN")); Assert.IsTrue(sql.Contains("ALTER TABLE")); Assert.IsTrue(sql.Contains("[Foo]")); Assert.IsTrue(sql.Contains("ADD [bar] [uniqueidentifier] NOT NULL DEFAULT newsequentialid()END")); } }