W skrócie wyjątek jest generowany podczas POST-owania modelu otoki i zmiany stanu jednego wpisu na „Zmodyfikowany”. Przed zmianą stanu stan jest ustawiony na „Odłączony”, ale wywołanie metody Attach () powoduje zgłoszenie tego samego błędu. Używam EF6.
Proszę znaleźć mój kod poniżej (nazwy modeli zostały zmienione, aby były bardziej czytelne)
Model
// Wrapper classes
public class AViewModel
{
public A a { get; set; }
public List<B> b { get; set; }
public C c { get; set; }
}
Kontroler
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
if (!canUserAccessA(id.Value))
return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
var aViewModel = new AViewModel();
aViewModel.A = db.As.Find(id);
if (aViewModel.Receipt == null)
{
return HttpNotFound();
}
aViewModel.b = db.Bs.Where(x => x.aID == id.Value).ToList();
aViewModel.Vendor = db.Cs.Where(x => x.cID == aViewModel.a.cID).FirstOrDefault();
return View(aViewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(AViewModel aViewModel)
{
if (!canUserAccessA(aViewModel.a.aID) || aViewModel.a.UserID != WebSecurity.GetUserId(User.Identity.Name))
return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
if (ModelState.IsValid)
{
db.Entry(aViewModel.a).State = EntityState.Modified; //THIS IS WHERE THE ERROR IS BEING THROWN
db.SaveChanges();
return RedirectToAction("Index");
}
return View(aViewModel);
}
Jak pokazano powyżej
db.Entry(aViewModel.a).State = EntityState.Modified;
zgłasza wyjątek:
Dołączanie jednostki typu „A” nie powiodło się, ponieważ inna jednostka tego samego typu ma już tę samą wartość klucza podstawowego. Może się to zdarzyć, gdy używasz metody „Dołącz” lub ustawiasz stan jednostki na „Niezmieniony” lub „Zmodyfikowany”, jeśli jakiekolwiek elementy na wykresie mają sprzeczne wartości kluczowe. Może to być spowodowane tym, że niektóre jednostki są nowe i nie otrzymały jeszcze wartości kluczy wygenerowanych przez bazę danych. W takim przypadku użyj metody „Dodaj” lub stanu jednostki „Dodano”, aby prześledzić wykres, a następnie ustaw stan elementów, które nie są nowe, odpowiednio na „Niezmieniony” lub „Zmodyfikowany”.
Czy ktoś widzi coś złego w moim kodzie lub rozumie, w jakich okolicznościach spowodowałby taki błąd podczas edycji modelu?
źródło
EntityState
? Ponieważ Twój podmiot pochodzi z żądania posta, nie powinien być śledzony przez bieżący kontekst, myślę, że uważa, że próbujesz dodać element z istniejącym identyfikatoremdb
instancja jest taka sama między twoimi dwiema akcjami, może to wyjaśnić twój problem, ponieważ twój przedmiot jest ładowany metodą GET (a następnie śledzony przez kontekst) i może nie rozpoznawać tego w twojej metodzie POST jako wcześniej pobranej encji .canUserAccessA()
ładuje podmiot bezpośrednio czy jako relacja innego podmiotu?Odpowiedzi:
Problem rozwiązany!
Attach
metoda mogłaby potencjalnie komuś pomóc, ale nie pomogłaby w tej sytuacji, ponieważ dokument był już śledzony podczas ładowania w funkcji kontrolera Edit GET. Attach zwróciłby dokładnie ten sam błąd.Problem, który tu napotykam, był spowodowany funkcją,
canUserAccessA()
która ładuje jednostkę A przed aktualizacją stanu obiektu a. To zepsuło śledzoną istotę i zmieniało stan obiektu naDetached
.Rozwiązaniem była zmiana
canUserAccessA()
tak, aby ładowany przeze mnie obiekt nie był śledzony. FunkcjęAsNoTracking()
należy wywołać podczas odpytywania kontekstu.Z jakiegoś powodu użycia kulisy
.Find(aID)
zAsNoTracking()
ale to naprawdę nie ma znaczenia, jak mogę osiągnąć to samo poprzez zmianę zapytania.Mam nadzieję, że pomoże to każdemu z podobnym problemem!
źródło
using System.Data.Entity;
użyćAsNoTracking()
.Co ciekawe:
Lub jeśli nadal nie jesteś ogólny:
wydaje się, że sprawnie rozwiązał mój problem.
źródło
AddOrUpdate
jest metodą rozszerzającą wSystem.Data.Entity.Migrations
przestrzeni nazw.Wygląda na to, że obiekt, który próbujesz zmodyfikować, nie jest prawidłowo śledzony i dlatego nie jest rozpoznawany jako edytowany, ale zamiast tego dodawany.
Zamiast bezpośrednio ustawiać stan, spróbuj wykonać następujące czynności:
Chciałbym również ostrzec, że Twój kod zawiera potencjalną lukę w zabezpieczeniach. Jeśli używasz encji bezpośrednio w modelu widoku, ryzykujesz, że ktoś może zmodyfikować zawartość encji poprzez dodanie poprawnie nazwanych pól w przesłanym formularzu. Na przykład, jeśli użytkownik dodał pole wejściowe o nazwie „A.FirstName”, a jednostka zawierałaby takie pole, to wartość zostanie przypisana do modelu widoku i zapisana w bazie danych, nawet jeśli użytkownik nie będzie mógł tego zmienić podczas normalnego działania aplikacji .
Aktualizacja:
Aby obejść wspomnianą wcześniej lukę w zabezpieczeniach, nigdy nie należy ujawniać modelu domeny jako modelu widoku, ale zamiast tego użyć oddzielnego modelu widoku. Wtedy twoja akcja otrzyma viewmodel, który możesz mapować z powrotem do modelu domeny za pomocą jakiegoś narzędzia do mapowania, takiego jak AutoMapper. Dzięki temu użytkownik nie będzie mógł modyfikować poufnych danych.
Oto rozszerzone wyjaśnienie:
http://www.stevefenton.co.uk/Content/Blog/Date/201303/Blog/Why-You-Never-Expose-Your-Domain-Model-As-Your-MVC-Model/
źródło
Spróbuj tego:
źródło
dla mnie źródłem problemu była lokalna kopia. to go rozwiązało
źródło
Mój przypadek polegał na tym, że nie miałem bezpośredniego dostępu do kontekstu EF z mojej aplikacji MVC.
Więc jeśli używasz jakiegoś repozytorium do utrwalania jednostek, właściwe może być po prostu odłączenie jawnie załadowanej jednostki, a następnie ustawienie powiązanej jednostki EntityState na Modified.
Przykładowy (abstrakcyjny) kod:
MVC
Magazyn
źródło
Pomyślałem, że podzielę się swoim doświadczeniem na ten temat, chociaż czuję się trochę głupio, że nie zdałem sobie z tego sprawy wcześniej.
Używam wzorca repozytorium z instancjami repozytorium wstrzykniętymi do moich kontrolerów. Konkretne repozytoria tworzą wystąpienie mojego ModelContext (DbContext), który trwa przez cały okres istnienia repozytorium, które jest
IDisposable
usuwane przez kontroler.Problem polegał na tym, że mam zmodyfikowaną wersję stempla i wiersza na moich obiektach, więc najpierw je pobierałem, aby porównać z nagłówkami przychodzącymi. Oczywiście to ładowało i śledziło jednostkę, która była następnie aktualizowana.
Poprawka polegała po prostu na zmianie repozytorium z tworzenia nowego kontekstu raz w konstruktorze na następujące metody:
Pozwala to metodom repozytorium na ponowne tworzenie nowej instancji kontekstu przy każdym użyciu przez wywołanie
GetDbContext
lub użycie poprzedniej instancji, jeśli sobie tego życzą, określając true.źródło
Dodałem tę odpowiedź tylko dlatego, że problem jest wyjaśniony na podstawie bardziej złożonego wzorca danych i trudno mi go tutaj zrozumieć.
Stworzyłem dość prostą aplikację. Ten błąd wystąpił podczas edycji akcji POST. Akcja przyjęła ViewModel jako parametr wejściowy. Powodem użycia ViewModel było wykonanie pewnych obliczeń przed zapisaniem rekordu.
Gdy akcja przeszła przez walidację, taką jak
if(ModelState.IsValid)
, moim błędem było rzutowanie wartości z ViewModel do zupełnie nowej instancji Entity. Pomyślałem, że będę musiał utworzyć nową instancję do przechowywania zaktualizowanych danych, a następnie zapisałem taką instancję.Później zdałem sobie sprawę, że musiałem odczytać rekord z bazy danych:
i zaktualizował ten obiekt. Teraz wszystko działa.
źródło
Miałem ten problem z lokalnym var i po prostu odłączam go w ten sposób:
Przyczyny problemów związanych z ładowanymi obiektami z tym samym kluczem, więc najpierw odłączymy ten obiekt i wykonamy aktualizację, aby uniknąć konfliktu między dwoma obiektami z tym samym kluczem
źródło
Miałem podobny problem, po sondowaniu przez 2-3 dni znaleziony plik „.AsNoTracking” powinien zostać usunięty, ponieważ EF nie śledzi zmian i zakłada, że nie ma żadnych zmian, dopóki obiekt nie jest dołączony. Również jeśli nie używamy .AsNoTracking, EF automatycznie wie, który obiekt zapisać / zaktualizować, więc nie ma potrzeby używania Attach / Added.
źródło
Użyj
AsNoTracking()
miejsca, w którym otrzymujesz zapytanie.źródło
Napotkałem ten błąd, gdzie
Zmieniłem metodę B, aby mieć instrukcję using i polegać tylko na lokalnej db2 . Po:
źródło
Podobnie jak mówi Luke Puplett, problem może być spowodowany niewłaściwym usuwaniem lub tworzeniem kontekstu.
W moim przypadku miałem klasę, która przyjęła kontekst o nazwie
ContextService
:Moja usługa kontekstowa miała funkcję, która aktualizuje jednostkę przy użyciu instancji obiektu jednostki:
Wszystko było w porządku, mój kontroler, na którym zainicjowałem usługę, był problemem. Mój kontroler pierwotnie wyglądał tak:
Zmieniłem to na to i błąd zniknął:
źródło
Problem ten może być również widoczne w
ViewModel
celuEntityModel
odwzorowania (za pomocąAutoMapper
, itd.) I próby obejmującontext.Entry().State
icontext.SaveChanges()
taki, jak pokazano za pomocą bloku poniżej może rozwiązać ten problem. Należy pamiętać, żecontext.SaveChanges()
metoda jest używana dwa razy zamiast używać tuż po,if-block
ponieważ musi być również używana przy użyciu bloku.Mam nadzieję że to pomoże...
źródło
Tutaj co zrobiłem w podobnej sprawie.
To zadowolenie oznacza, że ta sama istota już istniała w kontekście, więc podążanie może pomóc
Najpierw sprawdź z ChangeTracker, czy jednostka jest w kontekście
Jeśli istnieje
źródło
Udaje mi się rozwiązać problem, aktualizując stan. kiedy wyzwalasz wyszukiwanie lub jakakolwiek inna operacja kwerendy na tym samym stanie rekordu została zaktualizowana i zmodyfikowana, więc musimy ustawić status na Odłączony, wtedy możesz uruchomić zmianę aktualizacji
źródło
Rozwiązuję ten problem za pomocą bloku „using”
Tutaj mam pomysł https://social.msdn.microsoft.com/Forums/sqlserver/es-ES/b4b350ba-b0d5-464d-8656-8c117d55b2af/problema-al-modificar-en-entity-framework?forum = vcses jest po hiszpańsku (poszukaj drugiej odpowiedzi)
źródło
możesz użyć dodanej metody, takiej jak;
ale w wielu przypadkach, jeśli chcesz użyć więcej niż jednego modelu w tym czasie, to nie zadziała, ponieważ jednostka jest już dołączona do innej encji. Więc w tym czasie możesz użyć metody ADDOrUpdate Entity Migration, która po prostu migruje obiekt z jednego do drugiego, dzięki czemu nie otrzymasz żadnego błędu.
źródło
Wyczyść cały stan
dbContextGlobalERP.ChangeTracker.Entries (). Where (e => e.Entity! = null) .ToList (). ForEach (e => e.State = EntityState.Detached);
źródło