加入IQueryable和IList <keyvaluepair> </keyvaluepair>中的数据

时间:2015-02-25 04:21:38

标签: c# linq entity-framework join

我正在尝试将一行父数据连接到相关集合,该集合已被压缩成单个数据。

我有IQueryable个订单:

IQueryable<Order> orderList = context.Set<Order>
                              .Where("OrderDate >= @0", startDate)
                              .Where("OrderDate <= @0", endDate)

和相关的IList<KeyValuePair<int, string>>,其中每个KVP包含OrderID和每个订单行的产品名称的串联字符串。我想根据OrderID密钥将正确KeyValuePair的值加入订单信息。

为了说明,所需的输出看起来像:

OrderNum  OrderDate   Customer    State       OrderTotal   Products_Ordered
12345     12/12/2012  J.Bloggs    WA          $25.50       Bolts, Hammer, Suregrip Clamp

我正在尝试看起来像这样的linq连接:

IQueryable result = from o in orders
                    join line in orderLines on o.OrderID equals line.Key
                    select new
                    {
                      o.OrderNum
                      o.OrderDate,
                      o.Customer.CustomerFullName,
                      o.DeliverAddress.State,
                      o.TotalPrice,
                      line.Value
                    }

执行连接的方法似乎有效,但是当我访问返回的IQueryable时,我得到一个NotSupportedException无法创建类型为'System.Collections.Generic的常量值.KeyValuePair`2' 。在此上下文中仅支持原始类型或枚举类型。

我做错了什么?

3 个答案:

答案 0 :(得分:1)

实体框架存在某些类型的问题,因为它试图将它们推送到后备数据库。快速简便的解决方案是在执行连接之前将数据拉下来。

var orderList = context.Set<Order>
                       .Where("OrderDate >= @0", startDate)
                       .Where("OrderDate <= @0", endDate)
                       .ToList(); // ------> Relevant line <------

var result = from o in orderList
             join line in orderLines on o.OrderID equals line.Key
             select new
             {
                 o.OrderNum
                 o.OrderDate,
                 o.Customer.CustomerFullName,
                 o.DeliverAddress.State,
                 o.TotalPrice,
                 line.Value
             };

这只是加入发生的地方,RDBMS或客户端的变化。

性能不会成为问题,假设您没有大量数据并且所有行都将匹配。当然,您必须下载未加入的数据,因此如果orderList中只有少数result位于KeyValuePair,那么设计方面可能值得再考虑一下。唯一的选择是首先将{{1}}项目推送到服务器,这可能不是您正在寻找的。

答案 1 :(得分:1)

将查询结果(IQueryable)与存储在内存中的普通可枚举(IEnumerable<...>)相结合并没有多大意义。考虑一下,查询在实现之前不会被处理,在查询中注入自己的数据是在查询文本本身完成的 - 这不是你真正想要的,现在是吗?

我认为您最好的做法是首先将IQueryable实现为IEnumerable<>,然后在两个IEnumerable<>上进行简单的LINQ连接,这是微不足道的。< / p>

这不像是你使用IEnumerable<>来过滤服务器端的结果集,你在这里并没有真正失去任何性能。

修改:请注意,如果 使用IEnumerable<>过滤服务器端的结果,则可以执行那! EF(我假设你正在使用的)具有非常强大的特殊情况,例如IQueriable<>.Any<>()内有IEnumerable<>.Contains<>()的内容 - 它在查询文本中插入文字值。在这种情况下,这只是实际的连接没有多大意义。

答案 2 :(得分:1)

您的IQueryable实际上是一个专门的实体框架实现,在迭代时,尝试通过检查表达式树来构造SQL查询,执行此查询,并在结果上返回一个可枚举。这是脆弱的,您的投影和查询可能是任意复杂的,或表达式 - &gt; SQL转换器不知道如何处理它。

首先通过实现IQueryable来解决此问题很好,但您甚至不需要这样做。为什么有一个基本上是元组的列表,当你想要的是一个将订单ID映射到一堆数据的字典?

IDictionary<int, string> orderLines = new Dictionary<int, string>();

// Add a dummy item
orderLines[1234] = "Hello, this, is, a, test";

// Get your combined view
// Assuming you have an order with 1234 as the ID, this should work
var result = from o in orders
             select new
             {
                 o.OrderNum
                 o.OrderDate,
                 o.Customer.CustomerFullName,
                 o.DeliverAddress.State,
                 o.TotalPrice,
                 Products = orderLines[o.OrderID]
             }