2011-12-21 22 views
31

Agregué EntityFramework.Migrations (Beta 1) a una aplicación Code-First existente que está pasando por algunos cambios (tanto para la capacidad de migración como para un ajuste más preciso de las tablas que estoy generando a partir de mi API de primer código) y me encontré el escenario GETDATE().Posible el campo DateTime predeterminado para GETDATE() con Entity Framework Migrations?

Ya estaba usando una clase de inicializador personalizada en mi DbContext para ejecutar scripts SQL para establecer algunos campos y crear índices en mi base de datos. Un puñado de mis scripts AlterTable son primordiales solo para configurar campos con valores predeterminados (como ciertos campos DateTime que se configuran en GETDATE()). Realmente esperaba EntityFramework.Migrations tendría una respuesta para esto ya que puedes especificar fácilmente DefaultValue, pero hasta ahora no estoy viendo uno.

¿Alguna idea? Realmente estaba esperando que haciendo lo siguiente funcionaría mágicamente. (Es 'unicornio mágico', después de todo)

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

Por desgracia, y lógicamente, se establece el valor de mi defecto a la hora en que se ejecutó el comando Update-Database.

+0

No estoy buscando en conseguir en las teorías que giran alrededor' ... usando un ORM, no deberías poner lógica en la base de datos ... '. Entiendo que. Solo quiero asegurarme de que nadie arroje un ejemplo de POCO con DateTime.Now en el constructor :) – adammokan

+0

tiene probablemente un similar y encontró una buena solución. Espero que esto ayude: http://stackoverflow.com/questions/9830216/ef-4-3-1-migration-exception-altercolumn-defaultvaluesql-creates-same-default/ –

Respuesta

14

debe utilizar secuencia de comandos SQL personalizada en Up método para establecer el valor por defecto:

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

selección Valor por defecto en el código sólo permite valores estáticos - no hay funciones a nivel de base de datos.

De todos modos configurarlo en el constructor POCO es la forma correcta si va a utilizar el código primero. Además, si desea establecer el valor en la aplicación para algunos casos especiales, no puede usar un valor predeterminado en la base de datos porque el valor predeterminado en la base de datos requiere DatabaseGeneratedOption.Identity o DatabaseGeneratedOption.Computed. Ambas opciones permiten establecer la propiedad solo en la base de datos.

Editar:

Dado que el producto está todavía en desarrollo mi respuesta ya no es válida. Verifique la respuesta de @gius para obtener este requisito de manera efectiva utilizando defaultValueSql (no estaba disponible en EF Migrations Beta 1, pero se agregó en EF 4.3 Beta 1, que ya incluye migraciones).

+0

Gracias. Ya tenía la funcionalidad SQL Script en su lugar antes de pasar a EF.Migrations, así que esto realmente no me sirve de nada. – adammokan

+0

Marqué esto como la respuesta. No era la respuesta que esperaba, pero pensé que sería el caso. – adammokan

1

Alternativamente si sus entidades heredan de una interfaz común puede reemplazar el método SaveChanges en el DbContext y establecer o propiedades en ese punto (ideal para Fecha de Creación y los últimos modificados Fecha) actualización

79

Usted puede utilizar

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

Uso:

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

actualización 10/10/2012:

Según lo solicitado por Thiago en su comentario, agrego un poco de contexto adicional.

El código anterior es un archivo de migración generado por EF Migrations ejecutando Add-Migration MyMigration como un comando en la consola del administrador de paquetes. El código generado se basa en los modelos en el DbContext asociado con migraciones. La respuesta sugiere que modifique algo el script generado, de modo que se agregue un valor predeterminado cuando se crea la base de datos.

Puede leer más sobre Entity Framework Code First Migrations here.

+1

¿A dónde va este código? ¿Cuál es la variable "c"? ¿Puedes proporcionar un poco más de contexto en torno a esta línea de código? –

+1

@ThiagoSilva Agregué un poco de contexto adicional a la respuesta, como sugirió. –

19

Recientemente me encontré con este problema en EF6 (ya que todavía no lo han solucionado). La manera más fácil que encontré para hacerlo sin tener que modificar manualmente la clase de Migración es anulando el CodeGenerator en su clase de Configuración.

Al crear una clase que implemente MigrationCodeGenerator y luego anule el método Generate puede iterar a través de todas las operaciones y aplicar las modificaciones que desee.

Una vez que se hayan realizado las modificaciones, puede inicializar su CSharpMigrationCodeGenerator y devolver su valor predeterminado.

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

espero que esto ayude a

+0

Para EF6, funcionó perfectamente. Buena esa. – tony

+1

Desearía poder recompensarte con una recompensa ad-hoc por esto. Estoy tomando un proyecto existente que comenzó su vida como un proyecto nHibernate que alguien cambió a EF6. No hicieron un esfuerzo adicional para obtener todo en las migraciones debido a las limitaciones de tiempo, que tengo la intención de hacer ahora. Esto permitió eliminar varios scripts únicos que ejecutaban en su generador de bases de datos manuales. ¡Gracias! –

0

Ésta es la forma más sencilla.

primero AñadirDatabaseGeneratedOption.ComputedDataAnnotion a su propiedad

y ahora se puede modificar de SqlServerMigrationSqlGenarator, reemplazar el método Genarate y establecer el DefaultValueSql = "GETDATE()" or "GETUTCDATE()";

+1

Esta respuesta probablemente debería incluir el SqlServerMigrationSqlGenerator personalizado específico. La respuesta de JonnySchnittger es un pincel amplio y no incluye soporte para anotaciones de atributos por propiedad o definiciones de configuración fluidas. (Si termino implementando esto en lugar de modificar la migración, la editaré) – JoeBrockhaus

0

Una mejora: comprobar si existe la restricción:

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

Crear una migración:

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

Para los registros existentes que se ajustará la fecha y hora cuando se va a ejecutar comandos Update-Database, para los nuevos registros que se establecerá la fecha y hora de la creación

Cuestiones relacionadas