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