【问题标题】:Dapper map multiple joins Sql QueryDapper 映射多个连接 Sql Query
【发布时间】:2017-10-13 21:30:01
【问题描述】:

我想将复杂对象映射到具有两个内部连接的查询的简洁结果。我知道我们有映射一个内连接的解决方案,但我想映射两个内连接结果。

这里是场景:

我的课程是:

public class Order 
{
    public int id { get; set; }
    public string order_reference { get; set; }
    public string order_status { get; set; }
    public List<OrderLine> OrderLines { get; set; }
}

public class OrderLine
{
    public int id { get; set; }
    public int order_id { get; set; }
    public string product_number { get; set; }
    public List<OrderLineSize> OrderLineSizes { get; set; }
}

public class OrderLineSize
{
    public int id { get; set; }
    public int order_line_id { get; set; }
    public string size_name { get; set; }
}

Order 有 OrderLines 作为 List 和 OrderLine 作为 OrderLineSizes 作为 List。

现在,这是我的查询基础,我想用正确的数据填充 List&lt;Order&gt;

SELECT *
FROM orders_mstr o
INNER JOIN order_lines ol ON o.id = ol.order_id
INNER JOIN order_line_size_relations ols ON ol.id = ols.order_line_id

这是我目前尝试过的:

var lookup = new Dictionary<int, Order>();
            connection.Query<Order, OrderLine, Order>(@"
                    SELECT o.*, ol.*
                    FROM orders_mstr o
                    INNER JOIN order_lines ol ON o.id = ol.order_id                    
                    ", (o, ol) => {
                    Order orderDetail;
                    if (!lookup.TryGetValue(o.id, out orderDetail))
                    {
                        lookup.Add(o.id, orderDetail = o);
                    }
                    if (orderDetail.OrderLines == null)
                        orderDetail.OrderLines = new List<OrderLine>();
                    orderDetail.OrderLines.Add(ol);
                    return orderDetail;
                }).AsQueryable();

            var resultList = lookup.Values;

使用它,我可以成功地使用 OrderLine 映射订单对象,但我还想用正确的数据填充 OrderLineSizes。

