如何在表达式中使用委托

问题描述 投票:1回答:1

我在努力理解如何构造一个使用委托的表达式上有些费劲。我是新来的表达式用户,而且令人尴尬的是无法创建可复制我所见问题的单元测试,因此希望以下信息足以解释问题。

请考虑以下类:

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;这是我的第二个用例。第一个查询是引发异常的查询,但是我的目的是为第一个查询以及第二个查询的表达式重用委托。

c# .net delegates expression-trees
1个回答
1
投票

好,让我们打开包装;

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无法查看该方法的内部并将其转换为有效的查询。

© www.soinside.com 2019 - 2024. All rights reserved.