web-dev-qa-db-fra.com

Possibilité de passer par défaut du champ DateTime à GETDATE () avec Migration Entity Framework?

J'ai ajouté EntityFramework.Migrations (Beta 1) à une application Code-First existante qui subit quelques modifications (tant pour les capacités de migration que pour le réglage plus précis des tables que je génère à partir de mon API code-first), puis dans GETDATE () scénario. 

J'utilisais déjà une classe d'initialiseur personnalisée dans mon DbContext pour exécuter des scripts SQL afin de définir des champs et de créer des index dans ma base de données. Quelques-uns de mes scripts AlterTable permettent principalement de configurer des champs avec des valeurs par défaut (certains champs DateTime étant définis sur GETDATE ()). J'espérais vraiment qu'EntityFramework.Migrations aurait une réponse à cela puisque vous pouvez facilement spécifier defaultValue, mais je n'en vois pas encore.

Des idées? J'espérais vraiment que ce qui suit fonctionnerait comme par magie. (C'est la "Licorne magique", après tout)

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

Malheureusement et logiquement, il a défini ma valeur par défaut sur l'heure d'exécution de la commande Update-Database.

32
adammokan

Vous devez utiliser un script SQL personnalisé dans la méthode Up pour définir la valeur par défaut:

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

La définition de la valeur par défaut dans le code n'autorise que les valeurs statiques - pas de fonctions de niveau base de données. 

Quoi qu’il en soit, le paramétrer dans le constructeur POCO est correct si vous utilisez d’abord le code. De même, si vous souhaitez définir la valeur dans l'application dans certains cas particuliers, vous ne pouvez pas utiliser une valeur par défaut dans la base de données, car celle-ci requiert DatabaseGeneratedOption.Identity ou DatabaseGeneratedOption.Computed. Ces deux options permettent de définir la propriété uniquement dans la base de données.

Modifier:

Comme le produit est encore en développement, ma réponse n’est plus valable. Vérifiez la réponse @gius pour connaître le moyen effectif de satisfaire à cette exigence en utilisant defaultValueSql (elle n'était pas disponible dans EF Migrations Beta 1 mais a été ajoutée dans EF 4.3 Beta 1, qui inclut déjà des migrations).

14
Ladislav Mrnka

Vous pouvez utiliser

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

Usage:

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

Mise à jour 2012-10-10:

Comme demandé par Thiago dans son commentaire, j'ajoute un petit contexte supplémentaire.

Le code ci-dessus est un fichier de migration généré par EF Migrations en exécutant Add-Migration MyMigration en tant que commande dans la console du gestionnaire de packages. Le code généré est basé sur les modèles de DbContext associés aux migrations. La réponse vous suggère de modifier le script généré de sorte qu'une valeur par défaut soit ajoutée lors de la création de la base de données.

Vous pouvez en savoir plus sur les premières migrations de Entity Framework Code ici .

87
gius

J'ai récemment rencontré ce problème dans EF6 (car ils ne l'ont toujours pas corrigé). Le moyen le plus simple que j'ai trouvé de le faire sans avoir à modifier manuellement la classe de migration est de remplacer le CodeGenerator dans votre classe de configuration.

En créant une classe qui implémente MigrationCodeGenerator, puis en redéfinissant la méthode Generate, vous pouvez parcourir toutes les opérations et appliquer les modifications souhaitées.

Une fois vos modifications effectuées, vous pouvez ensuite initialiser votre CSharpMigrationCodeGenerator et renvoyer sa valeur par défaut.

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

J'espère que ça aide

23
JonnySchnittger

Créer une migration:

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

Pour les enregistrements existants, il définira l'heure de la date à laquelle vous exécuterez la commande Update-Database. Pour les nouveaux enregistrements, il sera défini l'heure de la création.

5
Nina

Si vos entités héritent d'une interface commune, vous pouvez également remplacer la méthode SaveChanges sur DbContext et définir ou mettre à jour les propriétés à ce stade (idéal pour la date de création et la date de la dernière modification).

1
Betty

Une amélioration: vérifiez si la contrainte existe:

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");
0
Martin Staufcik

C'est le moyen le plus simple.

First Add DatabaseGeneratedOption.ComputedDataAnnotion à votre propriété 

et maintenant vous pouvez modifier de SqlServerMigrationSqlGenarator, substituer la méthode Genarate et définir la DefaultValueSql = "GETDATE()" or "GETUTCDATE()";

0
Fesaro