【问题讨论】:

    标签: c# sql orm mapping dapper


    【解决方案1】:

    我尽力解决了。

    根据我的说法,这是更简单,更准确的解决方案。:

    var lookup = new Dictionary<int, OrderDetail>();
                var lookup2 = new Dictionary<int, OrderLine>();
                connection.Query<OrderDetail, OrderLine, OrderLineSize, OrderDetail>(@"
                        SELECT o.*, ol.*, ols.*
                        FROM orders_mstr o
                        INNER JOIN order_lines ol ON o.id = ol.order_id
                        INNER JOIN order_line_size_relations ols ON ol.id = ols.order_line_id           
                        ", (o, ol, ols) =>
                {
                    OrderDetail orderDetail;
                    if (!lookup.TryGetValue(o.id, out orderDetail))
                    {
                        lookup.Add(o.id, orderDetail = o);
                    }
                    OrderLine orderLine;
                    if (!lookup2.TryGetValue(ol.id, out orderLine))
                    {
                        lookup2.Add(ol.id, orderLine = ol);
                        orderDetail.OrderLines.Add(orderLine);
                    }
                    orderLine.OrderLineSizes.Add(ols);
                    return orderDetail;
                }).AsQueryable();
    
                var resultList = lookup.Values.ToList();
    

    【讨论】:

    • 如果我们使用 OP 的域类,这段代码实际上是行不通的。这是由于OrderLinesOrderLineSizes 为空,因此,您将在运行时的行中得到NullReferenceException:orderDetail.OrderLines.Add(orderLine);orderLine.OrderLineSizes.Add(ols);。将它们中的每一个在下一行初始化为 lookup.Addlookup2.Add,或者简单地在类级别使用 C#6 自动属性初始化器。
    【解决方案2】:

    我不知道您没有提供的“OrderDetail”类是什么,所以我使用了 Order 类。
    这也可以通过 QueryMultiple 完成,但因为您的问题包含 INNER JOIN,所以我不使用它。

    public Dictionary<int, Order> GetOrderLookup()
    {
        var lookup = new Dictionary<int, Order>();
    
        const string sql = @"   SELECT  o.id,
                                        o.order_reference,
                                        o.order_status,
    
                                        ol.id,
                                        ol.order_id,
                                        ol.product_number,
    
                                        ols.id,
                                        ols.order_line_id,
                                        ols.size_name
                                FROM    orders_mstr o
                                JOIN    order_lines ol ON o.id = ol.order_id
                                JOIN    order_line_size_relations ols ON ol.id = ols.order_line_id";
    
        List<Order> orders = null;
        using (var connection = OpenConnection(_connectionString))
        {
            orders = connection.Query<Order, OrderLine, OrderLineSize, Order>(sql, (order, orderLine, orderLizeSize) =>
            {
                orderLine.OrderLineSizes = new List<OrderLineSize> { orderLizeSize };
                order.OrderLines = new List<OrderLine>() { orderLine };
                return order;
            },
            null, commandType: CommandType.Text).ToList();
        }
    
        if (orders == null || orders.Count == 0)
        {
            return lookup;
        }
    
        foreach (var order in orders)
        {
            var contians = lookup.ContainsKey(order.id);
            if (contians)
            {
                var newLinesToAdd = new List<OrderLine>();
                var existsLines = lookup[order.id].OrderLines;
                foreach (var existsLine in existsLines)
                {
                    foreach (var newLine in order.OrderLines)
                    {
                        if (existsLine.id == newLine.id)
                        {
                            existsLine.OrderLineSizes.AddRange(newLine.OrderLineSizes);
                        }
                        else
                        {
                            newLinesToAdd.Add(newLine);
                        }
                    }
                }
                existsLines.AddRange(newLinesToAdd);
            }
            else
            {
                lookup.Add(order.id, order);
            }
        }
    
        return lookup;
    }
    

    【讨论】:

    • OrderDetail 基本上就是 Order 类
    • 好的,有问题的改一下,让别人明白
    • @DanielTshuva 你能告诉我为什么 Order 在连接中。Query 命令两次?谢谢。
    • @johnny - 在选择查询中,第一条语句是获取订单属性(id、order_reference、order_status)-> 这对于第一个订单,第二个订单是 OUT 类型。请注意,我们查询了 3 种类型的 3 条 Select 语句,但在 connection.Query 中,我们提供了 4 种类型 -> 最后一个类型是输出类型。希望你能理解。
    【解决方案3】:

    我决定使用 Dapper 来获取用于计算某些东西的大数据。这是我在 _RepositoryBase.cs 文件中加入 3 个表的 dapper 扩展方法。

        public List<Tuple<T, T2, T3, T4>> QueryMultiple<T2, T3, T4>(string sql, object param)
            where T2 : class
            where T3 : class
        {
            using (var con = new SqlConnection(GetConnStr()))
            {
                if (con.State == ConnectionState.Closed)
                    con.Open();
    
                var query = con.Query<T, T2, T3, T4, Tuple<T, T2, T3, T4>>(sql, (t, t2, t3, t4) => Tuple.Create(t, t2, t3, t4), param);
    
                //if (query.Count() == 0)
                //    return new List<T>();
    
                var data = query.ToList();
    
                con.Close();
                con.Dispose();
    
                return data;
            }
        }
    

    然后,这个功能将帮助您通过dapper获取sql连接数据。

        public List<Table1> GetSqlJoinedDataViaDaper(int customerId)
        {
            var repo = new GenericRepository<T_LOOKUP>();
            var sql = @"select table1.ID Table1ID, table1.*,
                        table2.ID Table2ID, table2.*,
                        table3.ID Table3ID, table3.*
                        from dbo.Table1 table1 (nolock)
                        left outer join dbo.Table2 table2 (nolock) on table2.ID=table1.FKTable2ID
                        left outer join dbo.Table3 table3 (nolock) on table3.ID=table1.FKTable3ID
                        where table1.CustomerID=@p1 ";
    
            var resultWithJoin = repo.QueryMultiple<Table1, Table2, Table3>(sql,
                new { p1 = 1, splitOn = "Table1ID,Table2ID,Table3ID" }).ToList();
    
            var retval = new List<Table1>();
            foreach (var item in resultWithJoin)
            {
                Table1 t1 = item.Item2; //After first split
                t1.Table2 = item.Item3; //After Table2ID split
                t1.Table3 = item.Item4; //After Table3ID split
    
                retval.Add(t1);
            }
            return retval;
        }
    

    总结: 在表之间编写您的选择和插入拆分。对 Dapper 说拆分并获取您的数据。我使用它并且比 Entity Framework 工作得更好。

    【讨论】:

    • 我遇到了QueryMultiple() 函数的问题:The type or namespace name T could not be found。当我将其更改为List&lt;Tuple&lt;T, T2, T3&gt;&gt; QueryMultiple&lt;T, T2, T3&gt;(string sql, object param) 时,它会编译,但调用函数没有按预期执行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-03
    相关资源
    最近更新 更多