C# 连续LINQ查询
我正在尝试构建一个LINQ查询来查询包含C# 连续LINQ查询,c#,.net,linq,C#,.net,Linq,我正在尝试构建一个LINQ查询来查询包含文档的大型SQL表(7M+个条目) 每个文档都有许多文档字段: 我的目标是对DocumentField的value字段应用连续过滤器(从0到~10个过滤器): 下面是我要应用的过滤器示例: [ {fieldId: 32, value: "CET20533"}, {fieldId: 16, value: "882341"}, {fieldId: 12, value: "101746"} ] 我想要的是检索数据库中与所有过滤器匹配的每个文档。对
文档的大型SQL表(7M+个条目)
每个文档都有许多文档字段
:
我的目标是对DocumentField
的value
字段应用连续过滤器(从0到~10个过滤器):
下面是我要应用的过滤器示例:
[
{fieldId: 32, value: "CET20533"},
{fieldId: 16, value: "882341"},
{fieldId: 12, value: "101746"}
]
我想要的是检索数据库中与所有过滤器匹配的每个文档。对于上一个示例,我希望Id为“32”的字段的值为CET20533,Id为16的字段的值为882341,依此类推
我有第一种方法:
List<MyFilter> filters = ... // Json deserialization
db.Documents.Where(document =>
filters.All(filter =>
document.DocumentFields.Any(documentField =>
documentField.Id == filter.Id
&& documentField.Value == filter.Value)));
我认为,这种方法的问题是某种并发性问题。我在foreach的每次迭代中都应用了一个简单的暂停Thread.Sleep(2000)
,以进行测试,它似乎起到了作用
问题:
- 如何消除暂停并且仍然没有并发问题
- 有没有更好的方法来构建我的查询
您必须基于过滤器构建表达式,并将每个表达式分别附加到其中(如果可以管理,则不附加表达式) 看到和
或者简单的情况:从DocumentFields开始,检索相关文档。操作包含简单类型的工作。在构建表达式的情况下,这也会更简单。我通常会这样做:将过滤器所需的所有Id放入列表中,然后使用
contains
List<int> myDesiredIds = new List<int> { 1, 2, 3, 4, 5 };
db.documents.Where(x=>myDesiredIds.Contains(x.DocumentId));
List mydesiredds=新列表{1,2,3,4,5};
其中(x=>mydesiredds.Contains(x.DocumentId));
非常确信您的数据模型过于通用。它将在程序清晰度和性能方面伤害您
但是,让我们用它来回答这个问题,我认为这是表达能力建设的一个挑战。我们的目标是获得一个很好的查询表,该查询表尊重数据服务器端的过滤器
以下是我使用的数据模型,我认为与您的数据模型非常匹配:
public sealed class Document
{
public int Id { get; set; }
// ...
public ICollection<DocumentField> Fields { get; set; }
}
public sealed class DocumentField
{
public int Id { get; set; }
public int DocumentId { get; set; }
public string StringValue { get; set; }
public float? FloatValue { get; set; }
// more typed vales here
}
其次,我实现了一个扩展方法HavingAllFields
(也在DocumentExtensions
)来创建一个IQueryable
,其中至少有一个字段满足所有字段谓词:
private static readonly MethodInfo _miAnyWhere = ((MethodCallExpression)((Expression<Func<IEnumerable<DocumentField>, bool>>)(fields => fields.Any(f => false))).Body).Method;
private static readonly Expression<Func<Document, IEnumerable<DocumentField>>> _fieldsAccessor = doc => doc.Fields;
/// <summary>
/// <paramref name="documents"/>.Where(doc => doc.Fields.Any(<paramref name="fieldPredicates"/>[0]) && ... )
/// </summary>
public static IQueryable<Document> HavingAllFields(this IQueryable<Document> documents, IEnumerable<Expression<Func<DocumentField, bool>>> fieldPredicates)
{
using (var e = fieldPredicates.GetEnumerator())
{
if (!e.MoveNext()) return documents;
Expression predicateBody = Expression.Call(_miAnyWhere, _fieldsAccessor.Body, e.Current);
while (e.MoveNext())
predicateBody = Expression.AndAlso(predicateBody, Expression.Call(_miAnyWhere, _fieldsAccessor.Body, e.Current));
var predicate = Expression.Lambda<Func<Document, bool>>(predicateBody, _fieldsAccessor.Parameters);
return documents.Where(predicate);
}
}
与文档1匹配,但与文档2不匹配。您确定“过滤器.所有”不会将iQuery更改为iNumber吗?使用探查器检查结果。我建议尽快解析过滤器并将其作为表达式添加到查询中。
db.Documents
是IQueryable
,第二种方法是将多个链接到应该可以工作的地方-我没有看到任何并发问题,因为它只是构建一个查询。当然,我假设你在两个地方都使用=
(=
是一个打字错误)。多重Where的问题是我事先不知道会有多少个过滤器:它可以是0,也可以是10。所以我不会写。Where(filter1)。Where(filter2)。Where(filter3)。正如您所说,foreach实际上只构建查询,所以我也不明白为什么没有暂停它就不能工作。。。编辑:现在似乎在没有睡眠的情况下工作,我想我之前犯了一个错误?您可以在循环中构建iQuery,如foreach(filter in filters)query=query.Where(GetExpr(fiter))代码>@ASpirin-好把戏!我必须记住这一点。实际上,每个文档都有每个字段id。重要的是与筛选器id关联的值。这在我的问题中并不清楚。@ThomasSauvajon-根据Robert的回答展开:db.documents.Where(x=>mydesiredId.Contains(x.DocumentId)&&x=>myDesiredValues.Contains(x.DocumentValue))代码>@InteXX:这样会丢失一些信息。例如,OP不需要字段ID为32且值为882341的元素;你的建议仍然会包括,因为它忽略了配对。不过问题比你的答案更复杂。这不仅仅是一个整数列表,而是一个组合标准列表。如果OP使用INT列表,他就不会收到他提到的错误(“我的过滤器列表不是原始类型,因此不能用于LINQ查询”)@ThomasSauvajon-看起来你已经用阿司匹林找到了答案。足够好;我也喜欢他的解决方案。只是一个简短的提醒:我只是偶然看到你的回复,因为它没有包含一个@notifier来确保便条进入我的收件箱。
public sealed class Document
{
public int Id { get; set; }
// ...
public ICollection<DocumentField> Fields { get; set; }
}
public sealed class DocumentField
{
public int Id { get; set; }
public int DocumentId { get; set; }
public string StringValue { get; set; }
public float? FloatValue { get; set; }
// more typed vales here
}
public static class DocumentExtensions
{
private static readonly PropertyInfo _piFieldId = (PropertyInfo)((MemberExpression)((Expression<Func<DocumentField, int>>)(f => f.Id)).Body).Member;
private static Expression<Func<DocumentField, bool>> FieldPredicate<T>(int fieldId, T value, Expression<Func<DocumentField, T>> fieldAccessor)
{
var pField = fieldAccessor.Parameters[0];
var xEqualId = Expression.Equal(Expression.Property(pField, _piFieldId), Expression.Constant(fieldId));
var xEqualValue = Expression.Equal(fieldAccessor.Body, Expression.Constant(value, typeof(T)));
return Expression.Lambda<Func<DocumentField, bool>>(Expression.AndAlso(xEqualId, xEqualValue), pField);
}
/// <summary>
/// f => f.<see cref="DocumentField.Id"/> == <paramref name="fieldId"/> && f.<see cref="DocumentField.StringValue"/> == <paramref name="value"/>.
/// </summary>
public static Expression<Func<DocumentField, bool>> FieldPredicate(int fieldId, string value) => FieldPredicate(fieldId, value, f => f.StringValue);
/// <summary>
/// f => f.<see cref="DocumentField.Id"/> == <paramref name="fieldId"/> && f.<see cref="DocumentField.FloatValue"/> == <paramref name="value"/>.
/// </summary>
public static Expression<Func<DocumentField, bool>> FieldPredicate(int fieldId, float? value) => FieldPredicate(fieldId, value, f => f.FloatValue);
// more overloads here
}
var fieldPredicates = new[] {
DocumentExtensions.FieldPredicate(32, "CET20533"), // f => f.Id == 32 && f.StringValue == "CET20533"
DocumentExtensions.FieldPredicate(16, "882341"),
DocumentExtensions.FieldPredicate(12, 101746F) // f => f.Id == 12 && f.FloatValue == 101746F
};
private static readonly MethodInfo _miAnyWhere = ((MethodCallExpression)((Expression<Func<IEnumerable<DocumentField>, bool>>)(fields => fields.Any(f => false))).Body).Method;
private static readonly Expression<Func<Document, IEnumerable<DocumentField>>> _fieldsAccessor = doc => doc.Fields;
/// <summary>
/// <paramref name="documents"/>.Where(doc => doc.Fields.Any(<paramref name="fieldPredicates"/>[0]) && ... )
/// </summary>
public static IQueryable<Document> HavingAllFields(this IQueryable<Document> documents, IEnumerable<Expression<Func<DocumentField, bool>>> fieldPredicates)
{
using (var e = fieldPredicates.GetEnumerator())
{
if (!e.MoveNext()) return documents;
Expression predicateBody = Expression.Call(_miAnyWhere, _fieldsAccessor.Body, e.Current);
while (e.MoveNext())
predicateBody = Expression.AndAlso(predicateBody, Expression.Call(_miAnyWhere, _fieldsAccessor.Body, e.Current));
var predicate = Expression.Lambda<Func<Document, bool>>(predicateBody, _fieldsAccessor.Parameters);
return documents.Where(predicate);
}
}
var documents = (new[]
{
new Document
{
Id = 1,
Fields = new[]
{
new DocumentField { Id = 32, StringValue = "CET20533" },
new DocumentField { Id = 16, StringValue = "882341" },
new DocumentField { Id = 12, FloatValue = 101746F },
}
},
new Document
{
Id = 2,
Fields = new[]
{
new DocumentField { Id = 32, StringValue = "Bla" },
new DocumentField { Id = 16, StringValue = "882341" },
new DocumentField { Id = 12, FloatValue = 101746F },
}
}
}).AsQueryable();
var matches = documents.HavingAllFields(fieldPredicates).ToList();