Używam Entity Framework 4.3.1 w projekcie, używając najpierw kodu i interfejsu API DbContext. Moja aplikacja jest aplikacją n-warstwową, w której odłączone obiekty mogą pochodzić od klienta. Używam programu SQL Server 2008 R2, ale wkrótce przejdę do SQL Azure. Mam problem, którego nie mogę rozwiązać.Zapisywanie pojedynczych obiektów przy użyciu kodu Entity Framework najpierw
Wyobraźmy mam kilka klas:
class A {
// Random stuff here
}
class B {
// Random stuff here
public A MyA { get; set; }
}
class C {
// Random stuff here
public A MyA { get; set; }
}
Domyślnie EF działa na wykresach obiektów. Na przykład, jeśli mam instancję B, która hermetyzuje instancję A i ja wywołuję myDbSet.Add(myB);
, będzie również oznaczać instancję A jako dodawaną (zakładając, że nie jest jeszcze śledzona).
Mam scenariusz w mojej aplikacji, gdzie muszę jasno określić, które obiekty zostaną utrwalone w bazie danych, zamiast śledzić całe wykresy obiektów. Kolejność czynności jest następująca:
A myA = new A(); // Represents something already in DB that doesn't need to be udpated.
C myC = new C() { // Represents something already in DB that DOES need to be updated.
A = myA;
}
B myB0 = new B() { // Not yet in DB.
A = myA;
}
B myB1 = new B() { // Not yet in DB.
A = myA;
}
myDbSetC.Attach(myC);
context.Entry(myC).State = Modified;
myDbSetB.Add(myB0); // Tries to track myA with a state of Added
myDbSetB.Add(myB1);
context.SaveChanges();
W tym momencie pojawia się błąd mówiąc AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges.
wierzę, że to się dzieje, ponieważ wywołanie dodać na myB0 oznacza wystąpienie jako dodanej, który jest niezgodny z instancji A już śledzony.
Idealnie mógłbym zrobić coś jak zadzwonić myDbSet.AddOnly(myB)
, ale oczywiście nie mamy tej opcji.
Próbowałem kilka obejścia:
Próba nr 1: Pierwsze starałem stworzenie metody pomocnika, aby zapobiec Mya od dodawanych po raz drugi.
private void MarkGraphAsUnchanged<TEntity>(TEntity entity) where TEntity : class {
DbEntityEntry entryForThis = this.context.Entry<TEntity>(entity);
IEnumerable<DbEntityEntry> entriesItWantsToChange = this.context.ChangeTracker.Entries().Distinct();
foreach (DbEntityEntry entry in entriesItWantsToChange) {
if (!entryForThis.Equals(entry)) {
entry.State = System.Data.EntityState.Unchanged;
}
}
}
...
myDbSetB.Add(myB0);
MarkGraphAsUnchanged(myB0);
Podczas gdy rozwiązuje to problem z próbą dodania myA, nadal powoduje naruszenia kluczy w ObjectStateManager.
Próba nr 2: Próbowałem robić to samo co powyżej, ale ustawienie stanu do indywidualnego zamiast niezmienione. Działa to w celu zapisania, ale nalega na ustawienie myB0.A = null
, która ma inne niepożądane efekty w moim kodzie.
Próba # 3: Użyłem TransactionScope wokół mojego całego DbContext. Jednak nawet po wywołaniu SaveChanges()
między poszczególnymi Attach()
i Add()
, moduł śledzenia zmian nie opróżnia swoich śledzonych wpisów, więc mam ten sam problem co w próbie nr 1.
Próba nr 4: I dalej z TransactionScope, poza tym, że stosuje się wzór w repozytorium/DAO i wewnętrznie utworzyć nowy DbContext i wywołać SaveChanges()
dla każdej odrębnej operacji ja. W tym przypadku wystąpił błąd "Aktualizacja sklepu, wstawienie lub usunięcie instrukcji wpłynęło na nieoczekiwaną liczbę wierszy". Podczas korzystania z SQL Profiler, stwierdzam, że podczas wywoływania SaveChanges()
na I zrobiłem (pierwszy Add()
), faktycznie wysyła UPDATE
SQL do bazy danych z pierwszy po raz drugi - ale nie zmień dowolne wiersze. To wydaje mi się błędem w Entity Framework.
Próba nr 5: Zamiast korzystać z TransactionScope, zdecydowałem się użyć wyłącznie opcji DbTransaction. Nadal tworzę wiele kontekstów, ale przekazuję prefabrykowane EntityConnection do każdego nowego kontekstu podczas jego tworzenia (przez buforowanie i ręczne otwieranie EntityConnection zbudowanego przez pierwszy kontekst). Jednak gdy to zrobię, drugi kontekst uruchamia zdefiniowany przeze mnie inicjator, mimo że byłby już uruchomiony po uruchomieniu aplikacji. W środowisku deweloperskim mam to wysiew niektórych danych testowych, a to faktycznie czasy na wating dla blokady bazy danych na stole mój pierwszy zmodyfikowany Attach()
(ale nadal jest zablokowany z powodu wciąż otwartej transakcji).
Pomoc !! Próbowałem już wszystkiego, co przychodzi mi do głowy, i nie w pełni refaktoryzowałem moją aplikację, aby nie korzystał z właściwości nawigacyjnych ani nie używał ręcznie skonstruowanych DAO do wykonywania instrukcji INSERT, UPDATE i DELETE. Brakuje mi tego. Wydaje się, że musi istnieć sposób na uzyskanie korzyści z Entity Framework dla mapowania O/R, ale nadal ręcznie kontrolując operacje w ramach transakcji!
Czy próbowałeś już dołączać myA? Będziesz musiał to zrobić przed podłączeniem czegokolwiek innego. – cadrell0