26

Próbuję dodać właściwość CreatedDate do elementów w moim modelu i używam najpierw kodu EF5. Chcę, aby ta data nie uległa zmianie po ustawieniu, chcę, aby była to data UTC. NIE chcę używać konstruktora, ponieważ mam wiele elementów w moim modelu, które chcę dziedziczyć po klasie abstrakcyjnej zawierającej właściwość CreatedDate i nie mogę wymusić konstruktora z interfejsem.Dodawanie obiektu CreatedDate do encji przy użyciu Entity Framework 5 Code First

Próbowałem różnych adnotacji danych i próbowałem napisać inicjator bazy danych, która będzie odebrać szczególnego rodzaju podmiotu i napisać alter ograniczenie o getdate() wartości domyślnej dla prawidłowego table_name i column_name, ale nie były w stanie napisać ten kod poprawnie.

Proszę nie odsyłać mnie do narzędzi AuditDbContext - Entity Framework Auditing Context lub EntityFramework.Extended, ponieważ nie robią tego, czego potrzebuję.

UPDATE

Moja CreatedDate jest zerowy na SaveChanges() bo jestem przechodzącej ViewModel do mojego widoku, który właściwie nie ma właściwość o nazwie CreatedDate audytu w nim. I nawet jeśli przekazałem model do mojego widoku, nie edytuję ani nie przechowuję w widoku obrazu CreatedDate.

czytam here, że mogę dodać [DatabaseGenerated(DatabaseGeneratedOption.Identity)] a to powie EF przechowywać CreatedDate poprawnie po Insert i Update, ale nie pozwolić mu zostać zmieniona przez mojego wniosku: ale ja po prostu błąd Cannot insert explicit value for identity column in table when IDENTITY_INSERT is set to OFF dodając ten atrybut .

Mam zamiar przejść do modelu EF Najpierw, ponieważ ten prosty wymóg bazy danych jest absurdalny do wdrożenia w Code First.

Odpowiedz

65

Oto jak to zrobiłem:

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

w moją migracja za() metoda:

AddColumn("Agents", "CreatedDate", n => n.DateTime(nullable: false, defaultValueSql: "GETUTCDATE()")); 
+0

Tylko jeśli nie przeszkadzają ci adnotacje na twoich zajęciach, zamiast tego użyłem odpowiedzi metody SaveChanges. – Maslow

+0

@Rusty Divine: Z jakiegoś powodu zawsze ustawia go na 01/01/1900 12:00:00 AM –

+4

Znalazłem lepszy sposób to zrobić tutaj: https://andy.mehalick.com/2014/02/06/ ef6-dodawanie-utworzonej-datetime-kolumny-automatycznie-z-pierwszym-migracją kodu – Dragouf

-1
Accounts account; 
account.Acct_JoinDate = DateTime.Now.ToUniversalTime(); 
data.Accounts.Add(account); 
data.SaveChanges(); 

Dlaczego nie dać znacznika czasu podczas tworzenia modelu? Podobne do tych kont tutaj.

+5

Chcę, aby funkcja audytu była zautomatyzowana i poza moimi kontrolerami. Nie chcę, aby inni deweloperzy musieli pamiętać o wejściu na stronę CreateDate za każdym razem, gdy robią Dodaj. –

25

przesłonić SaveChanges sposobie w kontekście:

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

} 

Updated ponieważ komentarzy: tylko świeżo dodane Podmioty będą mieli ustawić datę.

+0

Niesamowita rada, jednym z problemów jest to, że nie przekazuję wartości CreatedDate do mojego widoku. Przechodzę ViewModel do mojego widoku, a następnie za pomocą AutoMapper, aby odwzorować pola z mojego ViewModel do mojego widoku, tak więc InitialDay zawsze będzie null w Saving, czytam, że dodanie atrybut [DatabaseGenerated (DatabaseGeneratedOption.Identity)] do mojego InitialDate pozwoli EF, aby prawidłowo załadować dane z bazy danych, przeładować dane po wstawieniu lub zaktualizować, czy aktualność jest aktualna w aplikacji, a jednocześnie nie pozwoli na zmianę wartości, ale to tylko powoduje błąd –

+5

Nie jestem pewien, czy rozumiem cię poprawnie. Jest tu wieczór i być może piłem - przepraszam. Ale jeśli wartość CreatedDate nie jest dostępna dla kontekstu: a co dopiero z wyzwalaczem bazy danych. (Tak, zdecydowanie piłam, kiedy proponuję * wyzwalacze *) –

+0

Cóż, zwykle robiłbym spust, ale pracuję z EF5 Code First. Mogę dodać spust przez CodeFirst, ale to pokonuje podstawową zasadę CodeFirst, która ma na celu utrzymanie reguł biznesowych w jednym miejscu. Nie mogę uwierzyć, że nie ma sposobu, aby ustawić właściwość encji na "tylko do odczytu", że tak powiem, tak, że, czy obecny w kontekście, czy nie, nie aktualizuje wartości, chyba że ja wyraźnie to zrobię. –

4

OK, więc głównym problemem było to, że CreateDate był aktualizowany za każdym razem, gdy wywoływałem SaveChanges, a ponieważ nie przekazywałem CreateDate do moich widoków, był on aktualizowany do NULL lub MinDate przez Entity Framework.

Rozwiązanie było proste, wiedząc, że wystarczy ustawić wartość CreatedDate, gdy EntityState.Dodano, po prostu ustawiłem entity.CreatedDate.IsModified = false przed wykonaniem jakiejkolwiek pracy w moim nadpisaniu SaveChanges, w ten sposób zignorowałem zmiany z Aktualizacji a jeśli byłby to Add the CreatedDate zostałby ustawiony kilka linii później.

-1

Kod Najpierw nie jest dostępny mechanizm zapewniający domyślne wartości kolumn.

Trzeba będzie ręcznie zmodyfikować lub utworzyć klasę bazową do automatycznej aktualizacji CreatedDate

public abstract class MyBaseClass 
{ 
    public MyBaseClass() 
    { 
     CreatedDate = DateTime.Now; 
    } 
    public Datetime CreatedDate { get; set; } 
} 
+2

Ale co powiesz na aktualizację jednostki. Inicjowanie wartości CreatedDate do teraz w konstruktorze spowoduje zastąpienie oryginalnej aktualizacji CreateDate. Czyż nie? To doprowadza mnie do szału –

+1

@ Adolfo Najpierw uruchamia się konstruktor, następnie ustawia się domyślne wartości pól lokalnych, a dopiero potem EF wypchnie wartości db do obiektu encji w pamięci. Więc nie, dla istniejących jednostek, wartość db z CreateDate nie zostanie nadpisana przez 'DateTime.Now'. – Maarten

3

Podobny do Stephans's Answer, ale z Reflection i również ignoruje wszystkie aktualizacje użytkownika (zewnętrzne) Utworzono/zaktualizowano czasy. 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(); 
     }