EF i edycja danych a obiekty DTO

0

Witam, napotkałem w swoim projekcie na problem.
Chodzi o edycję danych w bazie danych. Projekt WPF, używam EF i AutoMapper. Dajmy na przykład mam klasę encji bazy User z polami Id, Firstname, Lastname i CreatedDate, Mam obiekt UserDTO, który zawiera te same pola oprócz CreatedDate. W ViewModelu korzystając z klasy UserService pobieram użytkownika do edycji, w serwisie pobieram z bazy, mapuję na dto i zwracam do viewmodelu. W widoku zmieniam imię i nazwisko i w serwisie odwrotnie - mapuję dto na klasę encji bazy i robię context.AddOrUpdate(user). Jak łatwo zauważyć wyczyści mi CreatedDate. Jest jakieś ładne rozwiązanie na taki problem? Mam wrzucać encję do widoku? Mój DTO musi być identyczny z klasą encji?

Proszę o sugestie.

1

Możesz pobrać na nowo przed zapisem i zmapować tylko właściwości z viewmodelu, albo przechować to chwilowo na widoku.

0

Ok, czyli co piszę coś takiego:

public void Update(UserGroupDTO userGroupDto)
        {
            var userGroup = db.UserGroups.SingleOrDefault(ug => ug.Id == userGroupDto.Id);
            userGroup = Mapper.Map<UserGroupDTO, UserGroup>(userGroupDto);
            db.UserGroups.AddOrUpdate(userGroup);
            db.SaveChanges();
        }

Mapowanie nie stworzy mi nowego obiektu i nie nadpisze tego pobranego z bazy? Wybacz za może takie głupie pytanie.

albo przechować to chwilowo na widoku.

Czyli wychodzi na to, że mam wrzucić encje na widok i zbindować do widoku?

public UserGroup GetToEditById(int id)
        {
            return db.UserGroups.SingleOrDefault(ug => ug.Id == id);
        }

A później metoda powyżej przyjmuje obiekt tej klasy i zapisuje.

Osobiście bardziej podoba mi się pierwsze rozwiązanie z mapowaniem, bo konsekwentnie unikam wyrzucania do widoku klas bazodanowych, tylko wydaje mi się, że ten sposób mapowania jaki mam nie jest poprawny.

0

Nie wiem czy to dobrze, że używasz mappera w tą stronę. Ja to robię tylko z modelu na dto. Jeśli chodzi o update to ja robię w ten sposób:

public void Update(UserGroupDTO userGroupDto)
        {
            var userGroup = db.UserGroups.SingleOrDefault(ug => ug.Id == userGroupDto.Id);
            userGroup.FirstName=userGroupDto.FirstName;
           //itd
            db.SaveChanges();
        }
0

Teoretycznie można tak. Ale mam 40 encji, po około 20 edytowalnych pól i nie chce mi się aż tyle pisać :P

Znalazłem takie rozwiązanie:

public void Update(UserGroupDTO userGroupDto)
        {
            var userGroup = db.UserGroups.SingleOrDefault(ug => ug.Id == userGroupDto.Id);
            userGroup = Mapper.Map<UserGroupDTO, UserGroup>(userGroupDto, userGroup);
            db.SaveChanges();
        }

Mam nadzieję, że z tym będzie działać.

2
lukaszek016 napisał(a):

Mapowanie nie stworzy mi nowego obiektu i nie nadpisze tego pobranego z bazy? Wybacz za może takie głupie pytanie.

Trzeba użyć przeciążonej wersji metody Map, która przyjmuje też obiekt docelowy.
Dlatego nie lubię AutoMappera i wolę ValueInjecter do takich rzeczy: https://github.com/omuleanu/ValueInjecter

Czyli wychodzi na to, że mam wrzucić encje na widok i zbindować do widoku?

Absolutnie nie.

szydlak napisał(a):

Nie wiem czy to dobrze, że używasz mappera w tą stronę.

Ale czemu?

0

Dzięki za pomoc. Masz rację, nie wiedziałem po prostu o przeciążeniu tej metody, ale już jest ok wszystko działa.

Co do pchania na widok. Właśnie nawet na chwilę, jak to ująłeś nie chcę przechowywać encji w widoku. Nie mam referencji pomiędzy projektami i tak niech najlepiej zostanie ;)

0

@somekind: A warto w ogóle używać takich bibliotek jak AutoMapper czy ValueInjecter, skoro jest MappingGenerator?

0

Jeśli płacą Ci od linijki kodu, to nie warto. Ja tam wolę napisać kod raz i nie musieć dodawać nowych mapowania przy każdej zmianie struktury danych.

0

@somekind A czy ValueInjecter rozwiązuje te problemy? https://planetlotus.github.io/2016/06/16/automapper.html

0

A to są w ogóle problemy?

Pierwsze trzy kwestie oczywiście będą występowały we wszystkim, co nie jest fizycznie istniejącym napisanym. Pytanie czy utrzymywanie dodatkowego kodu w projekcie jest tego warte?
Poza tym ten wpis jest chyba efektem nieznajomości narzędzia. AutoMapper ma przecież możliwość weryfikowania konfiguracji i sprawdzenia, czy wszystkie właściwości typów docelowych zostaną przypisane.
Punkt czwarty to prawda, zwykłe przypisanie w AutoMapperze trzeba realizować trzema lambdami. W ValueInjecterze customizacje wyglądają prościej.
Ale to jest po prostu coś, z czym trzeba się liczyć przy używaniu takiego narzędzia. Jeśli większość przypisań nie jest trywialna albo nie ma jasnych konwencji mapowania, to nie ma sensu prawdopodobnie w ogóle używać do tego mappera i lepiej napisać ręcznie.
Punkt piąty - naprawienie tego w AutoMapperze to jedna metoda rozszerzająca z jedną linijką kodu. W ValueInjecterze, o ile dobrze pamiętam, można po prostu wołać InjectFrom ile się chce.

0

Może i masz rację, ale i tak Mapping Generator wydaje się być dla mnie bardziej naturalny (mniej magiczny). Ciekaw jestem, co sądzi @neves, bo to w jego poście o nim przeczytałem.

1

Ja również nie przepadam za magią, sporo czasu używaliśmy AutoMapper'a w pracy, i dopóki jest to proste mapowanie 1:1 płaskich obiektów to wszystko śmiga pięknie. Problem jest z utrzymaniem mapowań gdzie w grę wchodzą jakieś hierarchię a w szczególności kolekcję. A u nas w projekcie akurat tego sporo jest. Strasznie dużo czasu zaczęło pożerać poprawianie takich mapowań bo błędy, jak już łaskawie jakiś rzuci AutoMapper, są bardzo mało czytelne dla bardziej skomplikowanych struktur.

Także obecnie AutoMapper jest już nie mile widziany, i kod mapujący jest po prostu automatycznie generowany.

1 użytkowników online, w tym zalogowanych: 0, gości: 1