Pytanie DbSet.Attach (entity) vs DbContext.Entry (entity) .State = EntityState.Modified


Kiedy jestem w oderwanym scenariuszu i otrzymuję dto od klienta, który zamieniam w obiekt, aby go zapisać, robię to:

context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();

Za to, co jest wtedy DbSet.Attach(entity)

lub dlaczego powinienem użyć metody .Attach, gdy EntityState.Modified już dołącza encję?


76
2018-06-22 19:03


pochodzenie


Lepiej dodaj niektóre informacje o wersji, o to pytano wcześniej. Nie jestem pewien, czy to zasługuje na nowe pytanie. - Henk Holterman


Odpowiedzi:


Kiedy to zrobisz context.Entry(entity).State = EntityState.Modified;, nie tylko przywiązujesz się do bytu DbContext, oznaczasz także cały byt jako brudny. Oznacza to, że kiedy to robisz context.SaveChanges(), EF wygeneruje instrukcję aktualizacji, która zostanie zaktualizowana wszystko pola podmiotu.

Nie zawsze jest to pożądane.

Z drugiej strony, DbSet.Attach(entity) przywiązuje jednostkę do kontekstu bez oznaczenie go jako brudnego. Jest to równoważne z robieniem context.Entry(entity).State = EntityState.Unchanged;

Podczas dołączania w ten sposób, o ile nie wykonasz aktualizacji właściwości obiektu, przy następnym wywołaniu context.SaveChanges()EF nie wygeneruje aktualizacji bazy danych dla tego obiektu.

Nawet jeśli planujesz dokonać aktualizacji jednostki, jeśli jednostka ma wiele właściwości (kolumny db), ale chcesz tylko zaktualizować kilka, może okazać się korzystne wykonanie DbSet.Attach(entity), a następnie aktualizuj tylko te kilka właściwości, które wymagają aktualizacji. Wykonanie tego w ten sposób wygeneruje bardziej wydajną instrukcję aktualizacji z EF. EF zaktualizuje tylko zmodyfikowane właściwości (w przeciwieństwie do context.Entry(entity).State = EntityState.Modified; co spowoduje aktualizację wszystkich właściwości / kolumn)

Odpowiednia dokumentacja: Dodaj / przyłącz i państwa członkowskie.

Przykład kodu

Załóżmy, że masz następujący byt:

public class Person
{
    public int Id { get; set; } // primary key
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Jeśli twój kod wygląda następująco:

context.Entry(personEntity).State = EntityState.Modified;
context.SaveChanges();

Wygenerowany SQL będzie wyglądał mniej więcej tak:

UPDATE person
SET FirstName = 'whatever first name is',
    LastName = 'whatever last name is'
WHERE Id = 123; -- whatever Id is.

Zwróć uwagę, w jaki sposób powyższa instrukcja aktualizacji zaktualizuje wszystkie kolumny, niezależnie od tego, czy faktycznie zmieniłeś wartości, czy nie.

W przeciwieństwie do tego, jeśli twój kod używa "normalnego" Załącznika w ten sposób:

context.People.Attach(personEntity); // State = Unchanged
personEntity.FirstName = "John"; // State = Modified, and only the FirstName property is dirty.
context.SaveChanges();

Następnie wygenerowana instrukcja aktualizacji jest inna:

UPDATE person
SET FirstName = 'John'
WHERE Id = 123; -- whatever Id is.

Jak widać, instrukcja aktualizacji tylko aktualizuje wartości, które zostały faktycznie zmienione po dołączeniu encji 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ć.


184
2018-06-22 19:24



Dobra odpowiedź ze szczegółami! - Elisabeth
EF nie generuje w ten sposób klauzuli WHERE. Jeśli podłączyłeś obiekt utworzony za pomocą nowego (to jest nowego Entity ()) i ustawisz go na zmodyfikowany, musisz ustawić wszystkie oryginalne pola z powodu optymistycznej blokady. Klauzula WHERE wygenerowana w kwerendzie UPDATE zwykle zawiera wszystkie oryginalne pola (nie tylko Id), więc jeśli tego nie zrobisz, EF wygeneruje wyjątek współbieżności. - bubi
@budi: Dziękujemy za opinię. Ponownie przetestowałem, aby być pewnym, i dla podstawowego bytu, to zachowuje się jak opisałem, z WHERE klauzula zawierająca tylko klucz podstawowy i bez sprawdzania współbieżności. Aby umożliwić sprawdzanie współbieżności, należy jawnie skonfigurować kolumnę jako token równoczesny lub wiersz. W takim przypadku WHERE Klauzula będzie miała tylko klucz podstawowy i kolumnę tokenu współbieżności, a nie wszystkie pola. Jeśli twoje testy pokazują inaczej, bardzo chciałbym o tym usłyszeć. - sstan
w jaki sposób mogę dynamicznie odnaleźć właściwość czarownicy? - Navid_pdp11
@ Navid_pdp11 DbContext.Entry(person).CurrentValues i DbContext.Entry(person).OriginalValues. - Shimmy


Kiedy używasz DbSet.Update metoda Entity Framework oznacza wszystkie właściwości twojego podmiotu jako EntityState.Modified, więc śledzi je. Jeśli chcesz zmienić tylko niektóre z twoich właściwości, nie wszystkie z nich, użyj DbSet.Attach. Ta metoda tworzy wszystkie twoje właściwości EntityState.Unchanged, więc musisz wprowadzić właściwości, które chcesz zaktualizować EntityState.Modified. Tak więc, gdy aplikacja trafi do DbContext.SaveChanges, będzie obsługiwać tylko zmodyfikowane właściwości.


0
2017-09-15 07:27