Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 连续LINQ查询_C#_.net_Linq - Fatal编程技术网

C# 连续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"} ] 我想要的是检索数据库中与所有过滤器匹配的每个文档。对

我正在尝试构建一个LINQ查询来查询包含
文档的大型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();