45

Mam model, w moim projekcie, jak poniżej:EF migracja do zmiany typu danych kolumny

public class Model 
{ 
    public int Id { get; set; } 
    public long FromNo { get; set; } 
    public long ToNo { get; set; } 
    public string Content { get; set; } 
    public long TicketNo { get; set; } 
} 

Migracja jest poniżej

public override void Down() 
{ 
    AlterColumn("dbo.Received", "FromNo", c => c.Long(nullable: false)); 
    AlterColumn("dbo.Received", "ToNo", c => c.Long(nullable: false)); 
    AlterColumn("dbo.Received", "TicketNo", c => c.Long(nullable: false)); 
} 
public override void Up() 
{ 
    AlterColumn("dbo.Received", "FromNo", c => c.String()); 
    AlterColumn("dbo.Received", "ToNo", c => c.String()); 
    AlterColumn("dbo.Received", "TicketNo", c => c.String()); 
} 

kiedy używać update-Database błąd poniżej podniesiono:

The object 'DF__Receiv__FromN__25869641' is dependent on column 'FromNo'. ALTER TABLE ALTER COLUMN FromNo failed because one or more objects access this column.

W tabelach nie ma klucza obcego lub co innego, na czym polega problem?

Odpowiedz

70

Masz domyślne ograniczenie dla swojej kolumny. Najpierw należy usunąć ograniczenie, a następnie zmienić kolumnę.

public override void Up() 
{ 
    Sql("ALTER TABLE dbo.Received DROP CONSTRAINT DF_Receiv_FromN__25869641"); 
    AlterColumn("dbo.Received", "FromNo", c => c.String()); 
    AlterColumn("dbo.Received", "ToNo", c => c.String()); 
    AlterColumn("dbo.Received", "TicketNo", c => c.String()); 
} 

Prawdopodobnie będziesz musiał usunąć domyślne ograniczenia również w innych kolumnach.

Właśnie widziałem komentarz Andrey (wiem - bardzo późno) i ma rację. Więc bardziej solidne podejście byłoby użyć coś takiego:

DECLARE @con nvarchar(128) 
SELECT @con = name 
FROM sys.default_constraints 
WHERE parent_object_id = object_id('dbo.Received') 
AND col_name(parent_object_id, parent_column_id) = 'FromNo'; 
IF @con IS NOT NULL 
    EXECUTE('ALTER TABLE [dbo].[Received] DROP CONSTRAINT ' + @con) 

wiem, że to prawdopodobnie nie pomaga OP, ale mam nadzieję, że to pomaga, że ​​ktoś natknie tej kwestii.

+20

Nazwa ograniczeniem jest wygenerowaną automatycznie przez SQL Serwer. Chociaż ten kod będzie działał w środowisku programistycznym - nie będzie w produkcji, ponieważ w innej bazie danych nazwa ograniczenia będzie inna – Andrey

+1

Bardzo ładne podejście. Specjalnie drugi. Działa świetnie. – Saket

+1

@Bigfellahull Przykro mi, że jestem kiepskim dowcipniakiem - gdzie mógłbyś umieścić drugą wersję swojej odpowiedzi? –

