Kiedy jestem w scenariuszu odłączonym i pobieram dto od klienta, który mapuję na jednostkę, aby ją zapisać, robię to:
context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();
Po co wtedy DbSet.Attach(entity)
lub dlaczego powinienem używać metody .Attach, gdy EntityState.Modified już dołącza jednostkę?
c#
entity-framework
entity-framework-6
Elisabeth
źródło
źródło
Odpowiedzi:
Kiedy to robisz
context.Entry(entity).State = EntityState.Modified;
, nie tylko przywiązujesz byt do bytuDbContext
, ale także oznaczasz całą istotę jako brudną. Oznacza to, że gdy to zrobiszcontext.SaveChanges()
, EF wygeneruje instrukcję aktualizacji, która zaktualizuje wszystkie pola jednostki.Nie zawsze jest to pożądane.
Z drugiej strony,
DbSet.Attach(entity)
dołącza jednostkę do kontekstu bez oznaczania go jako brudnego. To jest równoznaczne z działaniemcontext.Entry(entity).State = EntityState.Unchanged;
W przypadku dołączania w ten sposób, chyba że następnie przystąpisz do aktualizowania właściwości jednostki, następnym razem, gdy wywołasz
context.SaveChanges()
, EF nie wygeneruje aktualizacji bazy danych dla tej jednostki.Nawet jeśli planujesz aktualizację encji, jeśli encja ma wiele właściwości (kolumn bazy danych), ale chcesz zaktualizować tylko kilka, może okazać się korzystne wykonanie a
DbSet.Attach(entity)
, a następnie zaktualizowanie tylko kilku właściwości które wymagają aktualizacji. Zrobienie tego w ten sposób spowoduje wygenerowanie bardziej wydajnej instrukcji aktualizacji z EF. EF zaktualizuje tylko zmodyfikowane właściwości (w przeciwieństwie do tego,context.Entry(entity).State = EntityState.Modified;
które spowoduje zaktualizowanie wszystkich właściwości / kolumn)Odpowiednia dokumentacja: Dodaj / Dołącz i Stany jednostek .
Przykład kodu
Załóżmy, że masz następującą jednostkę:
Jeśli Twój kod wygląda tak:
Wygenerowany kod SQL będzie wyglądał mniej więcej tak:
Zwróć uwagę, jak powyższa instrukcja aktualizacji zaktualizuje wszystkie kolumny, niezależnie od tego, czy faktycznie zmieniłeś wartości, czy nie.
Z drugiej strony, jeśli Twój kod używa „normalnego” załącznika w następujący sposób:
Wtedy wygenerowana instrukcja aktualizacji jest inna:
Jak widać, instrukcja update aktualizuje tylko te wartości, które zostały faktycznie zmienione po dołączeniu jednostki do kontekstu. W zależności od struktury tabeli może to mieć pozytywny wpływ na wydajność.
Teraz, która opcja jest dla Ciebie lepsza, zależy całkowicie od tego, co próbujesz zrobić.
źródło
WHERE
klauzulą zawierającą tylko klucz podstawowy i bez sprawdzania współbieżności. Aby mieć sprawdzanie współbieżności, muszę jawnie skonfigurować kolumnę jako token współbieżności lub rowVersion. W takim przypadkuWHERE
klauzula będzie zawierała tylko klucz podstawowy i kolumnę tokenu współbieżności, a nie wszystkie pola. Jeśli twoje testy wykażą coś innego, chciałbym o tym usłyszeć.DbContext.Entry(person).CurrentValues
iDbContext.Entry(person).OriginalValues
.Podczas korzystania z
DbSet.Update
metody Entity Framework oznacza wszystkie właściwości jednostki jakoEntityState.Modified
, więc śledzi je. Jeśli chcesz zmienić tylko niektóre właściwości, a nie wszystkie, użyjDbSet.Attach
. Ta metoda tworzy wszystkie twoje właściwościEntityState.Unchanged
, więc musisz określić właściwości, które chcesz zaktualizowaćEntityState.Modified
. Dlatego gdy aplikacja osiągnie wartośćDbContext.SaveChanges
, będzie obsługiwać tylko zmodyfikowane właściwości.źródło
Tylko dodatkowo (do zaznaczonej odpowiedzi) jest istotna różnica między
context.Entry(entity).State = EntityState.Unchanged
icontext.Attach(entity)
(w EF Core):Zrobiłem kilka testów, aby lepiej to zrozumieć (dlatego obejmuje to również ogólne testy referencyjne), więc oto mój scenariusz testowy:
QueryTrackingBehavior.NoTracking
Oto modele:
Oto (oryginalne) dane testowe w bazie danych:
Aby otrzymać zamówienie:
Teraz testy:
Prosta aktualizacja za pomocą EntityState :
Prosta aktualizacja z załącznikiem :
Aktualizacja ze zmianą Child- ID z EntityState :
Aktualizacja ze zmianą identyfikatorów dzieci z załącznikiem :
Uwaga: zgłasza wyjątek, bez względu na to, czy identyfikator został zmieniony, czy został ustawiony na oryginalną wartość, wygląda na to, że stan Id jest ustawiony na „zmieniony” i jest to niedozwolone (ponieważ jest to klucz podstawowy)
Aktualizacja ze zmianą identyfikatorów podrzędnych jako nowych (bez różnicy między EntityState i Attach):
Uwaga: zobacz różnicę w porównaniu z aktualizacją z EntityState bez nowego (powyżej). Tym razem nazwa zostanie zaktualizowana z powodu nowej instancji użytkownika.
Aktualizacja ze zmianą identyfikatorów referencyjnych z EntityState :
Aktualizacja ze zmianą identyfikatorów referencyjnych z załączeniem :
Uwaga: odniesienie zostanie zmienione na użytkownika 3, ale również użytkownik 1 zostanie zaktualizowany, myślę, że dzieje się tak, ponieważ
order.OrderedByUser.Id
jest niezmieniony (nadal jest 1).Wniosek Dzięki EntityState masz większą kontrolę, ale musisz samodzielnie aktualizować właściwości podrzędne (drugiego poziomu). Za pomocą Attach możesz zaktualizować wszystko (chyba ze wszystkimi poziomami właściwości), ale musisz mieć oko na referencje. Na przykład: Jeśli User (OrderedByUser) byłby dropDown, zmiana wartości za pomocą dropDown może spowodować nadpisanie całego obiektu użytkownika. W takim przypadku oryginalna wartość dropDown zostanie nadpisana zamiast odwołania.
Dla mnie najlepszym przypadkiem jest ustawienie obiektów takich jak OrderedByUser na null i ustawienie tylko order.OrderedByUserId na nową wartość, jeśli chcę tylko zmienić odniesienie (bez względu na to, czy EntityState czy Attach).
Mam nadzieję, że to pomoże, wiem, że to dużo tekstu: D
źródło