2017-01-26 1 views
1

Von Github:Wie gebe ich Eins-zu-Viele-Datensätze in einer bestimmten Reihenfolge mit Dapper und Multi-Mapping zurück?

Dapper ermöglicht es Ihnen, eine einzelne Zeile auf mehrere Objekte abzubilden. Dies ist eine Hauptfunktion, wenn Sie vermeiden wollen, dass externe Abfragen und eifrig Verbände laden.

Beispiel:

Betrachten wir 2 Klassen: Post und Benutzer

> class Post { 
>  public int Id { get; set; } 
>  public string Title { get; set; } 
>  public string Content { get; set; } 
>  public User Owner { get; set; } } 
> 
> class User { 
>  public int Id { get; set; } 
>  public string Name { get; set; } } 

uns Lassen Sie uns jetzt sagen, dass wir eine Abfrage zuordnen möchten, die sowohl die Beiträge und die Benutzer-Tabelle verbindet. Bis jetzt Wenn wir das Ergebnis von 2 Abfragen kombinieren müssten, würden wir ein neues Objekt benötigen, um es auszudrücken, aber es macht in diesem Fall mehr Sinn, das Benutzerobjekt in das Post-Objekt zu setzen.

Wenn ich dies tue (Meine Klassen sind verschiedene Namen, aber das gleiche Konstrukt), bekomme ich einen Beitrag und einen Benutzer, einen Beitrag und einen Benutzer. Ich benutze die Web-API, also ist dies alles JSON, wenn das wichtig ist. Dies ist die Art und Weise, die ich es sehen würde, wenn ich gerade SQL in der Management Studio, Sie erhalten die vielen Zeilen und die entsprechenden Benutzer Datensätze

Was, wenn ich die JSON zurückschicken will, die den Benutzer einmal und alle hat Beiträge in einem Array, dann wird der nächste Benutzer, Anordnung von Pfosten usw.

id title content id name 
1 Article1 Content1 55 Smith 
2 Article2 Content2 55 Smith 
3 Article3 Content3 55 Smith 

ich erhalte die JSON zurück, dass die Benutzer Informationen über und über hat (wie erwartet, aber nicht wollte). Es ist rückwärts.

Was ich will, ist ein JSON-Objekt, das ein Format wie dieses hat (ich glaube, das ist richtig):

{ 
    "User": 55, 
    "Name": "Smith", 
    "Post": [ 
    { 
     "id": 1, 
     "title": "title1", 
     "content":"MyContent1" 
    }, 
    { 
     "id": 2, 
     "title": "title2", 
     "content":"MyContent2" 
    }, 
    { 
     "id": 3, 
     "title": "title3", 
     "content":"MyContent2" 
    } 
    ] 
} 

Wie kann ich das tun? Im Moment bekomme ich das Gegenteil. Ich dachte, ich würde einfach die Klassen wechseln, aber ich habe nicht wegen der Anweisungen auf Github, der "macht mehr Sinn" Teil. Ich verwende dieses,

(List<Post>)db.Query<Post, User, Paper>(sqlString, (post, user) => { post.user = user; return post; }, splitOn: "id"); 

Ich weiß, ich habe nicht die splitOn brauchen hier, aber in meinem wirklichen Abfrage der Name ist anders als ID.

Das ist ziemlich nah dran:

https://www.tritac.com/developers-blog/dapper-net-by-example/

public class Shop { 
    public int? Id {get;set;} 
    public string Name {get;set;} 
    public string Url {get;set;} 
    public IList<Account> Accounts {get;set;} 
} 

public class Account { 
    public int? Id {get;set;} 
    public string Name {get;set;} 
    public string Address {get;set;} 
    public string Country {get;set;} 
    public int ShopId {get;set;} 
} 

var lookup = new Dictionary<int, Shop>() 
conn.Query<Shop, Account, Shop>(@" 
        SELECT s.*, a.* 
        FROM Shop s 
        INNER JOIN Account a ON s.ShopId = a.ShopId      
        ", (s, a) => { 
         Shop shop; 
         if (!lookup.TryGetValue(s.Id, out shop)) { 
          lookup.Add(s.Id, shop = s); 
         } 
         if (shop.Accounts == null) 
          shop.Accounts = new List<Account>(); 
         shop.Accounts.Add(a); 
         return shop; 
        }, 
        ).AsQueryable(); 

var resultList = lookup.Values; 

