Dostaję ten błąd, gdy wykonuję GetById () na encji, a następnie ustawiam kolekcję encji potomnych na mojej nowej liście, która pochodzi z widoku MVC.
Operacja nie powiodła się: Nie można zmienić relacji, ponieważ co najmniej jedna właściwość klucza obcego nie ma wartości zerowej. Po zmianie relacji powiązana właściwość klucza obcego jest ustawiana na wartość zerową. Jeśli klucz obcy nie obsługuje wartości pustych, należy zdefiniować nową relację, właściwość klucza obcego musi mieć przypisaną inną wartość inną niż pusta lub niepowiązany obiekt musi zostać usunięty.
Nie do końca rozumiem tę linię:
Nie można zmienić relacji, ponieważ co najmniej jedna właściwość klucza obcego nie ma wartości zerowej.
Dlaczego miałbym zmieniać relacje między 2 podmiotami? Powinien pozostać taki sam przez cały okres istnienia całej aplikacji.
Kod, na którym występuje wyjątek, polega na prostym przypisaniu zmodyfikowanych klas potomnych w kolekcji do istniejącej klasy macierzystej. Mam nadzieję, że przyczyni się to do usunięcia klas podrzędnych, dodania nowych i modyfikacji. Myślałem, że Entity Framework to załatwi.
Wiersze kodu można przedestylować do:
var thisParent = _repo.GetById(1);
thisParent.ChildItems = modifiedParent.ChildItems();
_repo.Save();
Odpowiedzi:
Stare elementy potomne należy usuwać
thisParent.ChildItems
ręcznie jeden po drugim. Entity Framework tego nie robi. W końcu nie może zdecydować, co chcesz zrobić ze starymi elementami potomnymi - jeśli chcesz je wyrzucić lub jeśli chcesz zachować i przypisać je innym podmiotom nadrzędnym. Musisz poinformować Entity Framework o swojej decyzji. Ale jedną z tych dwóch decyzji musisz podjąć, ponieważ podmioty potomne nie mogą żyć same bez odniesienia do jakiegokolwiek rodzica w bazie danych (z powodu ograniczenia klucza obcego). Tak w zasadzie mówi wyjątek.Edytować
Co zrobiłbym, gdyby elementy potomne można było dodawać, aktualizować i usuwać:
Uwaga: To nie zostało przetestowane. Zakłada się, że kolekcja elementów potomnych jest typu
ICollection
. (Zwykle mam,IList
a potem kod wygląda nieco inaczej.) Usunąłem też wszystkie abstrakty repozytoriów, aby było to proste.Nie wiem, czy to dobre rozwiązanie, ale uważam, że należy wykonać jakąś ciężką pracę zgodnie z tymi wytycznymi, aby zająć się wszelkiego rodzaju zmianami w kolekcji nawigacji. Byłbym również szczęśliwy widząc łatwiejszy sposób na zrobienie tego.
źródło
Powodem tego jest różnica między kompozycją a agregacją .
W składzie obiekt podrzędny jest tworzony podczas tworzenia elementu nadrzędnego i jest niszczony, gdy element nadrzędny jest niszczony . Tak więc jego żywotność jest kontrolowana przez jego rodzica. np. post na blogu i jego komentarze. Jeśli post zostanie usunięty, jego komentarze powinny zostać usunięte. Komentarze do postu, który nie istnieje, nie ma sensu. To samo dotyczy zamówień i przedmiotów zamówienia.
W agregacji obiekt podrzędny może istnieć niezależnie od jego obiektu nadrzędnego . Jeśli element nadrzędny zostanie zniszczony, obiekt potomny może nadal istnieć, ponieważ można go później dodać do innego elementu nadrzędnego. np .: związek między listą odtwarzania a utworami na tej liście. Jeśli lista odtwarzania zostanie usunięta, utworów nie należy usuwać. Można je dodać do innej listy odtwarzania.
Sposób, w jaki Entity Framework różnicuje relacje agregacji i kompozycji, jest następujący:
Do kompozycji: oczekuje, że obiekt potomny będzie miał złożony klucz podstawowy (ParentID, ChildID). Ma to na celu zaprojektowanie, ponieważ identyfikatory dzieci powinny należeć do ich rodziców.
W celu agregacji: oczekuje, że właściwość klucza obcego w obiekcie potomnym będzie pusta.
Powodem tego problemu jest sposób ustawienia klucza podstawowego w tabeli podrzędnej. Powinien być złożony, ale nie jest. Tak więc Entity Framework traktuje to powiązanie jako agregację, co oznacza, że po usunięciu lub wyczyszczeniu obiektów potomnych nie nastąpi usunięcie rekordów potomnych. Po prostu usunie to powiązanie i ustawi odpowiednią kolumnę klucza obcego na NULL (dzięki czemu te rekordy potomne mogą być później powiązane z innym rodzicem). Ponieważ twoja kolumna nie pozwala na NULL, otrzymujesz wspomniany wyjątek.
Rozwiązania:
1- Jeśli masz silny powód, dla którego nie chcesz używać klucza złożonego, musisz jawnie usunąć obiekty potomne. Można to zrobić prościej niż sugerowane wcześniej rozwiązania:
2- W przeciwnym razie, ustawiając odpowiedni klucz podstawowy na tabeli podrzędnej, kod będzie wyglądał bardziej sensownie:
źródło
To bardzo duży problem. To, co faktycznie dzieje się w twoim kodzie, to:
Parent
z bazy danych i dostajesz dołączoną jednostkęTeraz rozwiązanie naprawdę zależy od tego, co chcesz zrobić i jak chcesz to zrobić?
Jeśli używasz ASP.NET MVC, możesz spróbować użyć UpdateModel lub TryUpdateModel .
Jeśli chcesz ręcznie zaktualizować istniejące dzieci, możesz po prostu zrobić coś takiego:
Dołączanie nie jest tak naprawdę potrzebne (ustawienie stanu
Modified
spowoduje również dołączenie encji), ale podoba mi się, ponieważ sprawia, że proces jest bardziej oczywisty.Jeśli chcesz zmodyfikować istniejące, usuń istniejące i wstaw nowe dzieci, musisz zrobić coś takiego:
źródło
.Clone()
. Czy masz na myśli przypadek, żeChildItem
ma inne właściwości nawigacji podrzędnej? Ale w takim przypadku, czy nie chcielibyśmy, aby cały pod-graf był dołączony do kontekstu, ponieważ spodziewalibyśmy się, że wszystkie pod-potomki są nowymi obiektami, jeśli samo dziecko jest nowe? (Cóż, może różnić się w zależności od modelu, ale załóżmy, że pod-dzieci są „zależne” od dziecka, tak jak dzieci są zależne od rodzica.)http://stackoverflow.com/questions/20233994/do-i-need-to-create-a-dbset-for-every-table-so-that-i-can-persist-child-entitie
Uważam, że ta odpowiedź jest znacznie bardziej pomocna w przypadku tego samego błędu. Wygląda na to, że EF nie lubi go po usunięciu, woli usunąć.
Możesz usunąć zbiór rekordów dołączonych do takiego rekordu.
W tym przykładzie wszystkie rekordy szczegółów dołączone do zamówienia mają stan ustawiony na Usuń. (W przygotowaniu do dodania ponownie zaktualizowanych szczegółów, w ramach aktualizacji zamówienia)
źródło
Nie mam pojęcia, dlaczego pozostałe dwie odpowiedzi są tak popularne!
Uważam, że miałeś rację, zakładając, że struktura ORM powinna sobie z tym poradzić - w końcu to właśnie obiecuje. W przeciwnym razie Twój model domeny zostanie uszkodzony przez problemy związane z trwałością. NHibernate radzi sobie z tym szczęśliwie, jeśli poprawnie skonfigurujesz ustawienia kaskady. W Entity Framework jest to również możliwe, po prostu oczekują, że będziesz przestrzegać lepszych standardów podczas konfigurowania modelu bazy danych, szczególnie gdy będą musieli wywnioskować, jakie kaskady należy wykonać:
Musisz poprawnie zdefiniować relację rodzic-dziecko , używając „ relacji identyfikującej ”.
Jeśli to zrobisz, Entity Framework wie, że obiekt podrzędny jest identyfikowany przez obiekt nadrzędny, a zatem musi to być sytuacja „kaskadowo-usuń-sieroty”.
Inne niż powyższe, może być konieczne (z doświadczenia NHibernate)
zamiast całkowicie zastępować listę.
AKTUALIZACJA
Komentarz Slaumy przypomniał mi, że odłączone byty to kolejna część ogólnego problemu. Aby rozwiązać ten problem, możesz przyjąć podejście polegające na użyciu niestandardowego spoiwa modelu, który konstruuje modele, próbując załadować go z kontekstu. Ten post na blogu pokazuje przykład tego, co mam na myśli.
źródło
parent.ChildItems.Remove
zamiast_dbContext.ChildItems.Remove
. Nadal (EF <= 6) nie ma wbudowanej obsługi EF, aby uniknąć długiego kodu, takiego jak ten w innych odpowiedziach.return context.Items.Find(id) ?? new Item()
Jeśli używasz AutoMapper z Entity Framework na tej samej klasie, możesz napotkać ten problem. Na przykład, jeśli twoja klasa jest
Spróbuje to skopiować obie właściwości. W takim przypadku ClassBId nie ma wartości Nullable. Ponieważ AutoMapper skopiuje
destination.ClassB = input.ClassB;
, spowoduje to problem.Ustaw AutoMapper na Ignoruj
ClassB
właściwość.źródło
Właśnie miałem ten sam błąd. Mam dwie tabele z rodzicielską relacją potomną, ale skonfigurowałem „kaskadę usuwania” w kolumnie klucza obcego w definicji tabeli potomnej. Kiedy więc ręcznie usunę wiersz macierzysty (przez SQL) w bazie danych, automatycznie usunie on wiersze potomne.
Jednak nie działało to w EF, pojawił się błąd opisany w tym wątku. Powodem tego było to, że w moim modelu danych encji (plik edmx) właściwości powiązania między tabelą nadrzędną i tabelą podrzędną były nieprawidłowe.
End1 OnDelete
Opcja została skonfigurowana, aby byćnone
( „koniec 1” w moim modelu jest koniec, który ma wiele 1).Ręcznie zmieniłem
End1 OnDelete
opcję naCascade
i wtedy zadziałało. Nie wiem, dlaczego EF nie jest w stanie tego podnieść, kiedy aktualizuję model z bazy danych (mam pierwszy model bazy danych).Dla kompletności, tak wygląda mój kod do usunięcia:
Gdybym nie zdefiniował kasowania kaskadowego, musiałbym ręcznie usunąć wiersze podrzędne przed usunięciem wiersza nadrzędnego.
źródło
Dzieje się tak, ponieważ element podrzędny jest oznaczony jako Zmodyfikowany zamiast Usunięte.
A modyfikacja, którą EF robi dla elementu potomnego, gdy
parent.Remove(child)
jest wykonywana, polega po prostu na ustawieniu odwołania na jego element nadrzędny nanull
.Możesz sprawdzić EntityState dziecka, wpisując następujący kod w oknie natychmiastowym Visual Studio, gdy wystąpi wyjątek, po wykonaniu
SaveChanges()
:gdzie X należy zastąpić usuniętą jednostką.
Jeśli nie masz dostępu do
ObjectContext
wykonania_context.ChildEntity.Remove(child)
, możesz rozwiązać ten problem, czyniąc klucz obcy częścią klucza podstawowego w tabeli potomnej.W ten sposób, jeśli wykonasz
parent.Remove(child)
, EF poprawnie oznaczy jednostkę jako usuniętą.źródło
Tego rodzaju rozwiązanie zrobiło dla mnie lewę:
Ważne jest, aby powiedzieć, że usuwa to wszystkie rekordy i wstawia je ponownie. Ale w moim przypadku (mniej niż 10) jest w porządku.
Mam nadzieję, że to pomoże.
źródło
Zetknąłem się dzisiaj z tym problemem i chciałem podzielić się moim rozwiązaniem. W moim przypadku rozwiązaniem było usunięcie elementów podrzędnych przed pobraniem elementu nadrzędnego z bazy danych.
Wcześniej robiłem to tak, jak w poniższym kodzie. Otrzymam ten sam błąd wymieniony w tym pytaniu.
To, co zadziałało, to najpierw pobrać elementy potomne, używając ParentId (klucz obcy), a następnie usunąć te elementy. Następnie mogę pobrać Rodzica z bazy danych iw tym momencie nie powinno już mieć żadnych elementów potomnych i mogę dodawać nowe elementy potomne.
źródło
Musisz ręcznie wyczyścić kolekcję ChildItems i dołączyć do niej nowe elementy:
Następnie możesz wywołać metodę rozszerzenia DeleteOrphans, która będzie obsługiwać osierocone jednostki (musi być wywołana między metodami DetectChanges i SaveChanges).
źródło
context.DetectChanges();
.Wypróbowałem te rozwiązania i wiele innych, ale żadne z nich się nie udało. Ponieważ jest to pierwsza odpowiedź w Google, dodam tutaj moje rozwiązanie.
Metodą, która działała dla mnie dobrze, było usunięcie relacji z obrazu podczas zatwierdzeń, więc EF nie miał nic do zepsucia. Zrobiłem to, ponownie wyszukując obiekt nadrzędny w DBContext i usuwając to. Ponieważ właściwości nawigacyjne ponownie znalezionego obiektu są zerowe, relacje dzieci są ignorowane podczas zatwierdzania.
Należy pamiętać, że zakłada to, że klucze obce są skonfigurowane przy użyciu opcji USUŃ KASKADĘ, więc po usunięciu wiersza nadrzędnego dzieci zostaną wyczyszczone przez bazę danych.
źródło
Użyłem rozwiązania Mosha , ale nie było dla mnie oczywiste, jak najpierw poprawnie zaimplementować klucz kompozycji w kodzie.
Oto rozwiązanie:
źródło
Miałem ten sam problem, ale wiedziałem, że działał OK w innych przypadkach, więc zredukowałem problem do tego:
Wszystko, co musiałem zrobić, to uczynić ParentId częścią złożonej PK, aby wskazać, że dzieci nie mogą istnieć bez rodzica. Użyłem modelu DB-first, dodałem PK i oznaczyłem kolumnę parentId jako EntityKey (więc musiałem zaktualizować go zarówno w DB, jak i EF - nie jestem pewien, czy wystarczy EF).
Kiedy się nad tym zastanowisz, jest to bardzo eleganckie rozróżnienie, którego EF używa do decydowania, czy dzieci „mają sens” bez rodzica (w tym przypadku Clear () nie usunie ich i nie wyrzuci wyjątku, chyba że ustawisz ParentId na coś innego / specjalnego ) lub - jak w pierwotnym pytaniu - oczekujemy, że elementy zostaną usunięte po usunięciu z elementu nadrzędnego.
źródło
Ten problem powstaje, ponieważ próbujemy usunąć tabelę nadrzędną, nadal istnieją dane tabeli podrzędnej. Rozwiązujemy problem za pomocą kasowania.
W modelu Utwórz metodę w klasie dbcontext.
Następnie w naszym wywołaniu API
Opcja usuwania kaskadowego pozwala usunąć tabelę podrzędną nadrzędną i nadrzędną za pomocą tego prostego kodu. Spróbuj w ten prosty sposób.
Usuń zakres, który został użyty do usunięcia listy rekordów w bazie Dzięki
źródło
Rozwiązałem również mój problem z odpowiedzią Mosha i pomyślałem, że odpowiedź PeteraB była nieco inna, ponieważ używał wyliczenia jako klucza obcego. Pamiętaj, że musisz dodać nową migrację po dodaniu tego kodu.
Mogę również polecić ten post na blogu, aby znaleźć inne rozwiązania:
http://www.kianryan.co.uk/2013/03/orphaned-child/
Kod:
źródło
Korzystając z rozwiązania Slauma, stworzyłem ogólne funkcje, które pomagają aktualizować obiekty potomne i kolekcje obiektów potomnych.
Wszystkie moje trwałe obiekty implementują ten interfejs
Dzięki temu zaimplementowałem te dwie funkcje w moim repozytorium
Aby go użyć, wykonaj następujące czynności:
Mam nadzieję że to pomoże
DODATKOWE: Możesz także utworzyć osobną klasę DbContextExtentions (lub własną kontekstową warstwę):
i używaj go w następujący sposób:
źródło
Miałem ten sam problem, kiedy zamierzam usunąć mój rekord, niż pojawił się jakiś problem, ponieważ w przypadku tego rozwiązania problemu, gdy masz zamiar usunąć swój rekord, niż czegoś brakuje przed usunięciem nagłówka / rekordu głównego, musisz napisać w kodzie dla usuń jego szczegóły przed nagłówkiem / Master Mam nadzieję, że problem zostanie rozwiązany.
źródło
Problem ten spotkałem przed kilkoma godzinami i próbowałem wszystkiego, ale w moim przypadku rozwiązanie różniło się od powyższego.
Jeśli użyjesz już pobranej encji z bazy danych i spróbujesz zmodyfikować jej potomek, wystąpi błąd, ale jeśli otrzymasz nową kopię encji z bazy danych, nie powinno być żadnych problemów. Nie używaj tego:
Użyj tego:
źródło