Mapowanie przy użyciu Automapper z bazy danych - problem wydajnościowy

0

Witam,
Mam niewielki problem podczas zaciągania danych z bazy danych i mapowaniu ich do ViewModeli.
Przykładowa baza:
https://zapodaj.net/319f773ac7b0a.png.html
Przykładowy Diagram klas
https://zapodaj.net/11d757d06da6c.png.html

Aby zmapować dane z bazy do ViewModeli używałem wcześniej selecta przypisująć wartości atrybutów z danych pól
Na przykład

  public OwnedCarViewModel GetOwnedCar(Guid id)
        {
            using (var db = new ErdimContext())
            {
                var model =
                    from c in db.Car
                    select new OwnedCarViewModel()
                    {
                        MachineName = c.Machine.Name + c.Machine.Capacity,
                        FullName = c.Driver.FirstName + c.Driver.SecondName ,
                        VIN = c.VIN ,
                        Registration = c.Registration
                    };
                return model;
            }
        }

        public SoldCarViewModel GetSoldCar(Guid id)
        {
            using (var db = new ErdimContext())
            {
                var model =
                    from c in db.Car
                    select new SoldCarViewModel()
                    {
                        MachineName = c.Machine.Name + c.Machine.Capacity
                    };
                return model;
            }
        }

Działało to szybko i sprawnie ale z czasem powstało mi coraz więcej viewmodeli. Często te ViewModele potrzebowały zmapowania w taki sam sposób pewnych pól (np. MachineName).
Niestety powyższy sposób zmusił mnie bym dublował te mapowania w select, i niestety przy zmianah mapowania musiałem wyszukiwać w całym kodzie i zmieniać to ręcznie.
Wpadłem zatem na pomysł by użyć Automapper. Zrobiłem następującą konstrukcje:

CreateMap<Car, OwnedCarViewModel>()
              .IncludeBase<Car, IMachineName>()
             .IncludeBase<Car, IDriverName>()
            ;

            CreateMap<Car, SoldCarViewModel>()
              .IncludeBase<Car, IMachineName>()
            ;

        CreateMap<Car, IMachineName>()
              .ForMember(dest => dest.MachineName, opts => opts.MapFrom(c => c.Machine.Name + c.Machine.Capacity))
            ;
         CreateMap<Car, IDriverName>()
              .ForMember(dest => dest.FullName, opts => opts.MapFrom(c => c.Driver.FirstName + c.Driver.SecondName))
            ;

A moja nowa metoda w dao wyglądała tak:

  public OwnedCarViewModel GetOwnedCar_WithAutomapper(Guid id)
        {
            using (var db = new ErdimContext())
            {

                var model = db.Car.Where(s => s.id == id).ProjectTo<OwnedCarViewModel>().First();
                return model;

            }
        }

Rozwiazanie to ma tą zaletę że jeśli teraz zmieni się mapowanie MachineName to wystarczy mi zmienić to tylko w jednym miejscu, dodatkowo unikam dublowania kodu.
Niestety okrutną wadą tego rozwiązania jest czas działania. Metoda ta działa prawie 10-20 razy wolniej ....
I tu moje pytanie, czy jest jakiś inny sposób, szybszy poradzenia sobie z mapowaniem ?
Pozdrawiam

0

db.Car.Where(s => s.id == id).ProjectTo<OwnedCarViewModel>().First(); - a ile samochodów o tym samym ID masz w bazie? Bo ja w ogóle nie rozumiem po co tam Where i First.

Co do problemu - gdzie konfigurujesz Automappera?
Jak zmierzyłeś, że jest to 10-20 razy wolniejsze?

0
  1. Jeden, chodzi Ci by zapisać to jako db.Car.ProjectTo<OwnedCarViewModel>().First(s => s.id == id) ?
  2. Mam klasę CarDao i w niej metody, konfiguracja:
    static CarDao (){
    MapperConfiguration mapperConfig = new MapperConfiguration(cfg =>
    {
    cfg.AddProfile<MapperCarProfile>();
    });
    mapper = mapperConfiguration.CreateMapper();

}
3. Za pomocą testów jednostkowych. TestProject w VisualStudio

2
ice25 napisał(a):
  1. Jeden, chodzi Ci by zapisać to jako db.Car.ProjectTo<OwnedCarViewModel>().First(s => s.id == id) ?

No i w ten sposób odczytasz i zmapujesz całą bazę, żeby potem wybrać jeden viewmodel.
Raczej db.Car.Single(x => x.Id).ProjectTo<OwnedCarViewModel>().

  1. Mam klasę CarDao i w niej metody, konfiguracja:

Mapper konfigurowany w statycznym konstruktorze jakiegoś obiektu z warstwy DAO wygląda dziwnie, ale mniejsza z tym. Zdajesz sobie sprawę, ze ta konfiguracja następuje w momencie pierwszego użycia klasy CarDao, czyli najpewniej w trakcie pierwszego testu, co może zaburzać wyniki pomiarów?

  1. Za pomocą testów jednostkowych. TestProject w VisualStudio

Ale jak? Przecież tam Ci pokazuje jedynie czas wykonania testów, a nie czas działania testowanego kodu.

0

1.@mad_penquin ma rację, jeśli zastosuje db.Car.Single(s=>s.id==id) to tutaj mamy już Obiekt typu Car, i nie możemy wywołać ProjectTo. Pytanie w takim razie jak ta linijka powinna wygladać ? Czy ta konstrukcja where.ProjectTo<>.Single może zostac ?
2. Dlaczego dziwnie ? Ja to rozumiem tak że konfiguracja jest niezmienna i nie zależna od instancji więc powołuje ją w statycznym konstruktorze. Chyba że czegoś tu nie uwzględniam
3. Masz rację, faktycznie czasy mogą być różne. Zaraz sprawdzę to za pomoca StopWatcha.

1
ice25 napisał(a):

1.@mad_penquin ma rację, jeśli zastosuje db.Car.Single(s=>s.id==id) to tutaj mamy już Obiekt typu Car, i nie możemy wywołać ProjectTo. Pytanie w takim razie jak ta linijka powinna wygladać ? Czy ta konstrukcja where.ProjectTo<>.Single może zostac ?

Po prostu sprawdź jaki SQL leci do bazy i ile rekordów jest wyciąganych.

  1. Dlaczego dziwnie ? Ja to rozumiem tak że konfiguracja jest niezmienna i nie zależna od instancji więc powołuje ją w statycznym konstruktorze. Chyba że czegoś tu nie uwzględniam

Po pierwsze nie ma sensu w robieniu zależności pomiędzy CarDao a jakimiś profilami Automappera.
Po drugie takie rzeczy jak konfiguracja ogólnie, to raczej się robi jednokrotnie przy starcie aplikacji. Właśnie, żeby uniknąć takich problemów, że nagle jakiś kawałek kodu zwalnia.

  1. Masz rację, faktycznie czasy mogą być różne. Zaraz sprawdzę to za pomoca StopWatcha.

Jak chcesz naprawdę sprawdzić wydajność, to może lepiej tym: https://benchmarkdotnet.org/articles/overview.html

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