如何在表达式中使用委托
How to consume a delegate within an expression
我有点难以理解如何构造使用委托的表达式。我是表达式的新手,令人尴尬的是无法创建一个单元测试来复制我所看到的问题,所以希望下面的信息足以解释这个问题。
考虑以下 类:
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
基础上用作参数,以后是 compiled/used。但是,当实际调用表达式时,我收到一条错误消息,指出 the variable
cis referenced from scope '' but is not defined
。如果我没记错的话,如果我可以将 Selector
委托合并到表达式中,使两者具有相同的作用域,这个问题就可以解决。 (如有错误请指正!)
我遇到了 this 问题,但我看到的一个根本区别是我的委托的参数不是常量;它是在编译时确定的。我没有多少运气来弄清楚如何为我的场景构建表达式。有人可以给我一些指导吗?
编辑:这是我能构建但失败的最简单的测试 - 抱歉,它不容易重复。只有当我尝试将表达式与 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);
}
编辑#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
);
} }
这不太可能是您的异常的真正来源。在其他地方构造了一个新的 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")
);
很明显,您正在尝试使用您的第 3 方库不支持的表达式类型。
既然您已经更新了问题以包含 NHibernate,那么您想要实现的目标似乎是;
foreach (var element in instanceList) query.And(c => c.Item.[Dynamic member] == element.Value);
以便 NHibernate 可以有效地评估标准。但是由于您的 Selector 是一个已编译的 Func,NHibernate 无法查看该方法内部并将其转换为有效的查询。
我有点难以理解如何构造使用委托的表达式。我是表达式的新手,令人尴尬的是无法创建一个单元测试来复制我所看到的问题,所以希望下面的信息足以解释这个问题。
考虑以下 类:
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
基础上用作参数,以后是 compiled/used。但是,当实际调用表达式时,我收到一条错误消息,指出 the variable
cis referenced from scope '' but is not defined
。如果我没记错的话,如果我可以将 Selector
委托合并到表达式中,使两者具有相同的作用域,这个问题就可以解决。 (如有错误请指正!)
我遇到了 this 问题,但我看到的一个根本区别是我的委托的参数不是常量;它是在编译时确定的。我没有多少运气来弄清楚如何为我的场景构建表达式。有人可以给我一些指导吗?
编辑:这是我能构建但失败的最简单的测试 - 抱歉,它不容易重复。只有当我尝试将表达式与 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);
}
编辑#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
);
} }
这不太可能是您的异常的真正来源。在其他地方构造了一个新的 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")
);
很明显,您正在尝试使用您的第 3 方库不支持的表达式类型。
既然您已经更新了问题以包含 NHibernate,那么您想要实现的目标似乎是;
foreach (var element in instanceList) query.And(c => c.Item.[Dynamic member] == element.Value);
以便 NHibernate 可以有效地评估标准。但是由于您的 Selector 是一个已编译的 Func,NHibernate 无法查看该方法内部并将其转换为有效的查询。