2016-12-28 35 views
5

Próbuję uciec od Entity Framework, ponieważ muszę wspierać bazy danych HANA oprócz baz danych SQL Server w naszym rozwiązaniu.Czy to jest właściwy sposób używania Dapper, czy też robię to wszystko źle?

Zrobiłem kilka badań z dapper, więc stworzyłem szybkie środowisko testowe z pewnym fikcyjnym scenariuszem.

Mam następujące Poços które przypominają moje schematu bazy danych (mam więcej, ale ogranicza się do pokazując je dla uproszczenia):

public class Adopter 
{ 
    public int Id { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string Address { get; set; } 
    public string Address2 { get; set; } 
    public string City { get; set; } 
    public State State { get; set; } 
    public int StateId { get; set; } 
    public string Zip { get; set; } 
    public string Email { get; set; } 
    public string Phone { get; set; } 
    public string Fax { get; set; } 
    public IEnumerable<Pet> Pets { get; set; } 
} 

public class State 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public string Abreviation { get; set; } 
} 

public class Pet 
{ 
    public int Id { get; set; } 
    public string IdTag { get; set; } 
    public string Name { get; set; } 
    public DateTime AdmitionDate { get; set; } 
    public Status Status { get; set; } 
    public int StatusId { get; set; } 
    public string Notes { get; set; } 
    public DateTime AdoptionDate { get; set; } 
    public bool IsAdopted { get; set; } 
    public int? AdopterId { get; set; } 
    public int Age { get; set; } 
    public decimal Weight { get; set; } 
    public string Color { get; set; } 
    public Breed Breed { get; set; } 
    public int BreedId { get; set; } 
    public Gender Gender { get; set; } 
    public int GenderId { get; set; } 
    public IEnumerable<PetImage> PetImages { get; set; } 
} 


public class Status 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public string Description { get; set; } 
} 

public class Gender 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

używam następujących w repozytorium, aby powrócić do listy wszystkich przysposabiające:

 using (SqlConnection connection = new SqlConnection(_connectionString)) 
     { 
      var adopters = connection.Query<Adopter>("SELECT a.* FROM Adopters a"); 
      foreach (var adopter in adopters) 
      { 
       adopter.State = connection.QueryFirst<State>("Select s.* FROM States s WHERE s.Id = @Id", new { Id = adopter.StateId }); 
       adopter.Pets = connection.Query<Pet>("Select p.* FROM Pets p WHERE p.AdopterId = @Id", new { Id = adopter.Id }); 
       foreach (var pet in adopter.Pets) 
       { 
        pet.Status = connection.QueryFirst<Status>("Select s.* FROM Status s WHERE s.Id = @Id", new { Id = pet.StatusId }); 
        pet.Gender = connection.QueryFirst<Gender>("Select g.* FROM Genders g WHERE g.Id = @Id", new { Id = pet.GenderId }); 
       } 
      } 

      return adopters; 
     } 

Jak widać, jestem pobierania danych dla każdego POCO indywidualnie na podstawie poprzedniej i robi przyłącza ręcznie w kodzie.

Czy jest to właściwy sposób robienia tego czy powinienem wykonywać duże zapytanie z wieloma złączeniami i mapować wynik w jakiś sposób za sprawą eleganckiego i LINQ?

+2

To powoduje powstanie pożądanych obiektów, więc technicznie rzecz biorąc miałoby rację ... Jednak nadal jest to zła praktyka. Ten kod wykonuje kilka wywołań do twojej bazy danych (w zależności od tego, ile razy ma pętla), więc najlepiej byłoby użyć 1 zapytania, by pobrać wszystko na raz. – RandomStranger

+0

Możesz przeczytać ten dokument => https://www.tritac.com/developers-blog/dapper-net-by-example/ "Lista obiektów z pojedynczym obiektem podrzędnym (multimap)" – pridemkA

+0

Jeśli istnieje pojęcie takie jak a DataReader NextResult w Dapper, w którym wykonujesz wszystkie zapytania i zwracasz wiele zestawów rekordów, które dla nich wybrałem. –

