使用嵌套列表创建复杂的XPQuery - LINQ to SQL

时间:2012-06-27 15:23:59

标签: linq-to-sql xpo

有关以下查询错误的任何提示?

return new ItemPricesViewModel()
            {
                Source = (from o in XpoSession.Query<PRICE>()
                          select new ItemPriceViewModel()
                          {
                              ID = o.ITEM_ID.ITEM_ID,
                              ItemCod = o.ITEM_ID.ITEM_COD,
                              ItemModifier = o.ITEM_MODIFIER_ID.ITEM_MODIFIER_COD,
                              ItemName = o.ITEM_ID.ITEM_COD,
                              ItemID = o.ITEM_ID.ITEM_ID,
                              ItemModifierID = o.ITEM_MODIFIER_ID.ITEM_MODIFIER_ID,
                              ItemPrices = (from d in o
                                                where d.ITEM_ID.ITEM_ID == o.ITEM_ID.ITEM_ID && d.ITEM_MODIFIER_ID.ITEM_MODIFIER_ID == o.ITEM_MODIFIER_ID.ITEM_MODIFIER_ID
                                                select new Price()
                                                {
                                                    ID = o.PRICE_ID,
                                                    PriceList = o.PRICELIST_ID.PRICELIST_,
                                                    Price = o.PRICE_

                                                }).ToList()
                          }).ToList()
            };
    在子查询中的
  1. o处于读取状态,我收到消息“无法找到源类型的查询模式的实现。'找不到的地方。”
  2. 我想要有不同的ItemID,ItemModifier:我应该创建一个自定义IEqualityComparer吗?
  3. 谢谢!

2 个答案:

答案 0 :(得分:1)

似乎XPO它无法响应这种情况。作为参考,您可以使用DbContext

听起来好像你想要GroupBy。尝试这样的事情。

