C# 在表达式中使用func? 背景
我有一个测试的例子,它通过了,但是在管道中发生了一个错误,我不知道为什么。我想弄清楚到底发生了什么,但我对表达式构造是新手,不想做任何假设 这是一个搜索过滤机制。它使用ServiceStack的C# 在表达式中使用func? 背景,c#,lambda,delegates,func,
ormlite-servicestack,C#,Lambda,Delegates,Func,
ormlite Servicestack,我有一个测试的例子,它通过了,但是在管道中发生了一个错误,我不知道为什么。我想弄清楚到底发生了什么,但我对表达式构造是新手,不想做任何假设 这是一个搜索过滤机制。它使用ServiceStack的PredicateBuilder实现。我基本上有一个我传入的值列表,我希望它构造一个表达式树。我以前只使用Func完成了这项工作,但我意识到我需要使用Expression。糟糕透了 目标 搜索过滤器由可重复使用的搜索过滤器类型构建而成,它由Funcs和Expressions构建而成,允许我从一个对象传入一
PredicateBuilder
实现。我基本上有一个我传入的值列表,我希望它构造一个表达式树。我以前只使用Func
完成了这项工作,但我意识到我需要使用Expression
。糟糕透了
目标
搜索过滤器由可重复使用的搜索过滤器类型构建而成,它由Func
s和Expression
s构建而成,允许我从一个对象传入一个字段名以及我应该匹配的值,最后得到可以运行Where()
语句的内容
代码/问题
我正在尝试的通用“nullable bool”筛选器--设置可接受的项并返回一个用于帮助筛选的func:
public class NullableBoolFilter : IGenericSearchFilter<bool?>
{
public Func<bool?, bool> GetFilterFunc(string valuesToProcess)
{
var acceptableValues = new List<bool?>();
if (string.IsNullOrWhiteSpace(valuesToProcess))
{
// all values acceptable
acceptableValues = new List<bool?>{true, false, null};
}
else
{
if (!valuesToProcess.Contains("0") && !valuesToProcess.Contains("1"))
{
throw new ArgumentException("Invalid Nullable boolean filter attribute specified");
}
if (valuesToProcess.Contains("0"))
{
acceptableValues.Add(false);
}
if (valuesToProcess.Contains("1"))
{
acceptableValues.Add(true);
}
}
Func<bool?, bool> returnFunc = delegate(bool? item) { return acceptableValues.Any(x=>x == item); };
return returnFunc;
}
}
以下测试通过:
public class ClaimsReportIsMDLFilterTests
{
// ReSharper disable InconsistentNaming
private readonly vSEARCH_ClaimsReport ItemWithMDL = new vSEARCH_ClaimsReport { IsMDL = true };
private readonly vSEARCH_ClaimsReport ItemWithoutMDL = new vSEARCH_ClaimsReport { IsMDL = false };
private readonly vSEARCH_ClaimsReport ItemWithNullMDL = new vSEARCH_ClaimsReport { IsMDL = null };
// ReSharper restore InconsistentNaming
[Fact]
public void WithSearchValueOf1_HidesNonMDLAndNull()
{
var sut = this.GetCompiledExpressionForValues("1");
sut.Invoke(ItemWithMDL).Should().BeTrue();
sut.Invoke(ItemWithoutMDL).Should().BeFalse();
sut.Invoke(ItemWithNullMDL).Should().BeFalse();
}
private Func<vSEARCH_ClaimsReport, bool> GetCompiledExpressionForValues(string searchValue)
{
return new ClaimsReportIsMDLFilter().GetExpression(searchValue).Compile();
}
}
公共类索赔报告DLFilterTests
{
//ReSharper禁用不一致命名
私有只读vSEARCH_ClaimsReport ItemWithMDL=新vSEARCH_ClaimsReport{IsMDL=true};
私有只读vSEARCH_ClaimsReport Itemwithout MDL=new vSEARCH_ClaimsReport{IsMDL=false};
私有只读vSEARCH_ClaimsReport ItemWithNullMDL=新vSEARCH_ClaimsReport{IsMDL=null};
//ReSharper还原不一致的命名
[事实]
搜索值为1_HidesNonMDLAndNull()的public void
{
var sut=this.GetCompiledExpressionForValues(“1”);
sut.Invoke(ItemWithMDL.Should().BeTrue();
调用(ItemWithoutMDL.Should().BeFalse();
调用(ItemWithNullMDL.Should().BeFalse();
}
private Func GetCompiledExpressionForValues(字符串搜索值)
{
返回新的ClaimsReportsMDLFILTER().GetExpression(searchValue).Compile();
}
}
问题
当我实际尝试运行此操作时,收到错误:
从作用域“”引用了类型为“vSEARCH_ClaimsReport”的变量“param”,但未定义该变量
我理解为什么会发生这种情况——在对其求值时,我没有真正的对象传递到Func
。然而,我不明白为什么我的测试可能会通过,但在实际使用中却没有
问题
- 为什么我的测试可能通过,但我仍然收到此错误
- 我该怎么着手解决这个问题呢
- 有没有一种简单的方法可以将
转换成Func
,我可以将字段传递到其中李>表达式
- 我是否需要放弃通用过滤器的想法,让每个类根据传入的输入手动将表达式添加到
?这是可行的,但看起来工作量可以减少很多PredicateBuilder
表达式
,而不是生成一个Func
,然后创建一个调用它的表达式
有没有一种简单的方法可以将Func转换成一个表达式,我可以将一个字段传递给它
不需要。您需要提取函数的IL代码,将其反编译为C代码,然后构建Expression
对象来表示该代码。这几乎不会发生
您几乎需要GetFilterFunc
返回一个表达式
,才能使其正常工作。幸运的是,考虑到您所拥有的,这很容易做到。您只需更改方法签名,并将最后两行替换为以下内容:
return item => acceptableValues.Any(x => x == item);
瞧。lambda可以根据上下文编译成表达式
对象,而不是委托,因此如果方法的返回类型是表达式
,那么您将得到它
现在,在GetExpression
中使用它。首先,PredicateBuilder
实际上什么都没有做。在表达式中添加一个或FALSE不会改变任何有意义的内容。所有这些都可以去。剩下的就是使用一个表达式
,并通过拉出一个布尔属性将其更改为表达式
。要做到这一点,表达式比代理的工作量要多一些。我们需要做更多的工作来编写它们,而不仅仅是调用表达式。我们将要编写一个方法来执行此操作:
public static Expression<Func<TFirstParam, TResult>>
Compose<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
这样做的目的是用第一个表达式的主体替换第二个表达式参数的所有实例,有效地将该表达式内联到第二个表达式中。剩下的只是用一个新的单个参数替换所有参数,并将其包装回lambda
现在我们有了这些,我们的方法非常简单:
public Expression<Func<vSEARCH_ClaimsReport, bool>> GetExpression(
string valuesToProcess)
{
Expression<Func<vSEARCH_ClaimsReport, bool?>> selector =
item => item.IsMDL;
return selector.Compose(base.GetFilterFunc(valuesToProcess));
}
公共表达式GetExpression(
字符串值(停止进程)
{
表达式选择器=
信息技术
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
public Expression<Func<vSEARCH_ClaimsReport, bool>> GetExpression(
string valuesToProcess)
{
Expression<Func<vSEARCH_ClaimsReport, bool?>> selector =
item => item.IsMDL;
return selector.Compose(base.GetFilterFunc(valuesToProcess));
}