【发布时间】:2014-05-07 00:05:05
【问题描述】:
背景
我有一个测试通过但在管道中发生错误的示例,我不知道为什么。我想弄清楚发生了什么,但我是表达式构造的新手,不想做任何假设。
这是一种搜索过滤机制。它使用 ServiceStack 的 PredicateBuilder 实现。我基本上有一个我传入的值列表,我希望它构造一个表达式树。我之前只使用Func<T<bool>> 完成了此操作,但意识到我需要使用Expression<Func<T<bool>>> 结束。无赖。
目标
从可重复使用的搜索过滤器类型构建的搜索过滤器,它由Funcs 和Expressions 构建而成,允许我从对象传递字段名称以及我应该匹配并结束的值我们可以运行 Where() 声明的东西。
代码/问题
我正在尝试的通用“可空布尔”过滤器——设置可接受的项目并返回一个用于帮助过滤的函数:
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;
}
}
然后我有另一个过滤器,它继承自 NullableBoolFilter 并尝试使用 Func:
public class ClaimsReportIsMDLFilter : NullableBoolFilter, ISearchFilter<vSEARCH_ClaimsReport>
{
public Expression<Func<vSEARCH_ClaimsReport, bool>> GetExpression(string valuesToProcess)
{
var theFunc = base.GetFilterFunc(valuesToProcess);
Expression<Func<vSEARCH_ClaimsReport, bool>> mdlMatches = item => theFunc(item.IsMDL);
var predicate = PredicateBuilder.False<vSEARCH_ClaimsReport>();
predicate = predicate.Or(mdlMatches);
return predicate;
}
}
以下测试通过:
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();
}
}
问题
当我实际尝试运行它时,我收到错误:
从范围“”引用的“vSEARCH_ClaimsReport”类型的变量“参数”,但未定义
这对我来说是有道理的——在评估它时,我没有真正的对象可以传递给Func。但是,我很困惑为什么我的测试可能会通过,但这并没有在实际使用中。
问题
- 为什么我的测试可能通过但我仍然收到此错误?
- 我应该如何着手解决这个问题?
- 有没有一种远程简单的方法可以将
Func转换为Expression,我可以将字段传递到其中? - 我是否需要放弃通用过滤器的想法并让每个类根据传入的输入手动将表达式添加到
PredicateBuilder?这是可行的,但似乎可以进一步减少工作量。
【问题讨论】:
-
您的
NullableBoolFilter似乎围绕着委托,而您的ClaimsReportIsMDLFilter围绕着表达式树。这几乎肯定是问题所在。如果您想使用表达式树,则需要始终如一地使用它们——只要您有一个引用某个本地Func<>的表达式树,就无法将其转换为 SQL(或其他) . -
@JonSkeet 感谢(快速!)响应。这段代码目前正在运行:gist.github.com/SeanKilleen/0a8ffa639c4916af0585 考虑到这一点,有没有什么方法可以创建我可以传入一个可为空的 bool 字段并让它每次都基于相同的过程进行评估的东西?或者,一旦我使用了表达方式,我就会陷入这种困境吗?我有几个类似的过滤器,所以只是想统一处理并尽可能保持 DRY。
标签: c# lambda delegates func ormlite-servicestack