2011-12-21 5 views
31

Dodałem EntityFramework.Migrations (Beta 1) do istniejącej aplikacji Code-First, która przechodzi pewne zmiany (zarówno w zakresie możliwości migracji, jak i dokładniejszego dostosowania tabel generowanych przez mój pierwszy API) i wpadła na scenariusz GETDATE().Możliwe do domyślnego pola DateTime do GETDATE() z Entity Framework Migrations?

Już używałem niestandardowej klasy inicjalizacyjnej w moim DbContext, aby uruchomić skrypty SQL, aby ustawić niektóre pola i utworzyć indeksy w mojej bazie danych. Garść moich skryptów AlterTable jest podstawowa tylko do ustawienia pól z wartościami domyślnymi (takimi jak pewne pola DateTime ustawione na GETDATE()). Naprawdę miałem nadzieję, że EntityFramework.Migrations będzie miał na to odpowiedź, ponieważ możesz łatwo określić defaultValue, ale jak dotąd nie widzę żadnej.

Wszelkie pomysły? Naprawdę miałem nadzieję, że wykonanie poniższych czynności będzie magicznie skuteczne. (To jest „magiczny jednorożec”, mimo wszystko)

DateCreated = c.DateTime(nullable: false, defaultValue: DateTime.Now) 

Niestety, i logicznie, to ustawić moją domyślną wartość do czasu, gdy komenda Update-Database został stracony.

+0

Nie szukam do dostania się do teorii krążących wokół”... za pomocą ORM nie powinieneś umieszczać logiki w bazie danych ... ". Rozumiem, że. Po prostu chcę się upewnić, że nikt nie odrzuci przykładu POCO z DateTime.Now w konstruktorze :) – adammokan

+0

po prostu miał podobne prawdopodobnie i znalazł dobre rozwiązanie. Mam nadzieję, że to pomoże: http://stackoverflow.com/questions/9830216/ef-4-3-1-migration-exception-altercolumn-defaultvaluesql-creates-same-default/ –

Odpowiedz

14

Musisz użyć niestandardowego skryptu SQL w Up metody ustalania wartości domyślne: Domyślna wartość

Sql("ALTER TABLE TableName ADD CONSTRAINT ConstraintName DEFAULT GETDATE() FOR ColumnName"); 

Ustawienie w kodzie pozwala jedynie wartości statyczne - bez funkcji poziomie bazy danych.

W każdym razie ustawienie go w konstruktorze POCO jest poprawne, jeśli zamierzasz najpierw użyć kodu. Również jeśli chcesz ustawić wartość w aplikacji dla niektórych szczególnych przypadków, nie możesz użyć wartości domyślnej w bazie danych, ponieważ domyślna wartość w bazie danych wymaga DatabaseGeneratedOption.Identity lub DatabaseGeneratedOption.Computed. Obie te opcje umożliwiają ustawienie właściwości tylko w bazie danych.

Edit:

Ponieważ produkt jest jeszcze w fazie rozwoju moja odpowiedź nie jest już ważna. Sprawdź odpowiedź @gius, aby poznać rzeczywisty sposób osiągnięcia tego wymogu, używając defaultValueSql (nie był dostępny w wersji EF Migrations Beta 1, ale został dodany w wersji EF 4.3 Beta 1, która obejmuje już migracje).

+0

Dzięki. Miałem już funkcjonalność SQL Script przed przeniesieniem do EF.Migrations, więc to naprawdę mi nie pomaga. – adammokan

+0

Oznaczam to jako odpowiedź. Nie odpowiedź, na którą liczyłem, ale pomyślałem, że tak właśnie jest. – adammokan

1

Alternatywnie, jeśli podmioty dziedziczyć ze wspólnego interfejsu można zastąpić metodę SaveChanges na DbContext i ustawić lub zmiana właściwości w tym momencie (świetny dla daty utworzenia i ostatniej zmiany data)

79

Można użyć

DateCreated = c.DateTime(nullable: false, defaultValueSql: "GETDATE()") 

Zastosowanie:

