NHibernate + IQueryable access subtype property in where

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-12 01:41:51

问题


I've got problem with NHibernate v3.2.0, and following query:

class DocumentBase {}
class Requisition: DocumentBase {}
class Order: DocumentBase {}

Repository.GetAll<DocumentBase>()
    .Where(d => (d is Requisition) && ((Requisition)d).ProductItem != null)

Base query is designed to list all documents, but there is also possibility to filter this documents by type (and quasi-type, which is for example document without product). In code above there is only one condition, but predicate can be more complicated, for ex:

Repository.GetAll<DocumentBase>()
    .Where(d => 
        ((d is Requisition) && ((Requisition)d).ProductItem != null) ||
        (d is Order) ||
        [...]
    )

When executed I receive InvalidPathException with message Invalid path: 'd.ProductItem'. Any ideas? Is it supported?

So far I managed to bypass this error with following query:

Repository.GetAll<DocumentBase>()
    .Where(d => 
        (d is Requisition) &&
        Repository.GetAll<Requisition>()
            .Any(r => r.Id == d.Id && r.ProductItem != null)
    )

But definitely it's not the best choise in terms of performance.


回答1:


I guess that your Repository.GetAll<T> method is returning session.Query<T>. There is only a limited subset of expressions you can do have your Where clause. NHibernate tries to convert your Where expression to HQL, and eventually to SQL. This means that you cannot write anything in Where and expect it to work, but only those expressions for which the conversion exists. However, NHibernate's Linq provider can be extended.

Your case can be simplified. If you have your subclasses mapped... and you probably do, the query can be written as:

Repository.GetAll<Requisition>().Where(r => r.ProductItem != null);



回答2:


As someone mentioned it is possible to extend Linq provider in NHibernate. Here is solution which worked for me:

public static class OzirNhExtensions
{
    // Cast method to use in query
    public static TTarget Cast<TTarget>(this Object source)
    {
        return ((TTarget)source);
    }
}

class CastHqlGeneratorForMethod : BaseHqlGeneratorForMethod
{
    public CastHqlGeneratorForMethod()
    {
        this.SupportedMethods = new MethodInfo[] {
            ReflectionHelper.GetMethodDefinition(
                () => OzirNhExtensions.Cast<Object>(null)
            )
        };
    }

    // In here simply skip cast expression 
    // (it works probably because I have every sub-entity 
    // in the same table that base entity)
    public override HqlTreeNode BuildHql(
        MethodInfo method, 
        Expression targetObject, 
        ReadOnlyCollection<Expression> arguments, 
        HqlTreeBuilder treeBuilder,
        IHqlExpressionVisitor visitor)
    {
        return visitor.Visit(arguments[0]).AsExpression();
    }
}

Example query:

Repository.GetAll<DocumentBase>()
    .Where(d => d.Cast<Requisition>().ProductItem != null && 
        d.Cast<Requisition>().ProductItem.Name == "Something"
    )


来源:https://stackoverflow.com/questions/11860654/nhibernate-iqueryable-access-subtype-property-in-where

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!