Odpowiedz

4

Ewentualna poprawa do rzeczywistego rozwiązania jest to dzięki zastosowaniu rozszerzenia QueryMultiple tak:

using (SqlConnection connection = new SqlConnection(_connectionString)) 
{ 
    string query = @"SELECT * FROM Adopters; 
        SELECT * FROM States; 
        SELECT * FROM Pets; 
        SELECT * FROM Status; 
        SELECT * FROM Genders;"; 

    using (var multi = connection.QueryMultiple(query, null)) 
    { 
     var adopters = multi.Read<Adopter>(); 
     var states = multi.Read<State>(); 
     var pets = multi.Read<Pet>(); 
     var statuses = multi.Read<Status>(); 
     var genders = multi.Read<Gender>(); 

     foreach (Adopter adp in adopters) 
     { 
      adp.State = states.FirstOrDefault(x => x.Id == adp.StateID); 
      adp.Pets = pets.Where(x => x.IsAdopted && 
            x.AdopterID.HasValue && 
            x.AdopterID.Value == adp.AdopterID) 
            .ToList(); 
      foreach(Pet pet in adp.Pets) 
      { 
       pet.Status = statuses.FirstOrDefault(x => x.Id == pet.StatusID); 
       pet.Gender = genders.FirstOrDefault(x => x.Id == pet.GenderID); 

      } 
     } 
    } 
} 

tu zaletą jest to, że osiągnie się baza danych tylko jeden raz, a następnie przetworzyć wszystko w pamięci.

Jednak może to być hit wydajnościowy i wąskie gardło w pamięci, jeśli masz naprawdę duże dane do pobrania (i ze zdalnej lokalizacji). Lepiej przyjrzeć się bliżej temu podejściu i wypróbować także przetwarzanie asynchroniczne i/lub stronicowanie, jeśli to możliwe.

+0

Dziękuję, wygląda to lepiej niż to, co miałem, zacznę eksperymentować z nim, paginacja będzie najprawdopodobniej najbardziej. –

+0

Wiele zależy od wielkości danych. Być może wystarczy przedstawić interfejs, w którym użytkownik może wybrać Adoptera po nazwie, a następnie uruchomić ten kod, dodając warunek WHERE do filtrowania. (Lub wyświetl stronę użytkowników i wybierz jedną z akcji wyboru/kliknięcia) – Steve

+0

Również punkt w linku, który przeglądałeś, jest bardzo użyteczny w prostych scenach łączenia, być może możliwe jest utworzenie jego rozszerzenia za pomocą wielu złączeń, ale Wynik końcowy będzie taki sam jak powyższy kod z dużo większą złożonością – Steve

2

Nie lubię być negatywny, ale ... nie rób tego! Nawet nie myśl tak. Chcesz zrzucić EF, ale wchodzisz w pułapkę, chcąc emulować EF. Pomost między twoją aplikacją a twoją DB nie jest czymś, co można zbudować raz na zawsze, dla każdego możliwego celu. Konkretnie, nie powinieneś nigdy przywracać całego stołu, a na pewno nie zapętlać każdego wiersza i emitować więcej zapytań. Możesz czuć się niesprawiedliwie krytykowany, po prostu testujesz narzędzia! Jeśli tak, to powiedz nam, jaki aspekt narzędzia analizujesz, a my skupimy się na tym.

Dapper lub QueryFirst znacznie upraszczają uruchamianie zapytań i pochłanianie wyników, więc zwróć tylko to, czego potrzebujesz, dokładnie wtedy, gdy tego potrzebujesz. Następnie trochę zdenormalizować, dla określonego zadania. Dlaczego w zapytaniach nie ma dołącza do? RDBMS są niesamowite i niesamowicie dobre w robieniu połączeń. Jeśli dołączasz do danych poza DB, szalone jest jedynym słowem, nawet jeśli Linq daje super składnię (sql-like) do tego. Bezmyślne założenie, że 1 tabela odpowiada 1 klasie, jest początkiem wielu problemów.