36
static internal class MigrationExtensions 
{ 
    public static void DeleteDefaultContraint(this IDbMigration migration, string tableName, string colName, bool suppressTransaction = false) 
    { 
     var sql = new SqlOperation(String.Format(@"DECLARE @SQL varchar(1000) 
     SET @SQL='ALTER TABLE {0} DROP CONSTRAINT ['+(SELECT name 
     FROM sys.default_constraints 
     WHERE parent_object_id = object_id('{0}') 
     AND col_name(parent_object_id, parent_column_id) = '{1}')+']'; 
     PRINT @SQL; 
     EXEC(@SQL);", tableName, colName)) { SuppressTransaction = suppressTransaction }; 
     migration.AddOperation(sql); 
    } 
} 

public override void Up() 
{ 
    this.DeleteDefaultContraint("dbo.Received", "FromNo"); 
    AlterColumn("dbo.Received", "FromNo", c => c.String()); 
    this.DeleteDefaultContraint("dbo.Received", "ToNo"); 
    AlterColumn("dbo.Received", "ToNo", c => c.String()); 
    this.DeleteDefaultContraint("dbo.Received", "TicketNo"); 
    AlterColumn("dbo.Received", "TicketNo", c => c.String()); 
} 
+2

To działa idealnie, lepiej niż akceptowana odpowiedź, ponieważ jest dynamiczne i niezmodyfikowane. – Jaanus

+1

Próbowałem tego i otrzymałem błąd: Nie można znaleźć żadnych zasobów odpowiednich dla określonej kultury lub neutralnej kultury. Upewnij się, że "XXXX2.DAL.Migrations.ChangeProcessedToByte.resources" zostało poprawnie osadzone lub połączone w zestawie "XXXX2.DAL" podczas kompilacji lub że wszystkie wymagane zestawy satelitów są ładowalne i w pełni podpisane. – Ian

+2

@Kleky Miałem ten problem i rozwiązałem go, umieszczając klasę MigrationExtensions w innym pliku do migracji (w moim przypadku MigrationExtensions.cs w folderze Migrations). –

2

Jest to przykład zmiany istniejącej kolumny na "nie null", która już ma ograniczenie klucza obcego. Nazwa kolumny to "FKColumnName" w tabeli "SubTable" i odnosi się do kolumny "Id" w tabeli "MainTable".

Up scenariusz:

Po kolumna wykonana jest „pustych” indeks i klucz obcy najpierw spadła, a następnie ponownie utworzona.

dół scenariusz:

Oto kroki są identyczne, z wyjątkiem, że kolumna jest wykonana ponownie pustych.

public partial class NameOfMigration : DbMigration 
{ 
    public override void Up() 
    { 
     DropForeignKey("dbo.SubTable", "FKColumnName", "dbo.MainTable"); 
     DropIndex("dbo.SubTable", new[] { "FKColumnName" }); 

     AlterColumn("dbo.SubTable", "FKColumnName", c => c.Int(nullable: false)); 

     CreateIndex("dbo.SubTable", "FKColumnName"); 
     AddForeignKey("dbo.SubTable", "FKColumnName", "dbo.MainTable", "Id"); 
    } 

    public override void Down() 
    { 
     DropForeignKey("dbo.SubTable", "FKColumnName", "dbo.MainTable"); 
     DropIndex("dbo.SubTable", new[] { "FKColumnName" }); 

     AlterColumn("dbo.SubTable", "FKColumnName", c => c.Int(nullable: true)); 

     CreateIndex("dbo.SubTable", "FKColumnName"); 
     AddForeignKey("dbo.SubTable", "FKColumnName", "dbo.MainTable", "Id"); 
    } 
} 
+0

To działało dla mnie, z wyjątkiem tego, że zmieniono '' new [] {"FKColumnName"} 'na' "IX_ColumnName" dla indeksu, ponieważ to była nazwa tego w mojej bazie danych. – styfle

-9

Jeśli używasz EF

  • Usuń folder migracji i bazy danych
  • enable-migrations
  • add-migration initial
  • update-database

Chociaż to rozwiązanie usunie wszystkie bieżące elementy z bazy danych. Jeśli to nie jest Twoja intencja, proponuję jedną z pozostałych odpowiedzi.

+2

To jest okropny pomysł. O ile twój projekt nie jest nowy. Nie słuchaj tej osoby. – jebar8

2

Lepszym sposobem jest rozwiązanie problemu na zawsze.

można zaimplementować niestandardową klasę sql generator pochodzący z SqlServerMigrationSqlGenerator od nazw System.Data.Entity.SqlServer:

using System.Data.Entity.Migrations.Model; 
using System.Data.Entity.SqlServer; 

namespace System.Data.Entity.Migrations.Sql{ 
    internal class FixedSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator { 
     protected override void Generate(AlterColumnOperation alterColumnOperation){ 
      ColumnModel column = alterColumnOperation.Column; 
      var sql = String.Format(@"DECLARE @ConstraintName varchar(1000); 
      DECLARE @sql varchar(1000); 
      SELECT @ConstraintName = name FROM sys.default_constraints 
       WHERE parent_object_id = object_id('{0}') 
       AND col_name(parent_object_id, parent_column_id) = '{1}'; 
      IF(@ConstraintName is NOT Null) 
       BEGIN 
       set @sql='ALTER TABLE {0} DROP CONSTRAINT [' + @ConstraintName+ ']'; 
      exec(@sql); 
      END", alterColumnOperation.Table, column.Name); 
       this.Statement(sql); 
      base.Generate(alterColumnOperation); 
      return; 
     } 
     protected override void Generate(DropColumnOperation dropColumnOperation){ 
      var sql = String.Format(@"DECLARE @SQL varchar(1000) 
       SET @SQL='ALTER TABLE {0} DROP CONSTRAINT [' + (SELECT name 
        FROM sys.default_constraints 
        WHERE parent_object_id = object_id('{0}') 
        AND col_name(parent_object_id, parent_column_id) = '{1}') + ']'; 
      PRINT @SQL; 
       EXEC(@SQL); ", dropColumnOperation.Table, dropColumnOperation.Name); 

        this.Statement(sql); 
      base.Generate(dropColumnOperation); 
     } 
    } 
} 

i ustawić tę konfigurację:

internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext> 
{ 
    public Configuration() 
    { 
     AutomaticMigrationsEnabled = true; 

     SetSqlGenerator("System.Data.SqlClient", new FixedSqlServerMigrationSqlGenerator()); 
    } 
    ... 
}