var result = dbContext.Prices
   .GroupBy(p => new {p.ItemName, p.ItemTypeName)
   .Select(g => new Item
                 {
                     ItemName = g.Key.ItemName,
                     ItemTypeName = g.Key.ItemTypeName,
                     Prices = g.Select(p => new Price 
                                                {
                                                    Price = p.Price
                                                }
                                       ).ToList()

                 })
 .Skip(x)
 .Take(y)
 .ToList();

答案 1 :(得分:0)

可能的原因

一般来说,XPO不支持&#34;免费加入&#34;在大多数情况下。它明确写在他们的知识库或Q / A网站的某个地方。如果我再次点击该文章,我将包含一个链接。

在原始代码示例中,您尝试执行&#34;免费加入&#34;在INNER查询中。 &#39; WHERE&#39;子句正在做一个按键加入,可能是导航,但它还包含了一个额外的过滤器&#34;修饰符&#34;这可能不是关系定义的一部分。

此外,该查询尝试在内部查询中重用IQueryable<PRICE> o - 实际上似乎有点支持XPO - 但是如果你曾经添加任何预过滤(&#39; where&#39;)到顶层& #39; o&#39;,它会有很高的再次破裂的可能性。

文档声明XPO仅支持由XPObject中定义的属性和/或xpcollections形成的路径的导航连接。这适用于整个XPO,所以XPQuery也是如此。所有其他类型的连接称为&#34;自由连接&#34;并且:或者:

  • 由XPO静默模拟,方法是获取相关对象,从中提取键值,然后将查询重写为多次往返,并使用WHERE-id-IN获取完整对象的一系列部分查询 - (@ p0,@ p1, @ p2,...) - 但这只发生在一些最简单的情况下
  • 或者&#34;不完全支持&#34;,这意味着他们会抛出异常并要求您手动拆分查询或改写它

可能的直接解决方案

如果ITEM_ID是PRICE类中的关系和XPCollection,那么您可以重写查询以便它获取PRICE 对象然后构建结果对象并使用PRICE 对象初始化其字段的属性。类似的东西:

return new ItemPricesViewModel()
{
    Source = (from o in XpoSession.Query<PRICE>().AsEnumerable()
              select new ItemPriceViewModel()
              {
                  ID = o.ITEM_ID.ITEM_ID,
                  ItemCod = o.ITEM_ID.ITEM_COD,
                  ....
                  ItemModifierID = o.ITEM_MODIFIER_ID.ITEM_MODIFIER_ID,
                  ItemPrices = (from d in o
                                where d.ITEM_ID.ITEM_ID == ....
                                select new Price()
    ....          ....          ....
};

注意&#39; AsEnumerable&#39;这会破坏查询并确保首先获取PRICE对象而不是仅尝试翻译查询。很可能这样做只会工作&#34;。

此外,将查询拆分为显式阶段有时可以帮助XPO分析它:

return new ItemPricesViewModel()
{
    Source = (from o in XpoSession.Query<PRICE>()
              select new
              {
                  id = o.ITEM_ID.ITEM_ID,
                  itemcod = o.ITEM_ID.ITEM_COD,
                  ....
              }
          ).AsEnumerable()
          .Select(temp => 
              select new ItemPriceViewModel()
              {
                  ID = temp.id
                  ItemCod = temp.itemcod,
                  ....
                  ItemPrices = (from d in XpoSession.Query<PRICE>()
                                where d.ITEM_ID.ITEM_ID == ....
                                select new Price()
    ....          ....          ....
};

请注意,我首先从服务器获取项目数据,然后在&#39;客户端&#39;上构建项目,然后构建所需的分组。请注意,我不能再引用变量o了。在这些精确的案例和例子中,毫无疑问,第二个(分裂)可能比第一个慢,因为它将获取所有PRICE,然后通过其他查询重新获取分组,而第一个将只获取所有PRICE和然后根据已经获取的PRICE计算内存组。这不是我懒惰的副作用,但是在重写LINQ查询时这是一个常见的陷阱,因此我将其作为警告包括在内:)

这两个代码示例都不推荐用于您的情况,因为它们可能性能很差,特别是如果表中有很多PRICE,这很有可能。我把它们包括在内,作为只是一个例子,你可以如何重写查询以简化其结构,以便XPO可以吃它而不会窒息。但是,你必须非常小心并注意细节,因为你很容易破坏性能。

观察和真实解决方案

然而,值得注意的是,它们并不比原始查询差。它本身就很差,因为它试图从表中执行O(N ^ 2)行提取附近的操作,只是为了通过&#34; ITEM_ID&#34;然后将结果格式化为单独的对象。正确完成,它会像O(N lg N)+ O(N),所以无论是否支持,你对GroupBy的替代尝试肯定是更好的方法,而我&# 39,我很高兴你自己找到了。

很多时候,当你试图像我上面那样分割/简化XPQuery表达式时,你隐含地重新思考这个问题并找到一种更容易和更简单的方式来表达最初不支持或者只是崩溃的查询。

不幸的是,您的查询实际上非常简单。对于一个非常复杂的查询,这些查询不能被重新定义,而是分成几个阶段,并使一些加入过滤器在“客户端”工作。是不可避免的..但是再次,使用CritieriaOperators在XPCollections或XPView上进行它们也是不可能的,所以要么我们必须忍受它,要么使用普通的直接手工SQL ..

<强>旁注:

整个XPO在&#34;免费加入&#34;方面存在问题,他们没有完全支持&#34;不仅在XPQuery中,而且在XPCollection,XPView,CriteriaOperators等中也没有那么多。但是,值得注意的是,至少在我的版本&#34;对于DX11,XPQuery的LINQ支持非常差

我遇到了许多正确的LINQ查询的情况:

  • 投掷&#34; NotSupportedException&#34;,主要在FreeJoins中,但也经常使用复杂的GroupBy或Select-with-Projection,GroupJoin等等 - 有时甚至Distinct(!)似乎出现故障
  • 投掷&#34; NullReferenceExceptions&#34;在一些正确的类型转换(XPO试图解释一个将INT / NULL作为对象的列...),我经常写一些完全奇怪和人为的表达式,如foo!=null && foo.bar!=123而不是foo = 123尽管&#39;富&#39;是一个public int Foo {get;set;},都是因为DX无法正确处理数据库中的NULL(因为XPO为这个属性创建了nullable-INT列......但那是另一个故事)
  • 从其他构造中抛出其他随机ArgumentException / InvalidOperation异常
  • 甚至不正确地分析查询结构,例如这个通常是有效的:

    session.Query<ABC>()
         .Where( abc => abc.foo == "somefilter" )
         .Select( abc => new { first = abc, b = abc } )
         .ToArray();
    

    但像这样的事情通常会抛出:

    session.Query<ABC>()
         .Select( abc => new { first = abc, b = abc } )
         .Where ( temp => temp.first.foo == "somefilter" )
         .ToArray();
    

    但这个有效:

    session.Query<ABC>()
         .Select( abc => new { first = abc, b = abc } )
         .ToArray()
         .Where ( temp => temp.first.foo == "somefilter" )
         .ToArray();
    

    中间代码示例通常会抛出一个错误,显示XPO层正在尝试查找&#34; .first.foo&#34; ABC类中的路径,这显然是错误的,因为此时元素类型不再是ABC而是a'匿名类。

<强>声明

我已经注意到了,但让我再说一遍:这些观察结果与DX11有关,很可能也是早些时候。我不知道在DX12及以上版本中修复了什么(如果有的话都是!)。