简洁的一对多关系映射

发布于 2025-01-16 05:41:13 字数 1412 浏览 1 评论 0原文

我正在尝试与 dapper 映射一对多关系。但我无法让它发挥作用。

我有一个表 order 和另一个表 order_product 产品 order_product

现在我有以下代码:

var sql = @"SELECT 
* 
FROM `order` o
INNER JOIN `order_product` op ON op.order_id = o.order_id
WHERE o.order_id = 5153";
var products = await connection.QueryAsync<Order, OrderProduct, Order>(sql, (order, product) =>
{
    if (order.Products == null)
    {
        order.Products = new List<OrderProduct>();
    }

    if (product != null)
    {
        order.Products.Add(product);
    }

    return order;
}, splitOn: "order_id");

出于测试目的,我正在加载 id 5153 的订单,其中包含 4 个产品。

OrderProduct 类定义为:

public class OrderProduct
    {
        public int order_product_id { get; set; }
        public int order_id { get; set; }
        public int product_id { get; set; }
        public string name { get; set; }
        public string model { get; set; }
        public int quantity { get; set; }
        public decimal price { get; set; }
        public decimal total { get; set; }
        public decimal tax { get; set; }
        public int reward { get; set; }
    }

订单类保存订单表中的所有属性 +

public ICollection<OrderProduct> Products { get; set; } 

但是,我得到的是 4 个 Order 对象,其中每个对象包含 4 个 Order 对象,而不是 1 个 Order 对象和 4 个 OrderProduct 对象。

有谁知道我在这里做错了什么?

I'm trying to map a one-to-many relationship with dapper. But I don't get it to working.

I have a table order and another one for the products order_product

Now I have this code:

var sql = @"SELECT 
* 
FROM `order` o
INNER JOIN `order_product` op ON op.order_id = o.order_id
WHERE o.order_id = 5153";
var products = await connection.QueryAsync<Order, OrderProduct, Order>(sql, (order, product) =>
{
    if (order.Products == null)
    {
        order.Products = new List<OrderProduct>();
    }

    if (product != null)
    {
        order.Products.Add(product);
    }

    return order;
}, splitOn: "order_id");

For testing purposes I'm loading the order with id 5153 which consists of 4 products.

The class OrderProduct is defined as:

public class OrderProduct
    {
        public int order_product_id { get; set; }
        public int order_id { get; set; }
        public int product_id { get; set; }
        public string name { get; set; }
        public string model { get; set; }
        public int quantity { get; set; }
        public decimal price { get; set; }
        public decimal total { get; set; }
        public decimal tax { get; set; }
        public int reward { get; set; }
    }

The order class holds all properties from the order table +

public ICollection<OrderProduct> Products { get; set; } 

However instead of 1 Order object with 4 OrderProduct objects I get 4 Order objects where each contains one product of the 4.

Does anybody know what I did wrong here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

゛清羽墨安 2025-01-23 05:41:13

尝试这种方式:

        var sql = @"SELECT * FROM `order` o 
              INNER JOIN `order_product` op ON op.order_id = o.order_id
            WHERE o.order_id = 5153";
        var orderDictionary = new Dictionary<int, Order>();
        var products = await connection.QueryAsync<Order, OrderProduct, Order>(sql, (order, product) =>
        {
            if (!orderDictionary.TryGetValue(order.order_id, out Order docEntry))
            {
                docEntry = order;
                docEntry.Products = new List<OrderProduct>();
                orderDictionary.Add(docEntry.order_id, docEntry);
            }

            if (product != null) docEntry.Products.Add(product);
            docEntry.Products = docEntry.Products.Distinct().ToList();

            return docEntry;
        }
        , splitOn: "order_id");

您在 orderDictionary 上获得了订单列表,如果您只想要一个列表,请以这种方式获取

var orderList = orderDictionary.Values.ToList();

select * 对于生产查询来说是一个坏主意,您需要确保您的 order_id 列是 splitOn 列,并且您有其中两个,通常 Dapper 会完成他的工作并尝试猜测哪一个是分离器,但如果你通过别名其中一个来修复它会更好

