c# - 如何在表达式中使用委托
问题描述
我正在努力理解如何构造一个使用委托的表达式。我是表达式的新手,令人尴尬的是无法创建一个单元测试来复制我看到的问题,所以希望下面的信息足以解释这个问题。
考虑以下类:
public class Instance
{
internal Instance(string value)
{
Value = value;
}
public string Value { get; }
public Expression<Func<Container, bool>> IsContainerMatch => c => Selector(c.Item) == Value;
public Func<Item, string> Selector => i => i.Value;
}
public class Container
{
internal Container(Item item)
{
Item = item;
}
public Item Item { get; }
}
public class Item
{
internal Item(string value)
{
Value = value;
}
public string Value { get; }
}
该IsContainerMatch
表达式被用作Instance
基于第三方方法的参数,并在以后编译/使用。但是,当实际调用表达式时,我收到一个错误,指出the variable
c is referenced from scope '' but is not defined
。如果我没记错的话,如果我可以将Selector
delegate合并到表达式中,就可以解决这个问题,使两者具有相同的范围。(如果我错了,请纠正我!)
我遇到了这个问题,但我看到的一个根本区别是我的代表的论点不是常数。它是在编译时确定的。我没有太多运气弄清楚如何为我的场景构建表达式。有人可以给我一点指导吗?
编辑:这是我可以构建的最简单的测试失败 - 抱歉,它不容易重复。仅当我尝试将表达式与 NHibernate 结合使用时才会出现此问题;当我使用@juharr 中的方法时,调用函数可以正常工作。当我将表达式注入 .And 语句时,我收到错误消息variable c of type Container referenced from scope '', but it is not defined
。
[Fact]
public void Test()
{
var instanceList = new Collection<Instance>{new Instance("test")};
var dbConfig = OracleDataClientConfiguration.Oracle10
.ConnectionString(c => c.Is("Data Source=(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = 10.11.12.13)(PORT = 1521))(CONNECT_DATA = (SERVER = DEDICATED)(SERVICE_NAME = orcl.test.com)));Password=test;User ID=test;"))
.Driver<NHibernate.Driver.OracleManagedDataClientDriver>();
FluentConfiguration configuration = Fluently.Configure().Database(dbConfig)
.Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()));
var sessionFactory = configuration.BuildSessionFactory();
var session = sessionFactory.OpenSession();
var query = session.QueryOver<Container>();
foreach (var element in instanceList) query.And(c => element.Selector(c.Item) == element.Value);
var query2 = session.QueryOver<Container>();
foreach (var element in instanceList) query.And(element.IsContainerMatch);
}
编辑#2:注意上面的query2;这是我的第二个用例。第一个查询是引发异常的查询,但我的意图是在第一个查询和第二个查询的表达式中重用委托。
解决方案
好的,让我们打开它;
public Expression<Func<Container, bool>> IsContainerMatch => c => Selector(c.Item) == Value;
IsContainerMatch 是一个属性,在每次获取时返回一个新的表达式实例。该表达式包含对实例的常量引用以访问值和选择器。这大致相当于;
public Expression<Func<Container, bool>> IsContainerMatch { get {
var inst = Expression.Constant(this);
var container = Expression.Parameter(typeof(Container), "c");
var selector = typeof(Instance).GetProperty("Selector");
var value = typeof(Instance).GetProperty("Value");
var item = typeof(Container).GetProperty("Item");
return Expression.Lambda<Func<Container, bool>>(
Expression.Equal(
Expression.Invoke(
Expression.MakeMemberAccess(inst, selector),
Expression.MakeMemberAccess(container, item)
),
Expression.MakeMemberAccess(inst, value)
),
container
);
} }
这不太可能是您的异常的真正来源。在其他地方构建了一个新的 LambdaExpression,可能是从这个 Expression 的片段中构建的,并引用了这个 ParameterExpression 'C'。但使用不同的参数。
例如,这样的事情可能会导致该异常;
...
return Expression.Lambda<Func<Container, bool>>(
Expression.Equal(
Expression.Invoke(
Expression.MakeMemberAccess(inst, selector),
Expression.MakeMemberAccess(Expression.Parameter(typeof(Container), "X"), item)
),
Expression.MakeMemberAccess(inst, value)
),
Expression.Parameter(typeof(Container), "Y")
);
显然,您正在尝试使用您的 3rd 方库不支持的表达式类型。
现在您已经更新了包含 NHibernate 的问题,看起来您想要实现的是;
foreach (var element in instanceList) query.And(c => c.Item.[Dynamic member] == element.Value);
以便 NHibernate 可以有效地评估标准。但是由于您的 Selector 是一个编译后的 Func,NHibernate 无法查看该方法的内部并将其转换为有效的查询。
推荐阅读
- vba - Word 宏,选择并附加到粗体文本
- c++ - 编译得很好,但是当它达到某个点时运行失败。我究竟做错了什么?
- javascript - 从顶部开始滚动 30px 的 div
- powershell - Flutter 更新导致错误“Flutter 需要 PowerShell 5.0 或更新版本”不可恢复
- python - Toplevel 中的 CheckButton 状态检查
- android - 谁能告诉我如何在 Firebase Firestore (Android) 中存储一组自定义对象
- yii2 - 如何重命名 yii\web\YiiAsset 文件名
- javascript - 悬停时的 Javascript 动画
- javascript - 在 iframe 网站中发送可变数据并提交
- c - 预处理器指令(#if 和 #endif)在 C 中不起作用