2012-09-13 12 views
5

Tworzę aplikację internetową MVC4 przy użyciu Entity Framework 5 (najpierw baza z wygenerowanymi POCO) dla dostępu do danych.Jak z wdziękiem ponownie podłączyć strukturę POCO Entity Framework 5 i zapisać ją?

W aplikacji użytkownik przechodzi przez kilka ekranów, tworząc lub edytując dokument (zwany "case study"). Kiedy dotrą do ostatniego ekranu, ich dokument istnieje w pamięci podręcznej CaseStudy POCO i wszystko jest wspaniałe, dopóki nie nadejdzie czas na zapisanie tej struktury do bazy danych.

Aby zapisać dokument, zdefiniowałem kilka tabel bazy danych, które z kolei są mapowane na EF POCO używane przez warstwę biznesową, które następnie są używane przez kontrolery MVC. Jako takie, krótkotrwałe DbContexts są używane do pobierania POCO i przechowywania ich w sesji pomiędzy żądaniami.

W rezultacie ekran zapisywania musi zapisać zawartość tego POCO, która ma właściwości nawigacyjne, do istniejących danych tabel (Tabele kategorii, układu i przekrojów), a także dodać lub zaktualizować dane (CaseStudySections i sam CaseStudy). Wszystkie POCO są nowe lub kontekst używany do ich odzyskiwania już dawno został usunięty. Innymi słowy, wszystkie one są "oderwane".

To, co niezwykłe w tym poście, to że mam już działające rozwiązanie w zasięgu ręki. Problem polega na tym, że jest on nieporęczny, łamliwy i nieelegancki. Zamieszczam poniższy kod. Zwróć uwagę na iterację poprzez sub-kolekcje, wyraźne dodawanie i dołączanie, konieczność uzyskania obiektu wejściowego i oznaczenia poszczególnych właściwości jako zmodyfikowanych, aby były one aktualizowane, a także straszna piosenka i taniec na końcu, aby zsynchronizować kolekcję AdditionalMaterials. Jeśli jest to wymagane, aby poradzić sobie z odłączonymi POCO w EF5, będę rozczarowany.

Czy tu czegoś brakuje? Czy jest to zgodne z najlepszymi praktykami? Czy istnieje bardziej elegancki i/lub zwięzły sposób dołączania struktury POCO i wstawiania/aktualizowania?

Kod zapisać Studium przypadku:

public void SaveCaseStudy(CaseStudy caseStudy) 
{ 
    foreach (var s in caseStudy.CaseStudySections) 
    { 
     this.Entities.Sections.Attach(s.Section); 

     if (s.CreatedByRefId == default(Guid)) 
     { 
      s.CreatedByRefId = this.UserRefId; 
      s.CreatedTime = DateTime.Now; 
      this.Entities.CaseStudySections.Add(s); 
     } 
     else 
     { 
      this.Entities.CaseStudySections.Attach(s); 
      var entry = this.Entities.Entry(s); 
      entry.Property(e => e.TextData).IsModified = true; 
      entry.Property(e => e.BinaryData).IsModified = true; 
     } 

     s.LastModifiedByRefId = this.UserRefId; 
     s.LastModifiedTime = DateTime.Now; 
    } 

    foreach (var m in caseStudy.AdditionalMaterials) 
    { 
     if (m.CreatedByRefId == default(Guid)) 
     { 
      m.CreatedByRefId = this.UserRefId; 
      m.CreatedTime = DateTime.Now; 
      this.Entities.AdditionalMaterials.Add(m); 
     } 
     else 
     { 
      this.Entities.AdditionalMaterials.Attach(m); 
     } 

     m.LastModifiedByRefId = this.UserRefId; 
     m.LastModifiedByTime = DateTime.Now; 
    } 

    this.Entities.Layouts.Attach(caseStudy.Layout); 
    this.Entities.Categories.Attach(caseStudy.Category); 

    if (caseStudy.CreatedByRefId != default(Guid)) 
    { 
     this.Entities.CaseStudies.Attach(caseStudy); 
     var entry = this.Entities.Entry(caseStudy); 
     entry.Property(e => e.CaseStudyName).IsModified = true; 
     entry.Property(e => e.CaseStudyTitle).IsModified = true; 
    } 
    else 
    { 
     this.Entities.CaseStudies.Add(caseStudy); 
     caseStudy.CreatedByRefId = this.UserRefId; 
     caseStudy.CreatedTime = DateTime.Now; 
    } 

    caseStudy.LastModifiedByRefId = this.UserRefId; 
    caseStudy.LastModifiedTime = DateTime.Now; 

    if (caseStudy.CaseStudyStatus != (int)CaseStudyStatus.Personalized) 
    { 
     caseStudy.CaseStudyStatus = (int)CaseStudyStatus.PendingApproval; 
    } 

    caseStudy.ApprovedByRefId = null; 
    caseStudy.ApprovedTime = null; 
    this.Entities.SaveChanges(); 

    var existingAdditionalMaterialRefIds = caseStudy.AdditionalMaterials 
     .Select(m => m.AdditionalMaterialRefId) 
     .ToArray(); 

    var additionalMaterialsToRemove = this.Entities.AdditionalMaterials 
     .Where(m => 
      m.CaseStudyRefId == caseStudy.CaseStudyRefId && 
      !existingAdditionalMaterialRefIds.Contains(m.AdditionalMaterialRefId)) 
     .ToArray(); 

    foreach (var additionalMaterialToRemove in additionalMaterialsToRemove) 
    { 
     this.Entities.AdditionalMaterials.Remove(additionalMaterialToRemove); 
    } 

    this.Entities.SaveChanges(); 
} 

Odpowiedz

3

W ogóle to jest to, co masz do zrobienia. Musisz powiedzieć EF o każdej zmianie, którą chcesz wykonać podczas dołączania odwzorowanego obiektu. Nie mówię, że twój kod nie może być uproszczony, ale nadal będziesz musiał zająć się każdym podmiotem i ustawić jego stan, jeśli chcesz go dodać lub zmodyfikować.

Here jest nieco starszą, ale wciąż aktualną odpowiedzią na dany temat - mówiąc krótko nic nie zmieniło się od czasu, gdy ją napisałem, tylko nowy interfejs API DbContext, który wciąż znajduje się na starym API. Najlepszy opis tego tematu, jaki widziałem do tej pory, znajduje się w książce Programming Entity Framework: DbContext.

2

Jak o właśnie robi:

db.CaseStudies.Attach(caseStudy); 
db.Entry(caseStudy).State = EntityState.Modified; 
db.SaveChange(); 

To będzie zapisać wszystkie zmiany w modelu do DB.

+0

To nie działa, niestety. W naszym przypadku jest to wyjątek DbUpdateConcurrencyException: 'Store update, insert lub delete dotyczy nieoczekiwanej liczby wierszy (0). Elementy mogły zostać zmodyfikowane lub usunięte od momentu załadowania jednostek. Odśwież wpisy w ObjectStateManager. Jest tak prawdopodobnie dlatego, że caseStudy składa się z obiektów danych, w których niektóre już istnieją w bazie danych (a więc najwyraźniej muszą być wyraźnie ponownie dołączone), a niektóre są nowe i trzeba je wstawić. –

Powiązane problemy