Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/337.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# 如何组合这两个表达式?_C#_Linq_Linq To Nhibernate - Fatal编程技术网

C# 如何组合这两个表达式?

C# 如何组合这两个表达式?,c#,linq,linq-to-nhibernate,C#,Linq,Linq To Nhibernate,我正在尝试建立一个表达式,该表达式将应用于IQueryable集合 我可以构建如下表达式: [TestClass] public class ExpressionTests { private IQueryable<MyEntity> entities; private class MyEntity { public string MyProperty { get; set; } } [TestInitialize]

我正在尝试建立一个表达式,该表达式将应用于IQueryable集合

我可以构建如下表达式:

[TestClass]
public class ExpressionTests
{
    private IQueryable<MyEntity> entities;

    private class MyEntity
    {
        public string MyProperty { get; set; }
    }

    [TestInitialize]
    public void Setup()
    {
        entities = new[]
                    {
                        new MyEntity {MyProperty = "first"}, 
                        new MyEntity {MyProperty = "second"}
                    }.AsQueryable();
    }

    [TestMethod]
    public void TestQueryingUsingSingleExpression()
    {
        Expression<Func<MyEntity, bool>> expression = e => e.MyProperty.Contains("irs");
        Assert.AreEqual(1, entities.Where(expression).Count());
    }
}
| Lambda / \ / \ Call Parameter z / | \ / | \ EndsWith | Constant | \ Property "5" / \ / \ z MyProperty
[TestClass]
公共类表达式测试
{
私人可转让实体;
私有类MyEntity
{
公共字符串MyProperty{get;set;}
}
[测试初始化]
公共作废设置()
{
实体=新[]
{
新MyEntity{MyProperty=“first”},
新MyEntity{MyProperty=“second”}
}.AsQueryable();
}
[测试方法]
使用SingleExpression()的公共void测试查询
{
Expression=e=>e.MyProperty.Contains(“irs”);
AreEqual(1,entities.Where(expression.Count());
}
}
现在我想把表达式的两部分分开:

[TestMethod]
public void TestQueryingByCombiningTwoExpressions()
{
    Expression<Func<MyEntity, string>> fieldExpression = e => e.MyProperty;
    Expression<Func<string, bool>> operatorExpression = e => e.Contains("irs");
    // combine the two expressions somehow...
    Expression<Func<MyEntity, bool>> combinedExpression = ???;

    Assert.AreEqual(1, entities.Where(combinedExpression).Count());
}
[TestMethod]
通过组合WoExpressions()进行的公共无效测试查询
{
表达式fieldExpression=e=>e.MyProperty;
表达式运算符Expression=e=>e.Contains(“irs”);
//以某种方式将这两个表达式组合起来。。。
表达式组合表达式=???;
AreEqual(1,entities.Where(combinedExpression.Count());
}
有没有关于我该怎么做的建议


顺便说一句,将解析表达式的提供程序是Linq for NHibernate。

查看两个表达式树:

| | Lambda Lambda / \ / \ / \ / \ Property Parameter x Call Parameter y / \ / | \ / \ / | \ x MyProperty EndsWidth y Constant | "5" | | 兰姆达兰姆达 / \ / \ / \ / \ 属性参数x调用参数y / \ / | \ / \ / | \ x MyProperty EndsWidth y常量 | "5" 您需要创建一个如下所示的新树:

[TestClass]
public class ExpressionTests
{
    private IQueryable<MyEntity> entities;

    private class MyEntity
    {
        public string MyProperty { get; set; }
    }

    [TestInitialize]
    public void Setup()
    {
        entities = new[]
                    {
                        new MyEntity {MyProperty = "first"}, 
                        new MyEntity {MyProperty = "second"}
                    }.AsQueryable();
    }

    [TestMethod]
    public void TestQueryingUsingSingleExpression()
    {
        Expression<Func<MyEntity, bool>> expression = e => e.MyProperty.Contains("irs");
        Assert.AreEqual(1, entities.Where(expression).Count());
    }
}
| Lambda / \ / \ Call Parameter z / | \ / | \ EndsWith | Constant | \ Property "5" / \ / \ z MyProperty | 兰姆达 / \ / \ 调用参数z / | \ / | \ EndsWith |常数 | \ 财产“5” / \ / \ Myz属性 您可以很容易地看到新树的哪些部分来自哪个原始树

要创建树,请获取第二个lambda表达式(调用)的主体,并将所有出现的
y
替换为第一个lambda表达式(属性)的主体,将所有出现的
x
替换为
z
。然后使用参数
z
将结果包装在一个新的lambda表达式中


可以使用重写树,使用创建新的lambda表达式。

这取决于提供程序支持的内容;如果它支持子表达式(LINQ到SQL,EF不支持;我不知道NH),那么:

var combinedExpression=Expression.Lambda(
Expression.Invoke(operatorExpression,fieldExpression.Body),
字段表达式(参数);

但是,如果没有,您将需要使用ExpressionVisitor来合并它们。

根据dtb和Marc的建议,我使用ExpressionVisitor来重写表达式树,这是我能做到的最干净的方法:

public class ExpressionBuilder<T> : ExpressionVisitor where T : class
{
    private Expression fieldExpressionBody;

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return fieldExpressionBody;
    }

    public Expression<Func<T, bool>> Build(
        Expression<Func<T, string>> fieldExpression,
        Expression<Func<string, bool>> operatorExpression)
    {
        fieldExpressionBody = fieldExpression.Body;
        Expression newExpressionBody = Visit(operatorExpression.Body);
        return Expression.Lambda<Func<T, bool>>(newExpressionBody, fieldExpression.Parameters[0]);
    }
}
公共类ExpressionBuilder:ExpressionVisitor其中T:class
{
私有表达式字段表达式体;
受保护的重写表达式VisitParameter(ParameterExpression节点)
{
返回字段表达式体;
}
公共表达构建(
表达式字段表达式,
表达式运算符(表达式)
{
fieldExpressionBody=fieldExpression.Body;
表达式newExpressionBody=Visit(operatiorexpression.Body);
返回表达式.Lambda(newExpressionBody,fieldExpression.Parameters[0]);
}
}
并在我的单元测试中使用它:

[TestMethod]
public void TestQueryingByCombiningTwoExpressions()
{
    Expression<Func<MyEntity, string>> fieldExpression = e => e.MyProperty;
    Expression<Func<string, bool>> operatorExpression = o => o.Contains("irs");

    var builder = new ExpressionBuilder<MyEntity>();
    Expression<Func<MyEntity, bool>> combinedExpression = builder.Build(fieldExpression, operatorExpression);
    Assert.AreEqual(1, entities.Where(combinedExpression).Count());
    Assert.AreEqual("e => e.MyProperty.Contains(\"irs\")", combinedExpression.ToString());
}
[TestMethod]
通过组合WoExpressions()进行的公共无效测试查询
{
表达式fieldExpression=e=>e.MyProperty;
表达式运算符Expression=o=>o.Contains(“irs”);
var builder=new ExpressionBuilder();
表达式combinedExpression=builder.Build(fieldExpression,operatorExpression);
AreEqual(1,entities.Where(combinedExpression.Count());
Assert.AreEqual(“e=>e.MyProperty.Contains(\“irs\”),combinedExpression.ToString();
}

已经完成,下面是一个可以使用2,3,n表达式的版本

public class ExpressionMerger : ExpressionVisitor
{
    Expression CurrentParameterExpression { get; set; }

    public Expression<Func<TIn, TOut>> Merge<TIn, TA, TOut>(Expression<Func<TIn, TA>> inner, Expression<Func<TA, TOut>> outer)
    {
        return MergeAll<TIn, TOut>(inner, outer);
    }

    public Expression<Func<TIn, TOut>> Merge<TIn, TA, TB, TOut>(Expression<Func<TIn, TA>> inner, Expression<Func<TA, TB>> transition, Expression<Func<TB, TOut>> outer)
    {
        return MergeAll<TIn, TOut>(inner, transition, outer);
    }

    protected Expression<Func<TIn, TOut>> MergeAll<TIn, TOut>(params LambdaExpression[] expressions)
    {
        CurrentParameterExpression = expressions[0].Body;

        foreach (var expression in expressions.Skip(1))
        {
            CurrentParameterExpression = Visit(expression.Body);
        }

        return Expression.Lambda<Func<TIn, TOut>>(CurrentParameterExpression, expressions[0].Parameters[0]);
    } 

    protected override Expression VisitParameter(ParameterExpression node)
    {
        //replace current lambda parameter with ~previous lambdas
        return CurrentParameterExpression;
    }
}
公共类expressionmerge:ExpressionVisitor
{
表达式CurrentParameterExpression{get;set;}
公共表达式合并(表达式内部、表达式外部)
{
返回所有(内部、外部);
}
公共表达式合并(表达式内部、表达式转换、表达式外部)
{
返回MergeAll(内部、过渡、外部);
}
受保护的表达式MergeAll(参数LambdaExpression[]表达式)
{
CurrentParameterExpression=表达式[0]。正文;
foreach(表达式中的var表达式。跳过(1))
{
CurrentParameterExpression=访问(expression.Body);
}
返回表达式.Lambda(CurrentParameterExpression,表达式[0]。参数[0]);
} 
受保护的重写表达式VisitParameter(ParameterExpression节点)
{
//将当前lambda参数替换为~previous lambda
返回CurrentParameterExpression;
}
}

}

谢谢dtb,很棒的图表,+1。我不认为您可以提供一个适用于我的示例代码的示例?我整理了我的示例代码,以便它可以作为独立的单元测试运行。当然可以。