Linq2Sql - 使用本地集合作为子查询的一部分 - “不支持使用本地集合的查询”

2023-12-22

Ok,

上次我发布此内容(上周)时,我没有正确描述问题。我创建了这个问题的快速示例。如果您将其用作基本查询的一部分,则查询本地集合可以正常工作。我发现的问题是将它与子查询的一部分一起使用。例如。

如果不给你们提供数据库图或代码图,这是相当难以描述的,但我会尽力而为。我正在尝试通过对数据库的一个查询来执行我的代码。我不想分解它并发送多个命令。这样做有一些优点,包括避免可能出现的问题,我将在本文末尾对此进行解释。

我正在加入一些有关系的表。属性 (DataEventAttributes) 表当然描述了主表 (DataEvents) 中特定行所独有的属性。

当我在没有任何本地集合的情况下查询它时,对于我的 20 GB 数据库来说,事情运行得很好并且速度非常快。但是,如果我将本地值集合放入获取结果的子查询的一部分,我将得到“不支持使用本地集合的查询”

这对我来说很难在我的代码中重现,所以我会尽我所能对其进行评论,您可以按照我正在做的事情进行操作。

// gets the initial query and join. We actually only care about the ID in the end, but we use the joined data
        // to determine if a row needs to be pulled.
        var initialQuery = from dataEvent in DataEvent.GetByQueryExpression(context)
                                  join attribute in DataEventAttribute.GetByQueryExpression(context) on dataEvent.DataEventID
                                      equals attribute.DataEventID
                           select new
                           {
                               ID = dataEvent.DataEventID,
                               PluginID = dataEvent.DataOwnerID,
                               TimeStamp = dataEvent.DataTimeStamp,
                               DataEventKeyID = attribute.DataEventKeyID,
                               ValueString = attribute.ValueString,
                               ValueDecimal = attribute.ValueDecimal
                           };

        // list of some ids that we need to confirm exist in the initial query before the final query
        var someSetOfIDs = new List<int>() {1, 2, 3, 4, 5};

        // This is the local collection thats filtering out some results before I rebuild the entire result set in the final query
        // If you comment this line out, the finalQuery will execute just fine.
        // with this in place, the "Queries with local collections are not supported" error will come about.
        initialQuery = initialQuery.Where(x => x.DataEventKeyID == 1 && someSetOfIDs.Contains((int) x.ValueDecimal));

        // reusable query for the sub queries in the results -- not part of the problem, just part of the example
        var attributeBaseQuery = from attribute in DataEventAttribute.GetByQueryExpression(context) select attribute;

        // Builds the final result With the IDs from the initial query 
        // the group by is to remove any duplicates that may be in the collection.
        // the select key is getting the ID that i needed
        // the select ID is the ID of the first item that was grouped.
        // the contains compares the local dataEvent object with the ID table (checking to see if it exists)
        // the result is just an example of one item I can be pulling out of the database with the new type
        var finalQuery = from dataEvent in DataEvent.GetByQueryExpression(context)
                         where initialQuery.GroupBy(x => x).Select(x => x.Key).Select(x => x.ID).Contains(dataEvent.DataEventID)
                         select new
                                    {
                                        BasicData =
                                         attributeBaseQuery.Where(
                                         attrValue =>
                                         attrValue.DataEventID == dataEvent.DataEventID &&
                                         attrValue.DataEventKeyID == (short) DataEventTypesEnum.BasicData).FirstOrDefault().
                                         ValueString
                                    };

        var finalResult = finalQuery.Take(100).ToList();

我发现的一个解决方案是在 FinalQuery 中的 .Select(x => x.ID) 之后执行 .ToList() ,但副作用有两个负面影响。第一,它首先运行该查询,并从数据库获取 ID。然后它必须将这些结果作为 FinalQuery 的参数传递回 sql 服务器。第二个主要问题(show stopper)是,如果 .ToList() 有太多结果,SQL Server 将抛出一些奇怪的错误消息,并且 Google 搜索显示传递了太多参数(这是有道理的,因为参数数量可能在数千到数百之间)。

也就是说,我试图弄清楚如何构建一个可以动态调整条件的查询,然后使用与满足子查询条件的 ID 相匹配的所有属性重建结果集。通过工作室在 SQL Server 中,这工作正常,但收集问题让我陷入困境。

