确定,
上次发布此内容(上周)时,我没有正确描述问题。我已经创建了这个问题的快速示例。查询本地集合可以正常使用它作为基本查询的一部分。我发现的问题是将其与子查询的一部分一起使用。例如。
如果没有给你们一个数据库图表或代码图,这很难描述,但我会尽我所能。我试图用一个查询执行我的代码到数据库。我不想打破它并发送多个命令。这样做有一些好处,包括避免可能出现的问题,我将在此结束时解释。
我正在加入一些有关系的桌子。属性(DataEventAttributes)表当然描述了主表(DataEvents)中特定行唯一的属性。
当我在没有任何本地收集的情况下查询它时,对我的20 gig数据库工作正常并且速度极快。但是,如果我将一个本地值集合放入获取结果的子查询的一部分,我将得到“不支持查询本地集合”
这对我来说很难在我的代码中重现,所以我会对它进行评论,我可以做,你可以按照我正在做的事情。
// 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 =&gt; x.ID)之后执行.ToList(),但副作用有两个底片。一,它首先运行该查询,并从数据库中获取ID ..然后它必须将这些结果作为参数传递给sql server作为finalQuery的参数。第二个主要(显示停止)是,如果.ToList()有很多结果,SQL服务器将抛出一些奇怪的错误消息,谷歌搜索显示有许多参数被传递(这是有道理的,因为参数计数可能在10-100s之内。
所以,那就是说,我试图弄清楚如何构建一个我可以动态调整条件的查询,然后使用与符合子查询条件的ID匹配的所有属性重建我的结果集。在通过工作室的SQL服务器中,这很好用,但收集问题让我陷入困境。
我尝试了许多不同的方法,但似乎重现这一点的唯一方法是使用本地集合的查询,然后将该查询用作使用第一个查询过滤结果的另一个查询的一部分。
我有什么想法可以做到这一点吗?
Screen shot show you know I'm not crazy.
提前感谢您的帮助
答案 0 :(得分:2)
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
};
答案 1 :(得分:0)
我不是这方面的专家,但LinqToSql的工作原理是构建一个表达式树,在执行时将其转换为SQL查询。如果您的所有查询都可以转换为SQL,这可以正常工作。但是,您正在做的是基本上尝试将您的SQL查询与.NET对象集合连接。麻烦的是,这不起作用,因为无法将连接转换为SQL查询。你混合了两个不同的东西 - LinqToSql和LinqToObjects。在LinqToSql上调用ToList()使它能够工作,因为您回到了LinqToObjects的域中。对不起,我恐怕不知道这件事。