NHibernate JoinAlias query with null test not working

…衆ロ難τιáo~ 提交于 2019-12-10 16:15:59

问题


I'm getting some unexpected behaviour with a JoinAlias QueryOver in NHibernate.

My entity essentially looks like this:

public class Field
{
    public virtual long Id { get; protected set; }
    public virtual Field Parent { get; protected set; }
    public virtual FieldType Type { get; protected set; }
    public virtual string Value { get; protected set; }

...(Ctors etc
}

My mapping is such:

public class FieldMap : ClassMap<Field>
{
    public FieldMap()
    {
        Id(x => x.Id)
            .GeneratedBy.Native();

        References(x => x.Type)
            .Column("FieldTypeId")
            .LazyLoad()
            .Cascade.All()
            ;

        Map(x => x.Value);

        References(x => x.Parent)
            .Column("ParentFieldId")
            .Nullable()
            .LazyLoad()
            .Cascade.All()
            ;
    }

My query:

        Field fieldAlias = null;
        string typeAlias = null;
        Field parentFieldAlias = null;

        var query = getSession().QueryOver<Field>(() => fieldAlias)
            .JoinAlias(() => fieldAlias.Type, () => typeAlias)
            .Where(() => typeAlias.Name == type)
            .JoinAlias(() => fieldAlias.Parent, () => parentFieldAlias)
            .Where(() => (parentFieldAlias.Value == parentValue) || (parentFieldAlias == null))
            ;

As far as I'm concerned, this should give me some SQL like this:

... WHERE (a.ParentFieldId == NULL) OR (a.ParentFieldId = c.FieldId AND c.Value = parentValue)

But I'm getting a null reference exception. (I assume when the alias is resolved and the Parent is null).

The exception details are:

System.NullReferenceException occurred
  HResult=-2147467261
  Message=Object reference not set to an instance of an object.
  Source=NHibernate
  StackTrace:
       at NHibernate.Criterion.ConstantProjection..ctor(Object value)
       at NHibernate.Criterion.Projections.Constant(Object obj)
       at NHibernate.Impl.ExpressionProcessor.FindMemberProjection(Expression expression)
       at NHibernate.Impl.ExpressionProcessor.ProcessSimpleExpression(Expression left, Expression right, ExpressionType nodeType)
       at NHibernate.Impl.ExpressionProcessor.ProcessSimpleExpression(BinaryExpression be)
       at NHibernate.Impl.ExpressionProcessor.ProcessBinaryExpression(BinaryExpression expression)
       at NHibernate.Impl.ExpressionProcessor.ProcessExpression(Expression expression)
       at NHibernate.Impl.ExpressionProcessor.ProcessOrExpression(BinaryExpression expression)
       at NHibernate.Impl.ExpressionProcessor.ProcessBinaryExpression(BinaryExpression expression)
       at NHibernate.Impl.ExpressionProcessor.ProcessExpression(Expression expression)
       at NHibernate.Impl.ExpressionProcessor.ProcessLambdaExpression(LambdaExpression expression)
       at NHibernate.Impl.ExpressionProcessor.ProcessExpression(Expression`1 expression)
       at NHibernate.Criterion.QueryOver`2.Add(Expression`1 expression)
       at NHibernate.Criterion.QueryOver`2.Where(Expression`1 expression)
       at NHibernate.Criterion.QueryOver`2.NHibernate.IQueryOver<TRoot,TSubType>.Where(Expression`1 expression)
       at Ismoos.Director.FieldOptionsQuery.Execute(Service service, String type, String parentValue) in D:\Work\Ismoos\Ismoos\Director\Ismoos.Director\FieldOptionsQuery.cs:line 31
  InnerException:

I've tried a few different ways, including:

            .JoinAlias(() => fieldAlias.Parent, () => parentFieldAlias)
            .Where(Restrictions.Or(Restrictions.On(() => fieldAlias.Parent).IsNotNull,
                Restrictions.On(() => parentFieldAlias.Value).IsLike(parentValue))))

but none of these work.

I have a workaround, by leaving out the restriction on the parent field value in the query, and performing a LINQ query after the QueryOver returns, like so:

        Field fieldAlias = null;
        string typeAlias = null;
        Field parentFieldAlias = null;

        var query = getSession().QueryOver<Field>(() => fieldAlias)
            .JoinAlias(() => fieldAlias.Type, () => typeAlias)
            .Where(() => typeAlias.Name == type)
            ;

        var list = query
        .List<Field>()
        ;

        return list
            .Where(x => (x.Parent == null) || (x.Parent.Value == parentValue))
            .ToList();

but this isn't as optimal as doing it in the QueryOver.

Any suggestions?


回答1:


The second solution with restrictions would do the job. There are two issues. We need a LEFT JOIN to Parent and I see a typo: IsNotNull should be IsNull to crrectly evaluate the OR statement:

Broken solution IsNotNull (and most likely inner join)

.JoinAlias(() => fieldAlias.Parent, () => parentFieldAlias)
.Where
(
  Restrictions.Or(
    Restrictions.On(() => fieldAlias.Parent).IsNotNull, // here
    Restrictions.On(() => parentFieldAlias.Value).IsLike(parentValue)))
)

Working OR with IsNull and LEFT JOIN:

.JoinAlias(() => fieldAlias.Parent, () => parentFieldAlias
           , NHibernate.SqlCommand.JoinType.LeftOuterJoin)) // left join for NULL
.Where
(
  Restrictions.Or(
    Restrictions.On(() => fieldAlias.Parent).IsNull, // this is what we need
    Restrictions.On(() => parentFieldAlias.Value).IsLike(parentValue))
);

The issue with the first solution is, that we cannot evaluate for null the floating/virtual object: parentFieldAlias

.Where(() => (parentFieldAlias.Value == parentValue) || (parentFieldAlias == null)) ;

We need to check the property of the owner: fieldAlias.Parent




回答2:


I had the similar problem and additional brackets between conditions

like

.Where(() => (parentFieldAlias.Value == parentValue) || (parentFieldAlias == null));

get things working for me.



来源:https://stackoverflow.com/questions/13672448/nhibernate-joinalias-query-with-null-test-not-working

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