web-dev-qa-db-fra.com

Ajout de CreatedDate à une entité à l'aide de Entity Framework 5 Code First

J'essaie d'ajouter une propriété CreatedDate aux entités de mon modèle et j'utilise EF5 Code First. Je veux que cette date ne soit pas changée une fois définie, je veux que ce soit une date UTC. Je ne veux PAS utiliser de constructeur, car j'ai dans mon modèle de nombreuses entités que je veux hériter d'une classe abstraite contenant la propriété CreatedDate et je ne peux pas imposer un constructeur avec une interface.

J'ai essayé différentes annotations de données et j'ai essayé d'écrire un initialiseur de base de données qui capturerait un type d'entité spécifique et écrirait une contrainte alter avec une valeur par défaut getdate() pour les corrects table_name et column_name, mais je n'ai pas pu écrire ce code. correctement.

Ne me référez pas aux outils AuditDbContext - Contexte d’audit de Entity Framework ou EntityFramework.Extended , car ils ne font pas ce dont j’ai besoin ici.

METTRE À JOUR

Ma CreatedDate est null sur SaveChanges() car je passe un ViewModel à ma vue, qui ne contient correctement aucune propriété d'audit appelée CreatedDate. Et même si je transmettais le modèle à ma vue, je ne suis pas en train d’éditer ou de stocker la variable CreatedDate dans la vue.

J'ai lu ici que je pouvais ajouter la [DatabaseGenerated(DatabaseGeneratedOption.Identity)] et que cela indiquerait à EF de stocker correctement la CreatedDate après Insertion et Mise à jour, mais je ne permettais pas qu'elle soit modifiée par mon application: mais j'obtiens simplement une erreur Cannot insert explicit value for identity column in table when IDENTITY_INSERT is set to OFF en ajoutant cet attribut.

Je suis sur le point de passer à EF Model First car cette simple exigence de base de données est ridicule à implémenter dans Code First.

29
Brian Ogden

Voici comment je l'ai fait:

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreatedDate{ get; set; }

dans la méthode Up () de ma migration:

AddColumn("Agents", "CreatedDate", n => n.DateTime(nullable: false, defaultValueSql: "GETUTCDATE()"));
67
Rusty Divine

Remplacez la méthode SaveChanges dans votre contexte:

public override int SaveChanges()
{
  DateTime saveTime = DateTime.UtcNow;
  foreach (var entry in this.ChangeTracker.Entries().Where(e => e.State == (EntityState) System.Data.EntityState.Added))
   {
     if (entry.Property("CreatedDate").CurrentValue == null)
       entry.Property("CreatedDate").CurrentValue = saveTime;
    }
    return base.SaveChanges();

}

Mis à jour à cause de commentaires : seules les entités nouvellement ajoutées auront leur date définie.

28
Stephan Keller

Similaire à Réponse de Stephans mais avec Reflection et ignore également toutes les mises à jour d'utilisateurs (externes) créées/mises à jour. Show Gist

  public override int SaveChanges()
        {
            foreach (var entry in ChangeTracker.Entries().Where(x => x.Entity.GetType().GetProperty("CreatedTime") != null))
            {
                if (entry.State == EntityState.Added)
                {
                    entry.Property("CreatedTime").CurrentValue = DateTime.Now;
                }
                else if (entry.State == EntityState.Modified)
                {
                    // Ignore the CreatedTime updates on Modified entities. 
                    entry.Property("CreatedTime").IsModified = false;
                }

                // Always set UpdatedTime. Assuming all entities having CreatedTime property
                // Also have UpdatedTime
                // entry.Property("UpdatedTime").CurrentValue = DateTime.Now;
                // I moved this part to another foreach loop
            }

            foreach (var entry in ChangeTracker.Entries().Where(
                e => 
                    e.Entity.GetType().GetProperty("UpdatedTime") != null && 
                    e.State == EntityState.Modified || 
                    e.State == EntityState.Added))
            {
                entry.Property("UpdatedTime").CurrentValue = DateTime.Now;
            }

            return base.SaveChanges();
        }
4
guneysus

Ok, le principal problème ici était que CreatedDate était mis à jour chaque fois que j'appelais SaveChanges et, comme je ne transmettais pas CreatedDate à mes vues, il était mis à jour vers NULL ou MinDate par Entity Framework.

La solution était simple, sachant que je n'ai besoin de définir que CreatedDate lorsque EntityState.Added, je viens de définir mon entity.CreatedDate.IsModified = false avant d'effectuer tout travail dans mon remplacement SaveChanges, de cette façon j'ai ignoré les modifications des mises à jour et si elle était Ajouter le CreatedDate sera défini quelques lignes plus tard.

4
Brian Ogden

Pour EF Core, vous trouverez la solution recommandée par MS ici: Valeurs par défaut .

Utilisez l'API Fluent dans votre DBContext:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Created)
        .HasDefaultValueSql("getdate()");
}
1
Laobu

Code First ne fournit actuellement pas de mécanisme pour fournir les valeurs par défaut des colonnes.

Vous devrez modifier ou créer manuellement la classe de base pour la mise à jour automatique CreatedDate

public abstract class MyBaseClass
{
    public MyBaseClass()
    {
        CreatedDate = DateTime.Now;
    }
    public Datetime CreatedDate { get; set; }
}
1
Aiska Hendra