2017-09-06 3 views
0

J'écris le vérificateur d'autorisation d'accès pour l'entité obtenir à partir de DB. J'ai déclaré la classe de règle de base:NHibernate et l'expression d'exécution

abstract class AccessRule 
{ 
    private Func<object, Entities.User, bool> _canViewCompiledExpression; 

    public abstract Expression<Func<object, Entities.User, bool>> RawCanEditExpression { get; } 
    public abstract Type ObjectType { get; } 
    public Expression<Func<object, bool>> CreateCanViewExpression(AccessContext accessContext, Entities.User user) 
    { 
     var entityArgument = Expression.Parameter(typeof(object), "arg1"); 
     var userExpression = Expression.Constant(user, typeof(Entities.User)); 
     var body = Expression.Invoke(RawCanViewExpression, entityArgument, userExpression); 

     return (Expression<Func<object, bool>>)Expression.Lambda(body, entityArgument); 
    } 
    } 

    abstract class AccessRule<T> : AccessRule 
    where T : Entity 
    { 
    public override Type ObjectType => typeof(T); 

    public abstract Expression<Func<T, Entities.User, bool>> CanViewExpression { get; } 
    public override Expression<Func<object, Entities.User, bool>> RawCanViewExpression => ExpressionHelper.BoxExpression(CanViewExpression);  
    } 

Et ceci est aide pour convertir expression> à l'expression>:

public static Expression<Func<object, Entities.User, bool>> BoxExpression<T>(Expression<Func<T, Entities.User, bool>> expression) 
{ 
    var objParameter = Expression.Parameter(typeof(object), "arg1"); 
    var userParameter = Expression.Parameter(typeof(Entities.User), "arg2"); 

    var convertExpression = Expression.Convert(objParameter, typeof(T)); 

    var body = Expression.Invoke(expression, convertExpression, userParameter); 
    var result = Expression.Lambda<Func<object, Entities.User, bool>>(body, objParameter, userParameter); 

    return result; 
} 

maintenant, par exemple une règle, pour un article:

public override Expression<Func<Entities.Order, Entities.User, bool>> CanViewExpression 
     => (obj, user) => obj.Owner == user; 

maintenant j'applique mon expression (enveloppé à la classe de spécification) pour interroger DB. J'extrais toutes les conditions de l'expression (Expression>, Expression>, MethodCallExpression et UnaryExpression) en fonction de ce code https://github.com/rjperes/DevelopmentWithADot.NHibernateSpecifications/blob/master/DevelopmentWithADot.NHibernateSpecifications/SpecificationExtensions.cs

et de l'appliquer à la méthode Query <>.

Je vérifie l'expression des résultats en la compilant à Func et invoque sur chaque élément de la base de données. tout va bien. mais alors j'exécute une requête sur une connexion réelle j'attrape NullReferenceException dans le code de base NHibertante.

après enquête i constaté que exception générée dans la méthode NHibernate.Linq.Visitors.ExpressionKeyVisitor.VisitConstantExpression car _constantToParameterMap est nulle mais utilisé sans aucun contrôle:

protected override Expression VisitConstantExpression(ConstantExpression expression) 
{ 
    NamedParameter param; 

    if (_constantToParameterMap.TryGetValue(expression, out param) && insideSelectClause == false) 

nulle a été transmise à partir de NHibernate.Linq.Visitors.WhereJoinDetectors. méthode VisitMemberExpression

var key = ExpressionKeyVisitor.Visit(expression, null); 

I utilisé NHibernate 4.1.1

peut tout le mon y aider avec ce problème?

MISE À JOUR 1 c'est une vue de débogage d'expression de résultat:

.Lambda #Lambda1<System.Func`2[System.Object,System.Boolean]>(System.Object $arg1) { 
    .Invoke (.Lambda #Lambda2<System.Func`3[System.Object,Project.Entities.User,System.Boolean]>)(
     $arg1, 
     .Constant<Project.Entities.User>(Project.Entities.User)) 
} 

.Lambda #Lambda2<System.Func`3[System.Object,Project.Entities.User,System.Boolean]>(
    System.Object $arg1, 
    Project.Entities.User $arg2) { 
    .Invoke (.Lambda #Lambda3<System.Func`3[Project.Entities.Order,Project.Entities.User,System.Boolean]>)(
     (Project.Entities.Windfarm)$arg1, 
     $arg2) 
} 

.Lambda #Lambda3<System.Func`3[Project.Entities.Order,Project.Entities.User,System.Boolean]>(
    Portal.Domain.Entities.Windfarm $obj, 
    Portal.Domain.Entities.User $user) { 
    .Call($obj.Owner).Equeal($user) 
} 

MISE À JOUR 2 après quelques expériences que j'ai trouvé que l'erreur a eu lieu seulement si j'essaie utilisateur d'accès à l'intérieur de règle ... à savoir si je remplace vérifier avec l'expression, qui ne contient pas l'accès à l'utilisateur tout fonctionne bien. donc le problème est dans l'expression constante, mais je ne peux pas comprendre pourquoi et comment le résoudre

+0

En lisant le titre de la question, je me suis souvenu de "Harry Potter et la pierre du sorcier". Pour de meilleures réponses, pensez à reformuler le titre. –

Répondre

0

ok, enfin j'ai trouvé la solution. Je n'ai pas trouvé de raison pour ce comportement ... d'abord, je l'étais était l'expression de vérification qui a généré par mon code et l'expression équivalente écrite dans le code. différence principale que mon expression contient invoquer d'autre expression, qui invoquent l'autre expression (nous devons aller plus loin), mais dans l'expression codée pas d'éléments imbriqués. Donc j'écris un peu expressionvisitor: d'abord pour changer l'argument de l'expression et le convertir en type original dans l'expression et ensuite pour remplacer le paramètre de l'expression par la constante.

et maintenant tout fonctionne parfaitement!