web-dev-qa-db-de.com

DateTime-Feld kann standardmäßig auf GETDATE () mit Entity Framework-Migrationen gesetzt werden?

Ich habe EntityFramework.Migrations (Beta 1) zu einer vorhandenen Code-First-App hinzugefügt, die einige Änderungen durchläuft (sowohl für die Migrationsfähigkeiten als auch für die Feinabstimmung der Tabellen, die ich mit meiner Code-First-API generiere) und auf GETDATE lief () Szenario. 

Ich habe bereits eine benutzerdefinierte Initialisierungsklasse in meinem DbContext verwendet, um SQL-Skripts auszuführen, um einige Felder festzulegen und Indizes in meiner Datenbank zu erstellen. Ein paar meiner AlterTable-Skripts dienen hauptsächlich dazu, Felder mit Standardwerten einzurichten (z. B. bestimmte DateTime-Felder, die auf GETDATE () gesetzt sind). Ich hatte wirklich gehofft, dass EntityFramework.Migrations eine Antwort darauf geben würde, da Sie defaultValue problemlos angeben können, aber bisher sehe ich keinen.

Irgendwelche Ideen? Ich hatte wirklich gehofft, dass das Folgende magisch funktionieren würde. (Es ist immerhin 'magisches Einhorn')

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

Leider und logischerweise setzte es meinen Standardwert auf den Zeitpunkt, zu dem der Befehl Update-Database ausgeführt wurde.

32
adammokan

Sie müssen ein benutzerdefiniertes SQL-Skript in der Up-Methode verwenden, um den Standardwert festzulegen:

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

Das Festlegen des Standardwerts im Code lässt nur statische Werte zu - keine Funktionen auf Datenbankebene. 

Die Einstellung im POCO-Konstruktor ist auf jeden Fall richtig, wenn Sie zuerst Code verwenden. Wenn Sie für bestimmte Fälle den Wert in der Anwendung festlegen möchten, können Sie keinen Standardwert in der Datenbank verwenden, da für den Standardwert in der Datenbank entweder DatabaseGeneratedOption.Identity oder DatabaseGeneratedOption.Computed erforderlich ist. Bei beiden Optionen kann die Eigenschaft nur in der Datenbank festgelegt werden.

Bearbeiten:

Da sich das Produkt noch in der Entwicklung befindet, ist meine Antwort nicht mehr gültig. Überprüfen Sie die Antwort von @gius auf tatsächliche Weise, um diese Anforderung zu erreichen, indem Sie defaultValueSql verwenden (diese Option war in EF Migrations Beta 1 nicht verfügbar, wurde jedoch in EF 4.3 Beta 1 hinzugefügt, in dem bereits Migrationen enthalten sind).

14
Ladislav Mrnka

Sie können verwenden

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

Verwendungszweck:

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

Update 2012-10-10:

Wie von Thiago in seinem Kommentar gefordert, füge ich noch einen zusätzlichen Kontext hinzu.

Der obige Code ist eine Migrationsdatei, die von EF Migrations generiert wird, indem Add-Migration MyMigration als Befehl in der Paketmanagerkonsole ausgeführt wird. Der generierte Code basiert auf den Modellen im DbContext, die den Migrationen zugeordnet sind. In der Antwort wird vorgeschlagen, dass Sie das generierte Skript so ändern, dass beim Erstellen der Datenbank ein Standardwert hinzugefügt wird.

Sie können mehr über Entity Framework Code First Migrations hier lesen.

87
gius

Ich bin kürzlich in EF6 auf dieses Problem gestoßen (da es immer noch nicht behoben wurde). Die einfachste Methode, die ich gefunden habe, ohne die Migrationsklasse manuell ändern zu müssen, ist das Überschreiben des CodeGenerators in Ihrer Konfigurationsklasse.

Durch Erstellen einer Klasse, die MigrationCodeGenerator implementiert, und die Generate-Methode überschreiben, können Sie alle Vorgänge durchlaufen und die gewünschten Änderungen anwenden.

Wenn Sie Ihre Änderungen vorgenommen haben, können Sie den CSharpMigrationCodeGenerator initialisieren und seinen Standardwert zurückgeben.

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

Ich hoffe das hilft

23
JonnySchnittger

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

Bei vorhandenen Datensätzen wird die Datumszeit festgelegt, zu der Sie den Befehl Update-Database ausführen. Bei neuen Datensätzen wird die Datumszeit der Erstellung festgelegt

5
Nina

Wenn Ihre Entitäten von einer allgemeinen Schnittstelle erben, können Sie alternativ die SaveChanges-Methode für DbContext überschreiben und Eigenschaften an diesem Punkt festlegen oder aktualisieren (ideal für Erstellungsdatum und Datum der letzten Änderung).

1
Betty

Eine Verbesserung: Prüfen Sie, ob die Einschränkung vorhanden ist:

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

Dies ist der einfachste Weg.

Zuerst fügen Sie Ihrer Immobilie DatabaseGeneratedOption.ComputedDataAnnotion hinzu 

und jetzt können Sie de SqlServerMigrationSqlGenarator ändern, die Genarate-Methode überschreiben und die DefaultValueSql = "GETDATE()" or "GETUTCDATE()"; setzen.

0
Fesaro