使用“动态Linq”;。任何;在Where子句中(C#/.Net Core/EF Core)

使用“动态Linq”;。任何;在Where子句中(C#/.Net Core/EF Core),c#,entity-framework,.net-core,entity-framework-core,ef-core-2.1,C#,Entity Framework,.net Core,Entity Framework Core,Ef Core 2.1,我试图根据存储在另一个dbset中的属性进行一些文章过滤。我正在使用一些类: public class Article { public string ArticleCode { get; set; } public string var1 { get; set; } public string var2 { get; set; } public string var3 { get; set; } public virtual List&l

我试图根据存储在另一个dbset中的属性进行一些文章过滤。我正在使用一些类:

public class Article
{
    public string ArticleCode { get; set; }
    public string var1 { get; set; }
    public string var2 { get; set; }
    public string var3 { get; set; }        
    public virtual List<ArticleProperty> Properties { get; set; }
}

public class ArticleProperty
{
    public string ArticleCode { get; set; }
    public string PropertyCode { get; set; }
    public string var4 { get; set; }
    public string var5 { get; set; }
    public string var6 { get; set; }
}

public class ArticleSummary
{
    public string ArticleCode { get; set; }
    public string var7 { get; set; }
    public string var8 { get; set; }       
}

public class WebDbContext : DbContext
{

    public virtual DbSet<Article> Article{ get; set; } 
    public virtual DbSet<ArticleProperty> ArticleProperty{ get; set; }
    /* some more code */
}

只有当有2个“Any”-语句(除以&&,与我“硬编码”时相同)时才会发生这种情况。我不知道为什么

不使用字符串,直接使用查询即可:

IQueryable<ArticleSummary> Articles = _DbContext.Article
    .Where(a => a.var1 == SomeLocalVariable1)
    .Where(a => a.var2 == SomeLocalVariable2 || a.var2 == SomeLocalVariable3)
    .Where(query);
foreach(...) {
    Articles = Articles.Where(...);
}
Articles = Articles.OrderByDescending(a => a.var6)
    .Select(a => new ArticleSummary
    {
        ArticleCode = a.ArticleCode ,
        var7 = a.var1
        var8 = a.var3
    });
IQueryable Articles=\u DbContext.Article
.Where(a=>a.var1==SomeLocalVariable1)
.其中(a=>a.var2==SomeLocalVariable2 | | a.var2==SomeLocalVariable3)
.何处(查询);
foreach(…){
条款=条款,其中(…);
}
Articles=Articles.OrderByDescending(a=>a.var6)
.选择(a=>new ArticleSummary
{
ArticleCode=a.ArticleCode,
var7=a.var1
var8=a.var3
});
动态LINQ有自己的功能。Lambda表达式不是以
a=>
ap=>
开头的,有一种称为current scope的东西可以简化一些查询,但通常在访问外部级别参数时会出现问题。所有可查询的扩展都定义了一个称为
it
的单作用域参数,可以省略该参数

简而言之,动态LINQ不太适合使用嵌套lambda表达式访问外部lambda参数的复杂查询

通过编译时和运行时表达式的组合,可以相对容易地实现这一目标。这个想法很简单

首先,创建一个编译时lambda表达式,其中包含用作占位符的其他参数。然后使用以下简单表达式访问者将占位符替换为实际表达式:

public static class ExpressionExtensions
{
    public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
    {
        return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
    }

    class ParameterReplacer : ExpressionVisitor
    {
        public ParameterExpression Source;
        public Expression Target;
        protected override Expression VisitParameter(ParameterExpression node)
            => node == Source ? Target : base.VisitParameter(node);
    }
}
非常类似于
string.Format
,但带有表达式。然后您可以使用
Expression.AndAlso
Expression.OrElse
生成
&
|
零件

话虽如此,以下是您案例中的情况:

Expression<Func<Article, string, string, bool>> detailExpr = (a, var4, var5) =>
    a.Properties.Any(ap => ap.ArticleCode == a.ArticleCode && ap.var4 == var4 && ap.var5 == var5);

var p_a = detailExpr.Parameters[0];
var p_var4 = detailExpr.Parameters[1];
var p_var5 = detailExpr.Parameters[2];

var body = dataFilter
    .Select(filter => filter.Value
        .Select(filterDetail => detailExpr.Body
            .ReplaceParameter(p_var4, Expression.Constant(filter.Key))
            .ReplaceParameter(p_var5, Expression.Constant(filterDetail.Key)))
        .Aggregate(Expression.OrElse))
    .Aggregate(Expression.AndAlso);

var predicate = Expression.Lambda<Func<Article, bool>>(body, p_a);
expressiondetailexpr=(a,var4,var5)=>
a、 属性.Any(ap=>ap.ArticleCode==a.ArticleCode&&ap.var4==var4&&ap.var5==var5);
var p_a=详细表达式参数[0];
var p_var4=详细表达式参数[1];
var p_var5=详细表达式参数[2];
var body=dataFilter
.Select(filter=>filter.Value
.Select(filterDetail=>detailExpr.Body
.ReplaceParameter(p_var4,Expression.Constant(filter.Key))
.ReplaceParameter(p_var5,Expression.Constant(filterDetail.Key)))
.合计(表示为OrElse))
.骨料(表示为AndAlso);
var谓词=表达式.Lambda(body,p_a);

然后用
Where(predicate)
代替当前的
Where(query)

我想我需要一个变量查询(类型为string或其他?)来构建我的第二个foreach循环,对吗?只需在循环中执行与以前相同的操作。使用表达式而不是字符串我不知道在使用表达式时如何使用“ap=>”中的“a.ArticleCode”。我觉得表达起来很复杂。
Articles.Where((a)=>ap.ArticleCode==a.ArticleCode)
好了。。。
IQueryable<ArticleSummary> Articles = _DbContext.Article
    .Where(a => a.var1 == SomeLocalVariable1)
    .Where(a => a.var2 == SomeLocalVariable2 || a.var2 == SomeLocalVariable3)
    .Where(query);
foreach(...) {
    Articles = Articles.Where(...);
}
Articles = Articles.OrderByDescending(a => a.var6)
    .Select(a => new ArticleSummary
    {
        ArticleCode = a.ArticleCode ,
        var7 = a.var1
        var8 = a.var3
    });
public static class ExpressionExtensions
{
    public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
    {
        return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
    }

    class ParameterReplacer : ExpressionVisitor
    {
        public ParameterExpression Source;
        public Expression Target;
        protected override Expression VisitParameter(ParameterExpression node)
            => node == Source ? Target : base.VisitParameter(node);
    }
}
Expression<Func<Article, string, string, bool>> detailExpr = (a, var4, var5) =>
    a.Properties.Any(ap => ap.ArticleCode == a.ArticleCode && ap.var4 == var4 && ap.var5 == var5);

var p_a = detailExpr.Parameters[0];
var p_var4 = detailExpr.Parameters[1];
var p_var5 = detailExpr.Parameters[2];

var body = dataFilter
    .Select(filter => filter.Value
        .Select(filterDetail => detailExpr.Body
            .ReplaceParameter(p_var4, Expression.Constant(filter.Key))
            .ReplaceParameter(p_var5, Expression.Constant(filterDetail.Key)))
        .Aggregate(Expression.OrElse))
    .Aggregate(Expression.AndAlso);

var predicate = Expression.Lambda<Func<Article, bool>>(body, p_a);