Es macht die erste Objektkennung. Nicht sicher, ob ich es so benutzen kann oder nicht. Aber das tut die Reihe von Büchern, wie ich gefragt habe, und ich musste kein spezielles Objekt erstellen. Ursprünglich sollte es auf Google Code sein, aber ich konnte diesen Test auf Github nicht finden.

Antwort

1

Da Ihre SQL-Abfrage die flachen Datensätze zurückgibt, empfehle ich Ihnen, ein flaches POCO zu erstellen und dapper die Ergebnismenge einer Sammlung davon zuzuordnen. Sobald Sie Daten in dieser Sammlung haben, können Sie die Methode LINQ GroupBy verwenden, um sie nach Ihren Wünschen zu gruppieren.

Angenommen, Sie haben Klassen wie

public class User 
{ 
    public int Id { set;get;} 
    public string Name { set;get;} 
    public IEnumerable<Post> Posts { set;get;} 
} 
public class Post 
{ 
    public int Id { set;get;} 
    public string Title{ set;get;} 
    public string Content { set;get;} 
} 

nun die POCO für das flache Ergebnis schaffen gesetzt Reihe

public class UserPost 
{ 
    public int Id { set; get; } 
    public string Title { set; get; } 
    public string Content { set; get; } 

    public int UserId { set; get; } 
    public string Name { set; get; } 
} 

Jetzt ist Ihre SQL-Abfrage aktualisieren Sie eine Ergebnismenge mit Spaltennamen zurück, die oben genannten Eigenschaften passend .

Jetzt Dapper verwenden, um die flachen Aufzeichnungen

var userposts= new List<UserPost>(); 
using (var conn = new SqlConnection("YourConnectionString")) 
{ 
    userposts = conn.Query<UserPost>(query).ToList(); 
} 

Jetzt bewerben GroupBy

var groupedPosts = userposts.GroupBy(f => f.UserId, posts => posts, (k, v) => 
    new User() 
    { 
     UserId = k, 
     Name = v.FirstOrDefault().Name, 
     Posts = v.Select(f => new Post() { Id = f.Id, 
              Title= f.Title, 
              Content = f.Content}) 
    }).ToList(); 
+1

Dank dafür. Ich dachte, Multimapping sollte mich davon abhalten, andere Objekte zu erstellen. – johnny

+0

Können Sie mir sagen, welche Funktionssignatur Ihre GroupBy verwendet? Ich weiß nicht, wofür die ersten beiden Lambdas sind. Was macht die Gruppe? Was ist f in diesem Lambda? – johnny

1

Eine weitere Option ist das zu bekommen. QueryMultiple

[Test] 
    public void TestQueryMultiple() 
    { 
     const string sql = @"select UserId = 55, Name = 'John Doe' 
        select PostId = 1, Content = 'hello' 
        union all select PostId = 2, Content = 'world'"; 

     var multi = _sqlConnection.QueryMultiple(sql); 
     var user = multi.Read<User>().Single(); 
     user.Posts = multi.Read<Post>().ToList(); 

     Assert.That(user.Posts.Count, Is.EqualTo(2)); 
     Assert.That(user.Posts.First().Content, Is.EqualTo("hello")); 
     Assert.That(user.Posts.Last().Content, Is.EqualTo("world")); 
    } 

Update:

mehrere Benutzer und ihre Beiträge zurück:

[Test] 
    public void TestQueryMultiple2() 
    { 
     const string sql = @"select UserId = 55, Name = 'John Doe' 
        select UserId = 55, PostId = 1, Content = 'hello' 
        union all select UserId = 55, PostId = 2, Content = 'world'"; 

     var multi = _sqlConnection.QueryMultiple(sql); 
     var users = multi.Read<User>().ToList(); 
     var posts = multi.Read<Post>().ToList(); 

     foreach (var user in users) 
     { 
      user.Posts.AddRange(posts.Where(x => x.UserId == user.UserId)); 
     } 

     Assert.That(users.Count, Is.EqualTo(1)); 
     Assert.That(users.First().Posts.First().Content, Is.EqualTo("hello")); 
     Assert.That(users.First().Posts.Last().Content, Is.EqualTo("world")); 
    } 
+0

Was passiert, wenn ich eine Liste zurücksende? Ich kann kein Single() zu List zurückgeben. – johnny

+1

Ich aktualisierte die Antwort mit einem trivialen Beispiel ... –

Verwandte Themen