2017-09-06 69 views
0

我爲從db獲取的實體寫入訪問權限檢查器。 我宣佈基地規則類:NHibernate和運行時表達式

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

這是把表達式幫手>表達式>:

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

現在,例如一個規則,一個項目:

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

現在我應用我的表達式(包裝到規範類)查詢到數據庫。我基於此代碼從表達式(如Expression>,Expression>,MethodCallExpression和UnaryExpression)中提取所有條件,並將其應用於查詢<>方法https://github.com/rjperes/DevelopmentWithADot.NHibernateSpecifications/blob/master/DevelopmentWithADot.NHibernateSpecifications/SpecificationExtensions.cs

>

我檢查結果表達式編譯爲Func並調用數據庫上的每個項目。一切都好。 但後來我執行查詢真正的連接,我在NHibertante核心代碼捕獲NullReferenceException。

調查後,我發現在NHibernate.Linq.Visitors.ExpressionKeyVisitor.VisitConstantExpression方法產生的異常,因爲_constantToParameterMap爲空,但沒有任何檢查使用:

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

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

空從NHibernate.Linq.Visitors.WhereJoinDetectors通過。 VisitMemberExpression方法

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

我使用的NHibernate 4.1.1

可以anybod Ÿ幫助解決這個問題?

更新1 這是結果表達式的調試視圖:

.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) 
} 

更新2 經過一些實驗,我發現只有當我嘗試內部規則接入用戶......也就是說,如果發生錯誤我用表達式替換檢查,其中不包含對用戶的訪問所有工作正常。所以問題是在不斷的表達,但我不明白爲什麼以及如何解決它

+0

讀題的題目,我想起了「哈利波特與魔法石」。爲了獲得更好的答覆,請考慮重新改寫標題。 –

回答

0

好吧,最後我找到了解決方案。我沒有發現任何此行爲的原因... 首先,我是檢查表達式由我的代碼和等效表達式編寫的代碼中生成的。主要區別在於我的表達式包含調用其他表達式,其調用其他表達式(我們需要深入),但在編碼表達式中沒有嵌套項。

所以我寫了一些expressionvisitor:第一個用於改變表達式的參數並將其轉換爲表達式中的原始類型,第二個用於將表達式的參數替換爲常量。

現在都完美了!