Try this way:

        var sql = @"SELECT * FROM `order` o 
              INNER JOIN `order_product` op ON op.order_id = o.order_id
            WHERE o.order_id = 5153";
        var orderDictionary = new Dictionary<int, Order>();
        var products = await connection.QueryAsync<Order, OrderProduct, Order>(sql, (order, product) =>
        {
            if (!orderDictionary.TryGetValue(order.order_id, out Order docEntry))
            {
                docEntry = order;
                docEntry.Products = new List<OrderProduct>();
                orderDictionary.Add(docEntry.order_id, docEntry);
            }

            if (product != null) docEntry.Products.Add(product);
            docEntry.Products = docEntry.Products.Distinct().ToList();

            return docEntry;
        }
        , splitOn: "order_id");

you got the order List on the orderDictionary, if you only want a list, get it this way

var orderList = orderDictionary.Values.ToList();

select * is a bad idea for productions queries, you need to be sure your order_id column is the splitOn one, and you have two of them, normally Dapper does his work and try to guess which one is the splitter, but bettter if you fix itby aliasing one of them

朮生 2025-01-23 05:41:13

这就是关联的表示方式:

public class Tag
{
    public int TagId { get; set; }
    public string TagName { get; set; }
    public List<Post> Posts { get; set; }
}
public class Post
{
    public int PostId { get; set; }
    public string Headline { get; set; }
    public string Content { get; set; }
    public Author Author { get; set; }
    public List<Tag> Tags { get; set; } 
}

您使用多重映射以与之前相同的方式填充两个实体,但这次,您对结果使用分组函数来组合重复的帖子实例和标签:

using(var connection = new SqlConnection(connectionString))
{
var sql = @"SELECT p.PostId, Headline, t.TagId, TagName
            FROM Posts p 
            INNER JOIN PostTags pt ON pt.PostId = p.PostId
            INNER JOIN Tags t ON t.TagId = pt.TagId";
            
var posts = await connection.QueryAsync<Post, Tag, Post>(sql, (post, tag) 
=> {
    post.Tags.Add(tag);
    return post;
}, splitOn: "TagId");

var result = posts.GroupBy(p => p.PostId).Select(g =>
{
    var groupedPost = g.First();
    groupedPost.Tags = g.Select(p => p.Tags.Single()).ToList();
    return groupedPost;
});

foreach(var post in result)
{
    Console.Write($"{post.Headline}: ");
    
    foreach(var tag in post.Tags)
    {
        Console.Write($" {tag.TagName} ");
    }
    
    Console.Write(Environment.NewLine);
    }
}

This is how the associations are represented:

public class Tag
{
    public int TagId { get; set; }
    public string TagName { get; set; }
    public List<Post> Posts { get; set; }
}
public class Post
{
    public int PostId { get; set; }
    public string Headline { get; set; }
    public string Content { get; set; }
    public Author Author { get; set; }
    public List<Tag> Tags { get; set; } 
}

You use multi-mapping to populate both entities in the same way as previously, but this time, you use a grouping function on the result to combine the duplicate post instances and the tags:

using(var connection = new SqlConnection(connectionString))
{
var sql = @"SELECT p.PostId, Headline, t.TagId, TagName
            FROM Posts p 
            INNER JOIN PostTags pt ON pt.PostId = p.PostId
            INNER JOIN Tags t ON t.TagId = pt.TagId";
            
var posts = await connection.QueryAsync<Post, Tag, Post>(sql, (post, tag) 
=> {
    post.Tags.Add(tag);
    return post;
}, splitOn: "TagId");

var result = posts.GroupBy(p => p.PostId).Select(g =>
{
    var groupedPost = g.First();
    groupedPost.Tags = g.Select(p => p.Tags.Single()).ToList();
    return groupedPost;
});

foreach(var post in result)
{
    Console.Write(
quot;{post.Headline}: ");
    
    foreach(var tag in post.Tags)
    {
        Console.Write(
quot; {tag.TagName} ");
    }
    
    Console.Write(Environment.NewLine);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文