我在努力理解如何构造一个使用委托的表达式上有些费劲。我是新来的表达式用户,而且令人尴尬的是无法创建可复制我所见问题的单元测试,因此希望以下信息足以解释问题。
请考虑以下类:
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
委托合并到表达式中,使两者具有相同的范围,就可以解决此问题。 (如果我错了,请纠正我!)
我遇到了this问题,但我看到的一个根本区别是,我的代表的论点不是一个常数;它是在编译时确定的。我不太想办法为我的场景构造表达式。有人可以给我一些指导吗?
EDIT:这是我可以构造的最简单的失败测试-抱歉,它不容易重复。仅当我尝试将表达式与NHibernate结合使用时,才会出现此问题。当我使用@juharr中的方法时,调用func可以正常工作。当我将表达式注入.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);
}
EDIT#2:注意上面的查询2;这是我的第二个用例。第一个查询是引发异常的查询,但是我的目的是为第一个查询以及第二个查询的表达式重用委托。
好,让我们打开包装;
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
);
} }
这不太可能成为您例外情况的真实来源。可能已从此Expression的各个部分构造了一个新的LambdaExpression,并引用了此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")
);
很显然,您正在尝试使用您的第三方库不支持的一种表达式。
现在您已经更新了问题以包含NHibernate,似乎您想要实现的目标是]
foreach (var element in instanceList) query.And(c => c.Item.[Dynamic member] == element.Value);
因此,NHibernate可以有效地评估标准。但是,由于您的Selector是已编译的Func,因此NHibernate无法查看该方法的内部并将其转换为有效的查询。