public partial class MyMigration : DbMigration 
{ 
    public override void Up() 
    { 
     CreateTable(
      "dbo.Users", 
      c => new 
       { 
        Created = c.DateTime(nullable: false, defaultValueSql: "GETDATE()"), 
       }) 
      .PrimaryKey(t => t.ID); 
... 

Aktualizacja 10.10.2012:

Zgodnie z prośbą Thiago w komentarzu, dodam trochę dodatkowego kontekstu.

Powyższy kod jest plikiem migracyjnym generowanym przez EF Migrations przez uruchomienie Add-Migration MyMigration jako polecenia w konsoli menedżera pakietów. Wygenerowany kod jest oparty na modelach w DbContext powiązanych z migracjami. Odpowiedź sugeruje, że modyfikujesz wygenerowany skrypt w pewien sposób, dzięki czemu wartość domyślna jest dodawana podczas tworzenia bazy danych.

Możesz przeczytać więcej o Entity Framework Code First Migrations here.

+1

gdzie ten kod idzie? jaka jest zmienna "c" ?? Czy możesz podać nieco więcej kontekstu wokół tej linii kodu? –

+1

@ThiagoSilva Dodałem trochę dodatkowego kontekstu do odpowiedzi, zgodnie z sugestią. –

19

Niedawno dotarłem do tego numeru EF6 (ponieważ nadal go nie naprawiłem). Najprostszym sposobem, w jaki udało mi się to zrobić bez konieczności ręcznej modyfikacji klasy migracji, jest zastąpienie CodeGeneratora w twojej klasie konfiguracji.

Tworząc klasę, która implementuje MigrationCodeGenerator, a następnie przesłonię metodę Generate, można wykonywać iteracje we wszystkich operacjach i stosować dowolne modyfikacje.

Po dokonaniu modyfikacji można zainicjować CSharpMigrationCodeGenerator i przywrócić jego domyślną wartość.

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) 
        if (column.ClrType == typeof(DateTime) && column.IsNullable.HasValue && !column.IsNullable.Value && string.IsNullOrEmpty(column.DefaultValueSql)) 
         column.DefaultValueSql = "GETDATE()"; 
      } 
      else if (operation is AddColumnOperation) 
      { 
       ColumnModel column = ((AddColumnOperation)operation).Column; 

       if (column.ClrType == typeof(DateTime) && column.IsNullable.HasValue && !column.IsNullable.Value && string.IsNullOrEmpty(column.DefaultValueSql)) 
        column.DefaultValueSql = "GETDATE()"; 
      } 
     } 

     CSharpMigrationCodeGenerator generator = new CSharpMigrationCodeGenerator(); 

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

internal sealed class Configuration : DbMigrationsConfiguration<Project.Models.Context.DatabaseContext> 
{ 
    public Configuration() 
    { 
     AutomaticMigrationsEnabled = false; 
     MigrationsDirectory = @"Migrations"; 
     this.CodeGenerator = new ExtendedMigrationCodeGenerator(); 
    } 
} 

Mam nadzieję, że to pomoże

+0

Dla EF6 działało to doskonale. niezłe. – tony

+1

Chciałbym móc przyznać ci nagrodę za doraźną nagrodę. Biorę istniejący projekt, który rozpoczął swoje życie jako projekt nHibernate, który następnie został zmieniony na EF6. Nie zrobili więcej, aby uzyskać wszystko na temat migracji z powodu ograniczeń czasowych, które zamierzam teraz zrobić. Umożliwiło to usunięcie kilku jednorazowych skryptów, które uruchomiono w ręcznie zwiniętym generatorze DB. Dziękuję Ci! –

0

Jest to najbardziej prosty sposób.

najpierw dodaćDatabaseGeneratedOption.ComputedDataAnnotion do nieruchomości

a teraz można modyfikować de SqlServerMigrationSqlGenarator, nadpisać metodę Genarate i ustawić DefaultValueSql = "GETDATE()" or "GETUTCDATE()";

+1

Ta odpowiedź powinna prawdopodobnie zawierać określony niestandardowy SqlServerMigrationSqlGenerator. Odpowiedź JonnySchnittgera jest szeroka i nie obejmuje obsługi adnotacji atrybutów atrybutu lub płynnych definicji konfiguracji. (Jeśli to zrobię, zamiast modyfikować migrację, zmienię ją) – JoeBrockhaus

0

poprawy: sprawdzić, czy ograniczenie istnieje:

Sql(@" 
if not exists (
    select * 
     from sys.all_columns c 
     join sys.tables t on t.object_id = c.object_id 
     join sys.schemas s on s.schema_id = t.schema_id 
     join sys.default_constraints d on c.default_object_id = d.object_id 
    where 
     d.name = 'DF_ThubOutputEmail_Created' 
) 
begin 
    ALTER TABLE dbo.ThubOutputEmails ADD CONSTRAINT DF_ThubOutputEmail_Created default getdate() for Created; 
end"); 
4

Utwórz migrację:

public partial class Table_Alter : DbMigration 
{ 
    public override void Up() 
    { 
     AddColumn("dbo.tableName", "columnName", 
      c => c.DateTime(nullable: false, defaultValueSql: "GETDATE()")); 
    } 

    public override void Down() 
    { 
     DropColumn("dbo.tableName", "columnName"); 
    } 
} 

Dla istniejących zapisów będzie ustawić datetime kiedy będzie uruchomić polecenie Update-Database dla nowych zapisów będzie ustawić datetime tworzenia

Powiązane problemy