4 votes

La requête NHibernate JoinAlias avec test null ne fonctionne pas

J'obtiens un comportement inattendu avec un QueryOver JoinAlias dans NHibernate.

Mon entité ressemble essentiellement à ceci :

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
}

Ma cartographie est telle :

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

Ma question :

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

En ce qui me concerne, cela devrait me donner un SQL comme celui-ci :

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

Mais j'obtiens une exception de référence nulle. (Je suppose que lorsque l'alias est résolu et que le Parent est nul).

Les détails de l'exception sont :

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:

J'ai essayé plusieurs méthodes différentes, notamment :

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

mais aucune d'entre elles ne fonctionne.

J'ai trouvé une solution de contournement, en laissant de côté la restriction sur la valeur du champ parent dans la requête, et en effectuant une requête LINQ après le retour de QueryOver, comme suit :

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

mais ce n'est pas aussi optimal que de le faire dans le QueryOver.

Des suggestions ?

7voto

Radim Köhler Points 26836

La deuxième solution avec des restrictions ferait l'affaire. Il y a deux problèmes. Nous avons besoin d'une JOINT GAUCHE à Parent et je vois une faute de frappe : IsNotNull devrait être IsNull pour évaluer correctement le OR déclaration :

Solution cassée IsNotNull (et très probablement la jointure interne)

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

Travail OR avec IsNull et 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))
);

Le problème avec la première solution est que nous ne pouvons pas évaluer pour nullité le objet flottant/virtuel : parentFieldAlias

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

Nous devons vérifier la propriété du propriétaire : fieldAlias.Parent

1voto

J'ai eu le même problème et des parenthèses supplémentaires entre les conditions.

comme

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

pour que les choses fonctionnent pour moi.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X