.NET [PL] Backend CodeTip Entity Framework

CodeTip #3 – Aktualizacja property w Entity Framework

Hej!

często w systemach, które implementujemy zachodzi potrzeba aktualizacji jednej kolumny w bazie danych. Przykładowo, tabela User może posiadać flagę IsLocked, która będzie informowała o tym, czy użytkownik jest zablokowany. Chcąc zaimplementować metodę blokującą delikwenta, część pomyśli o następującej implementacji:


public async Task LockUserAsync(string id)
{
    var context = new DbContext();
    var user = context.Users.SingleOrDefault(u => u.Id == id);

    user.IsLocked = true;

    context.Entry(user).State = EntityState.Modified;

    await context.SaveChangesAsync();
}

 

Poniżej zapytania jakie zostały wysłane do bazy danych:

 

sample1

 

Kod działa i spełnia swoje założenia. Problem polega na tym, że zmiana jednej flagi wymusiła na nas uprzednie pobranie danych użytkownika. Cała operacja potrzebowała zatem dwóch round tripów do bazy danych. Z punktu widzenia SQL, jest to trochę dziwne. Skoro posiadamy identyfikator użytkownika oraz nową wartość kolumny to dlaczego nie możemy od razu przejść do wykonania polecenia UPDATE? Otóż możemy 😉

 


public async Task LockUserAsync(string id)
{
    var user = new UserEntity { Id = id, IsLocked = true };

    var context = new DbContext();

    context.Users.Attach(user);

    var entry = context.Entry(user);

    entry.Property(u => u.IsLocked).IsModified = true;</pre>
<pre>    await context.SaveChangesAsync();</pre>
<pre>} 

 

Co właściwie zrobiliśmy? Po pierwsze utworzyliśmy obiekt encji użytkownika, uzupełniając przy tym  identyfikator oraz flagę IsLocked. Należy pamiętać, że nasz utworzony obiekt musi posiadać właściwość, która jednoznacznie określi jej odpowiednik w bazie danych. Z tego względu najlepszym rozwiązaniem jest Id, a nie np. nazwisko. Następnie do naszego kontekstu bazodanowego metodą Attach dołączamy obiekt, po czym określamy które pole zostało zaktualizowane. Zobaczmy, jak wygląda rezultat naszych działań:

 

sample2

 

Teraz jest ok! Zbędny round trip został wyeliminowany, a my  jesteśmy hepi 😉 Warto oczywiście pokusić się o jakieś opakowanie kodu w metodę i umieszczenie jej np. w repozytorium. Poniżej przykładowa implementacja:

 


public void UpdateProperty<TProperty>(TEntity entity, Expression<Func<TEntity, TProperty>> propertySelector)
{
    _context.Set<TEntity>().Attach(entity);

    var entry = _context.Entry(entity);

    entry.Property(propertySelector).IsModified = true;
}

 

To chyba wszystko. Niebawem pojawi się kolejny wpis konkursowy (najprawdopodobniej w weekend). Przypominam o śledzeniu mojego twittera, gdzie znajdziecie najnowsze wpisy i ciekawostki ze świata IT.

Cześć !

  • Fajna sprawa, nie wiedziałem, że EF potrafi takie rzeczy. Pytanie ile można realnie zyskać na takiej optymalizacji? I ciekawe jak z tym współpracują interceptory?

    No i takiej metody na pewno nie można umieszczać w repozytorium, bo wszystkie metody repozytorium muszą operować na encjach.

  • Dariusz Pawlukiewicz

    Dawid,
    jako pojedyncza operacja to jest niewielki benefit. W skali systemu to zawsze połowa mniej odpytań. Raczej projektując system powinniśmy zakładać wariant najgorszy więc każda taka „sztuczka” jest warta zachodu (imho). Co do repo, to gdzie byś to umieścił w takim razie?

    • Maav

      Rozwiązanie w sam raz do użycia z CQRSem.
      Zamiast Repository użyć pojedyncze UpdateCommand tak jak tutaj:
      http://www.exceptionnotfound.net/implementing-cqrs-in-net-part-4-more-events-and-summary/

      • Dariusz Pawlukiewicz

        Maav,
        ekstra, dzięki ! Muszę przeczytać całą serię 🙂

        • Anonim

          Do tej serii trzeba podejść z przymrużeniem oka. Dobrze oddaje ideę, ale już 3 eventy, które odpalają 3 zapytania na bazie, żeby zmienić 3 propertiesy, to trochę overkill.

          Cqrs jest do zastosowania właściwie wszędzie. Za to ES już się przydaje tylko w konkretnych zastosowań.

          • Dariusz Pawlukiewicz

            Mnie bardziej rozwalił Service Locator :/

    • Łatwiej by było, gdybyś odpowiedział na mój komentarz, a nie dodał nowy do postu. Nie dostałem powiadomienia. 😉

      Jeżeli chcesz aktualizować poszczególne właściwości rekordu, to nie stosujesz DDD, więc nie potrzebujesz repozytoriów. A właściwie – nie masz repozytoriów, nawet jeśli jakieś klasy tak nazywasz.

      Czy sztuczki są warte zachodu? Zazwyczaj nie. Warto optymalizować to, co faktycznie spowalnia system, a żeby to zrobić, trzeba najpierw znaleźć faktyczne problemy.

  • Włoch

    Polecam użycie https://github.com/loresoft/EntityFramework.Extended, oprócz metody Update posiada również Delete, które wykonują się w jednym round tripie.

    • Dariusz Pawlukiewicz

      Łoooo dzięki, na pewno sprawdzę!

  • Pingback: dotnetomaniak.pl()

  • Pingback: muse headband()

  • Pingback: Hyderabad Corporate Event Managers()

  • Pingback: ADME()

  • Pingback: how to make money with a iphone()

  • Pingback: find a niche market()

  • Pingback: Coehumaan iraqi()

  • Pingback: formasi cpns kemenkes 2018()

  • Pingback: handasa()

  • Pingback: rekrutmen cpns 2018()

  • Pingback: Aplikasi klinik gigi()

  • Pingback: top real estate agents()

  • Pingback: GVK Bioscience()