2012-10-19 12 views
19

Mam procedura składowana, która zwraca wiele zestawów wyników. Wykonuję to z eleganckim wyglądem.Dapper Multi Mapping z QueryMultiple

Jednym z zestawów wyników jest Person JOIN Checks, gdzie osoba może mieć wiele czeków.

Celem końcowym jest posiadanie odrębnych obiektów osób, które mają kolekcję obiektów kontrolnych.

QueryMutliple daje mi Sqlmapper.GridReader. Widzę przeciążenie SqlMapper.GridReader.Read(), które ma Func<TFirst, TSecond, TReturn>.

Czy istnieje przykład, jak tego użyć?

Odpowiedz

5

Oto jak mam to działa:

var q = _sqlConnection.QueryMultiple("MySproc", 
            myParams, 
            commandType: CommandType.StoredProcedure); 
var set1 = q.Read<Set1Type>(); 

var set2Func = new Func<Person, Check, Person>((p, c) => { 
    p.CheckAlert = c; 
    return p; 
}); 

var set2 = q.Read(set2Func, "CheckId") 
      .GroupBy(x => x.PersonId) 
      .Select(x => { 
       var person = x.First(); 
       person.Checks = x.Select(p => p.Check).ToArray(); 
       person.Check = null; // i really don't like this 
       return person; 
      }) 
      .ToArray(); 

Jako komentarz mówi, że nie podoba mi się niepotrzebne właściwość wyboru na obiekcie osoby.

Wciąż chciałbym usłyszeć o lepszym sposobie robienia tego.

+2

+1, ale może warto * nie * akceptując to jako odpowiedź, jeśli szukasz lepszy sposób to zrobić, ponieważ pytania bez odpowiedzi wydają się przyciągać więcej uwagi. – dumbledad

+0

Co to jest "CheckId", skąd to bierzesz? – dumbledad

+0

Rozumiem - to parametr [splitOn:] (http://stackoverflow.com/a/7478958/575530) – dumbledad

4

Oto wersja rozwiązania, którego użyłem. Rozwiązałem problem, który Ronnie podniósł w his answer, używając hierarchii dziedziczenia zamiast ustawiania właściwości na wartość null, ale oznacza to tyle samo.

Oto SQL: użytkownicy mają przedmioty i kolekcje, a przedmioty mogą znajdować się w kolekcjach.

CREATE TABLE Users 
(id UNIQUEIDENTIFIER DEFAULT (NEWID()) NOT NULL, 
name NVARCHAR (MAX) NULL, 
email NVARCHAR (128) NULL, 
PRIMARY KEY (id)) 

CREATE TABLE Items 
(id UNIQUEIDENTIFIER DEFAULT (NEWID()) NOT NULL, 
userId UNIQUEIDENTIFIER NOT NULL, 
name NVARCHAR (MAX) NULL, 
description NVARCHAR (MAX) NULL, 
PRIMARY KEY (id), 
FOREIGN KEY (userId) REFERENCES Users (id)) 

CREATE TABLE Collections 
(id UNIQUEIDENTIFIER DEFAULT (NEWID()) NOT NULL, 
userId UNIQUEIDENTIFIER NOT NULL, 
name NVARCHAR (MAX) NULL, 
layoutSettings NVARCHAR (MAX) NULL, 
PRIMARY KEY (id), 
FOREIGN KEY (userId) REFERENCES Users (id)) 

CREATE TABLE CollectedItems 
(itemId UNIQUEIDENTIFIER NOT NULL, 
collectionId UNIQUEIDENTIFIER NOT NULL, 
PRIMARY KEY CLUSTERED (itemId, collectionId), 
FOREIGN KEY (itemId) REFERENCES Items (id), 
FOREIGN KEY (collectionId) REFERENCES Collections (id)) 

Teraz klasy modelu danych. Kolekcje są nieco bardziej skomplikowane, niż można się było spodziewać, aby poradzić sobie z wieloma mapami Dappera z wieloma zapytaniami.

public class User 
{ 
    public Guid Id { get; set; } 
    public string Name { get; set; } 
    public string Email { get; set; } 
    public List<Item> Items { get; set; } 
    public List<Collection> Collections { get; set; } 
} 

public class Item 
{ 
    public Guid Id { get; set; } 
    public Guid UserId { get; set; } 
    public string Name { get; set; } 
    public string Description { get; set; } 
} 

public class CoreCollection 
{ 
    public Guid Id { get; set; } 
    public Guid UserId { get; set; } 
    public string Name { get; set; } 
    public string LayoutSettings { get; set; } 
} 

public class PartialDataCollection : CoreCollection 
{ 
    public Guid ItemId { get; set; } 
} 

public class Collection : CoreCollection 
{ 
    public List<Guid> ItemIds { get; set; } 
} 

public class CollectedItem 
{ 
    public Guid ItemId { get; set; } 
    public Guid CollectionId { get; set; } 
    public DateTime CreatedAt { get; set; } 
} 

Wreszcie mamy metody kontrolera, który wykorzystuje Dapper wielu mapowania z wieloma zapytaniami

[Route("GetUser/{id}")] 
public User GetUser(Guid id) 
{ 
    var sql = @"SELECT * FROM Users WHERE id = @id 
       SELECT * FROM Items WHERE userId = @id 
       SELECT * FROM Collections 
        LEFT OUTER JOIN CollectedItems ON Collections.id = CollectedItems.collectionId 
        WHERE userId = @id"; 
    using (var connection = new SqlConnection(ConnectionString)) 
    { 
     var multi = connection.QueryMultiple(sql, new { id = id }); 
     var user = multi.Read<User>().Single(); 
     var items = multi.Read<Item>().ToList(); 
     var partialDataCollections = multi.Read<PartialDataCollection, CollectedItem, PartialDataCollection>(AddCollectedItem, splitOn: "itemId").ToList(); 

     user.Items = items; 

     user.Collections = partialDataCollections.GroupBy(
      pdc => pdc.Id, 
      (key, group) => new Collection 
      { 
       Id = key, 
       UserId = group.First().UserId, 
       Name = group.First().Name, 
       LayoutSettings = group.First().LayoutSettings, 
       ItemIds = group.Select(groupMember => groupMember.ItemId).ToList() 
      }).ToList(); 

     return user; 
    } 
} 

private PartialDataCollection AddCollectedItem(PartialDataCollection collection, CollectedItem collectedItem) 
{ 
    if (collection != null && collectedItem != null) 
    { 
     collection.ItemId = collectedItem.ItemId; 
    } 
    return collection; 
} 

Gdzie Ronnie troszczy się o ustawienie person.Check = null w his answer Jestem niespokojny o dodatkowej złożoności w mojej odpowiedzi z dodanie klasa PartialDataCollection do mojego modelu. Ale nie widzę na to prostego sposobu.

(NB I podniosły to jako an issue nad projektem Wytworny GitHub.)