1

来自Github

Dapper 允许您将单行映射到多个对象。如果您想避免无关的查询和急切的加载关联,这是一个关键特性。

例子:

考虑 2 个类:Post 和 User

> 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; } } 

现在让我们说我们想要映射一个连接帖子和用户表的查询。到目前为止,如果我们需要组合 2 个查询的结果,我们需要一个新对象来表达它,但在这种情况下,将 User 对象放在 Post 对象中更有意义。

当我这样做时(我的类名称不同,但构造相同),我得到一个帖子和一个用户,一个帖子和一个用户。我正在使用 Web API,所以这都是 JSON,如果这很重要的话。如果我在 Management Studio 中直接执行 SQL,这就是我看到的方式,你会得到很多行和相应的用户记录

如果我想发回包含用户一次的 JSON 以及数组中的所有帖子,然后是下一个用户、帖子数组等,该怎么办?

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

我一遍又一遍地得到包含用户信息的 JSON(如预期但不想要)。它是倒退的。

我想要的是一个具有这样格式的 JSON 对象(我认为这是正确的):

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

我该怎么做呢?现在我得到相反的结果。我以为我会简单地改变课程,但我没有因为 Github 上的说明,“更有意义”的部分。我正在使用这个,

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

我知道我在这里不需要 splitOn,但在我的真实查询中,名称与 id 不同。

这非常接近:

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;

它生成第一个对象标识符。不确定我是否可以那样使用它。但这确实像我要求的那样完成了一系列书籍,而且我不必创建一个特殊的对象。最初,它应该在 Google Code 上,但我在 Github 上找不到这个测试。

4

2 回答 2

1

另一种选择是使用 . 查询多个

    [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"));
    }

更新:

要返回多个用户及其帖子:

    [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"));
    }
于 2017-01-27T17:30:47.183 回答
1

由于您的 SQL 查询返回的是平面记录,我建议您创建一个平面 POCO 并使用 dapper 将结果集映射到此集合。在此集合中拥有数据后,您可以使用 LINQ GroupBy 方法按您想要的方式对其进行分组。

假设您有类似的课程

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;}
}

现在为平面结果集行创建 POCO

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; }
}

现在更新您的 SQL 查询以返回与上述属性匹配的列名的结果集。

现在使用 Dapper 获取平面记录

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

现在申请 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();
于 2017-01-27T12:51:17.143 回答