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

Linq 将表达式传递给NHibernate中的方法会导致“ConstantExpression”类型的对象无法转换为“LambdaExpression”类型,linq,nhibernate,subquery,expression,Linq,Nhibernate,Subquery,Expression,此问题在NHibernate 2和3中都会出现。我有一个类a,它的成员集是类B。直接查询这些类可以很好地执行。但是,当我将一个涉及类B的表达式传递给一个方法时,我得到以下错误: System.ArgumentException:无法将“System.Linq.Expressions.ConstantExpression”类型的对象转换为“System.Linq.Expressions.LambdaExpression”类型 据我所知,我正在将完全相同的表达式传递到Any方法中。但由于某些原因,他

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

System.ArgumentException:无法将“System.Linq.Expressions.ConstantExpression”类型的对象转换为“System.Linq.Expressions.LambdaExpression”类型

据我所知,我正在将完全相同的表达式传递到Any方法中。但由于某些原因,他们受到不同的对待。我已经做了一些调试,在第一个方法中,表达式被视为带有NodeType“Quote”的表达式,而在第二个方法中,相同的表达式似乎被视为带有NodeType“Constant”的表达式。第二个方法中表达式的父表达式具有节点类型“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());
        }
    }
这是一个问题:

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调用是在数据字段上进行的:

var records = session.Query<A>().Where<A>(a => a.DataFields
                                                .Any(b => b.Id == 1));
我还建议您将参数名称从where改为whereExpression或predicate之类的名称。无论如何,某种名词:

这是一个问题:

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调用是在数据字段上进行的:

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

我还建议您将参数名称从where改为whereExpression或predicate之类的名称。无论如何,某种名词:

不太确定这是否是正确的解决方案。这个问题感觉像一个bug,我的解决方案感觉像是一个变通方法。尽管如此,下面的内容对我来说还是有用的,它可以归结为通过使用给定表达式的主体和参数构造新表达式来创建给定表达式的“副本”

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());
    }
}

不太确定这是否是正确的解决方案。这个问题感觉像一个bug,我的解决方案感觉像是一个变通方法。尽管如此,下面的内容对我来说还是有用的,它可以归结为通过使用给定表达式的主体和参数构造新表达式来创建给定表达式的“副本”

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());
    }
}

谢谢乔恩的回答。不用讨论空格和命名约定,我可以告诉你我已经尝试了@user581780:啊,我没有发现数据字段只是一组。我怀疑您需要改变NHibernate对数据字段的思考方式——是否有类似EntitySet的东西实现IQueryable?如果希望NHibernate将谓词转换为SQL,它必须是表达式树。是数据字段是Iesi.Collections.Generic.ISet集合。你是说这个集合应该是一个实现IQueryable的集合类型?首先,我不知道应该使用哪个类/接口。有什么想法吗?第二,我仍然不明白为什么第一种方法是有效的。看起来我正在向Any扩展方法传递一个参数,就像我在第二个方法上做的那样。@user581780:可能是NHibernate特有的。请注意,这两种情况之间存在差异——您在其中一种情况下使用的是可计算的,而在另一种情况下使用的是不可计算的。这有关系吗?嗨,Jon,一个可计算的调用是不相关的,我已经从我的代码示例中删除了它,并修复了可读性!,因此,就我所知,这两者之间并没有什么区别。你知道为什么DataFields.AsQueryable不适合我吗?这是NH特有的还是概念上的错误?任何来自NH导师的话都将不胜感激。谢谢Jon的回答。不用讨论空格和命名约定,我可以告诉你我已经尝试了@user581780:啊,我没有发现数据字段只是一组。我怀疑您需要改变NHibernate对数据字段的思考方式——是否有类似EntitySet的东西实现IQueryable?如果希望NHibernate将谓词转换为SQL,它必须是表达式树。是数据字段是Iesi.Collections.Generic.ISet集合。你是说这个集合应该是一个实现IQueryable的集合类型?首先,我不知道应该使用哪个类/接口。有什么想法吗?第二,我仍然不明白为什么第一种方法是有效的。看起来我正在向Any扩展方法传递一个参数,就像我在第二个方法上做的那样。@user581780:可能是NHibernate特有的。注意这里有
这两种情况之间有区别——一种情况下使用的是可计算的,而另一种情况下使用的是不可计算的。这有关系吗?嗨,Jon,一个可计算的调用是不相关的,我已经从我的代码示例中删除了它,并修复了可读性!,因此,就我所知,这两者之间并没有什么区别。你知道为什么DataFields.AsQueryable不适合我吗?这是NH特有的还是概念上的错误?NH guru中的任何单词都会受到欢迎。T泛型参数从表达式w…中的哪里来。。。?您是否打算使用B而不是表达式w…中的T泛型参数来自何处。。。?你打算用B来代替吗