将表达式传递给NHibernate中的方法会导致“ConstantExpression”类型的Object无法转换为“LambdaExpression”类型

时间:2011-01-19 16:56:20

标签: linq nhibernate subquery expression

NHibernate 2和3都会出现这个问题。我有一个A类,它有一个B类成员集。直接执行类很好地执行。但是当我将一个涉及B类的表达式传递给一个方法时,我得到以下错误:

System.ArgumentException:'System.Linq.Expressions.ConstantExpression'类型的对象无法转换为'System.Linq.Expressions.LambdaExpression'类型。

据我所知,我将完全相同的表达式传递给Any()方法。但出于某种原因,他们受到不同的待遇。我做了一些调试,看起来在第一个方法中,表达式被视为具有NodeType'Quote'的表达式,而第二个方法中的相同表达式似乎被视为具有NodeType'Constant'的表达式。第二种方法中表达式的父表达式具有NodeType“MemberAccess”。所以看起来表达式树在不同的测试方法中是不同的。我只是不明白为什么以及如何解决这个问题。

课程涉及:

public class A
{
   public virtual int Id { get; set; }
   public virtual ISet<B> DataFields { get; set; }
}

public class B
{
   public virtual int Id { get; set; }
}

示例测试代码:

    [TestMethod]
    public void TestMethod1()
    {
        using (ISession session = sessionFactory.OpenSession())
        {
            var records = session.Query<A>()
                                 .Where<A>(a => a.DataFields
                                                 .Any(b => b.Id == 1));
            Console.Write("Number of records is {0}", records.Count());
        }
    }

    [TestMethod]
    public void TestMethod2()
    {
        GetAsWhereB(b => b.Id == 1);
    }

    private void GetAsWhereB(Func<B, bool> where) 
    {
        using (ISession session = sessionFactory.OpenSession())
        {
            var records = session.Query<A>()
                                 .Where(a => a.DataFields
                                              .Any(where));
            Console.Write("Number of records is {0}", records.Count());
        }
    }

2 个答案:

答案 0 :(得分:5)

这是一个问题:

private void GetAsWhereB(Func<B, bool> where) 

这是一个委托 - 你想要一个表达式树,否则NHibernate无法参与其中。试试这个:

private void GetAsWhereB(Expression<Func<B, bool>> where)

顺便说一句,由于您使用了空格,您的查询很难阅读。我建议不要:

var records = session.Query<A>().Where<A>(a => a.DataFields.
                                 Any(b => b.Id == 1));

你明确表示“Any”调用是在DataFields上进行的:

var records = session.Query<A>().Where<A>(a => a.DataFields
                                                .Any(b => b.Id == 1));

我还建议您将参数名称从“where”更改为“whereExpression”或“predicate”。无论如何,某种名词:)

答案 1 :(得分:0)

不确定这是否是正确的解决方案。问题就像一个bug,我的解决方案就像一个解决方法。尽管如此,以下工作对我来说很有用,可以归结为通过使用其body和参数来构造新表达式来创建给定表达式的“副本”。

private void GetAsWhereB(Func<B, bool> where) 
{
    Expression<Func<T, bool>> w = Expression.Lambda<Func<T, bool>>(where.Body, where.Parameters);
    using (ISession session = sessionFactory.OpenSession())
    {
        var records = session.Query<A>()
                             .Where(a => a.DataFields
                                          .Any(w));
        Console.Write("Number of records is {0}", records.Count());
    }
}