我尝试了许多不同的方法,但似乎重现此问题的唯一方法是使用本地集合的查询,然后将该查询用作另一个查询的一部分,该查询使用第一个查询来过滤结果。

我有什么想法可以做到这一点吗?

屏幕截图显示你知道我没有疯。 http://www.traviswhidden.com/Portals/15/BlogImages/QueriesWithLocalCollections.jpg

先谢谢您的帮助


AFAIK,不可能在 LINQ to SQL 查询中使用内存中集合。我可以想到两种可能的解决方法:

选项 1:对每个 ID 执行查询:

    var someSetOfIDs = new List<int>() {1, 2, 3, 4, 5};

    // queryPerID will have type IEnumerable<IQueryable<'a>>
    var queryPerID = from id in someSetOfIDs
                     select (
                       from dataEvent in DataEvent.GetByQueryExpression(context)
                       join attribute in DataEventAttribute.GetByQueryExpression(context)
                         on dataEvent.DataEventID
                                  equals attribute.DataEventID
                       where attribute.DataEventKeyID == 1
                               && (int)attribute.ValueDecimal == id // Changed from Contains
                       select new
                       {
                           ID = dataEvent.DataEventID,
                           PluginID = dataEvent.DataOwnerID,
                           TimeStamp = dataEvent.DataTimeStamp,
                           DataEventKeyID = attribute.DataEventKeyID,
                           ValueString = attribute.ValueString,
                           ValueDecimal = attribute.ValueDecimal
                       });

    // For each of those queries, we an equivalent final queryable
    var res = from initialQuery in queryPerID
              select (
                  from dataEvent in DataEvent.GetByQueryExpression(context)
                  where initialQuery.GroupBy(x => x).Select(x => x.Key.ID).Contains(dataEvent.DataEventID)
                  select new
                  {
                      BasicData =
                          attributeBaseQuery.Where(
                          attrValue =>
                              attrValue.DataEventID == dataEvent.DataEventID &&
                              attrValue.DataEventKeyID == (short) DataEventTypesEnum.BasicData).FirstOrDefault().
                              ValueString
                  }) into finalQuery
              from x in finalQuery
              select x;

    var finalResult = finalQuery.Take(100).ToList();

我不确定这是否可以编译,但应该非常接近。

选项 2:构建谓词表达式someSetOfIDs传递给 SQL。

        var someSetOfIDs = new List<decimal>() { 1, 2, 3, 4, 5 };

        Expression<Func<DataEventAttribute, bool>> seed = x => false;
        var predicate = someSetOfIDs.Aggregate(seed,
            (e, i) => Expression.Lambda<Func<DataEventAttribute, bool>>(
                Expression.OrElse(
                    Expression.Equal(
                        Expression.Property(
                            e.Parameters[0],
                            "ValueDecimal"),
                        Expression.Constant(i)),
                    e.Body),
                e.Parameters));

本质上我们已经构建了一个 where 子句:

x => ((x.ValueDecimal = 5) || ((x.ValueDecimal = 4) || ((x.ValueDecimal = 3) ||
((x.ValueDecimal = 2) || ((x.ValueDecimal = 1) || False)))))

需要注意的是,这种方法不适用于匿名类型,因此您必须在具有命名类型的可查询对象上使用谓词。如果您稍微重新组织一下,这不是问题(实际上可能会产生更好的查询计划):

    var attributes = DataEventAttribute.GetByQueryExpression(context)
                     .Where(a => a.DataEventKeyID ==1)
                     .Where(predicate);

    var initialQuery = from dataEvent in DataEvent.GetByQueryExpression(context)
                       join attribute in attributes
                       select new
                       {
                           ID = dataEvent.DataEventID,
                           PluginID = dataEvent.DataOwnerID,
                           TimeStamp = dataEvent.DataTimeStamp,
                           DataEventKeyID = attribute.DataEventKeyID,
                           ValueString = attribute.ValueString,
                           ValueDecimal = attribute.ValueDecimal
                       };
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linq2Sql - 使用本地集合作为子查询的一部分 - “不支持使用本地集合的查询” 的相关文章