2011-06-21 61 views
1

我在存儲庫模式中使用Linq-to-SQL和Unity。我試圖在存儲庫方法[Securable]IQueryable<TEntity> List<TEntity>()上添加一個對象安全攔截器,攔截該調用並僅返回用戶有權訪問的實體。LINQ to SQL with Unity Interception

public class SecurableAttribute : HandlerAttribute 
{...} 

public class SecurableHandler : ICallHandler 
{ 
    ... 
    IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) 
    { 
     var message = getNext()(input, getNext); 
     var returnType = message.ReturnValue.GetType(); 
     if (typeof(IQueryable).IsAssignableFrom(returnType)) 
     { 
      var entityType = returnType.GetGenericArguments().Single(); 
      var securableAttribute = entityType.GetAttribute<SecurableTypeAttribute>(); 
      if(securableAttribute != null) 
      { 
       //Build expression to filter the list from the attribute and primary key of the entity 
       //Return the new IQueryable 
      } 
     } 
     return message; 
    } 
} 

我已經建立了一個表情,但我不能這樣做,因爲message.ReturnValue.Where(expression)message.ReturnValueobjectmessage.ReturnValue實際上是一個System.Data.Linq.Table<TEntity>,但我不想太依賴於L2S),它是在因此我無法將其轉換回通用並替換爲message.ReturnValue

另外,我試過

public interface ISecurable<TKey> 
{ 
    TKey SecurityId { get; } 
} 
的實體,它鎖定我有點

,但我與確定,如果我能分開剩餘安全方面的問題。這使我在那裏我建立上述表達式做IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)如下:

if(typeof(ISecurableType).IsAssignableFrom(entityType)) 
{ 
    var secured = ((IQueryable<ISecurable>)message.ReturnValue).Where(expression); 
    //Need to return secured as IQueryably<TEntity> 
} 

我現在已經投securedIQueryable<ISecurable>typeof(IQueryable<TEntity>).IsAssignableFrom(secured.GetType())返回false,並換出的返回值拋出一個異常,但它似乎據我所知,可以延遲執行。 (另外,在設計時我不知道TEntity在SecurableHandler,但我確實知道反射類型 - 但我已嘗試使用類聲明,我知道它在測試中。)

有什麼方法可以修改不知何故返回結果?我被困在需要返回一個我不知道在設計時間的泛型,從而使這不可能,但我也不能修改表達式(((IQueryable)message.ReturnType).Expression被宣佈爲Expression Expression { get; })。

那裏有任何輝煌,可以指向我的方式有效嗎?

tl; dr需要返回IQueryable<TEntity>在運行時從一個object是一個Table<TEntity> : IQueryable<TEntity>與一個額外的.Where(expression)

回答

1

您可以嘗試在運行時創建動態表達式。你不應該明確地將IQueryable轉換回它的泛型類型,只要你不用「Select」來改變元素類型。

例子:

public class SecurityHandler : ICallHandler 
{ 
    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) 
    { 
     var message = getNext()(input, getNext); 
     var returnType = message.ReturnValue.GetType(); 
     if (typeof(IQueryable).IsAssignableFrom(returnType)) 
     { 
      var entityType = returnType.GetGenericArguments().Single(); 

      var securableAttribute = entityType.GetAttribute<SecurableTypeAttribute>(); 
      if (securableAttribute != null) 
      { 
       //Build expression to filter the list from the attribute and primary key of the entity 
       //Return the new IQueryable 
       message.ReturnValue = AddWhereExpression(
        (IQueryable)message.ReturnValue, 
        securableAttribute.FilterValues, 
        securableAttribute.FilterPropertyName); 
      } 
     } 
     return message; 
    } 

    public int Order { get; set; } 

    private static IQueryable AddWhereExpression(IQueryable query, IEnumerable ids, string filterPropertyName) 
    { 
     // Build this expression: 
     // item => ids.Contains(item.[PrimaryKeyPropertyName]) 

     var itemParameter = Expression.Parameter(query.ElementType, "item"); 

     var itemParameterProperty = Expression.Property(itemParameter, filterPropertyName); 

     var listParameter = Expression.Constant(ids); 

     var containsExpression = Expression.Call(
      typeof(System.Linq.Enumerable), 
      "Contains", 
      new[] { typeof(int) }, 
      listParameter, 
      itemParameterProperty); 

     var delegateTypeExpression = Expression.GetFuncType(new[] { query.ElementType, typeof(bool) }); 

     var whereExpression = Expression.Lambda(
      delegateTypeExpression, 
      containsExpression, 
      new[] { itemParameter } 
      ); 

     Expression callWhere = Expression.Call(
            typeof(Queryable), 
            "Where", 
            new Type[] { query.ElementType }, // type args for Where<T>() 
            query.Expression, 
            whereExpression 
            ); 

     return query.Provider.CreateQuery(callWhere); 
    } 
} 

我假設你的屬性將提供允許值的一些陣列。

這裏有一些擴展方法,這將有助於這一過程:

public static class TypeExtensions 
{  

    public static TAttribute GetAttribute<TAttribute>(this Type type) 
    { 
     var attributes = type.GetCustomAttributes(typeof(TAttribute), true); 
     if (attributes.Length == 0) return default(TAttribute); 
     return (TAttribute)attributes[0]; 
    }  

    public static PropertyInfo GetPropertyWithAttributeValue<TAttribute>(
     this IEnumerable<PropertyInfo> properties, 
     Func<TAttribute, bool> findPredicate) 
     where TAttribute : Attribute 
    { 
     var property = from p in properties 
         where p.HasAttribute<TAttribute>() && 
         findPredicate.Invoke(p.GetAttribute<TAttribute>()) 
         select p; 

     return property.FirstOrDefault(); 
    } 

    public static bool HasAttribute<TAttribute>(this PropertyInfo propertyInfo) 
    { 
     return propertyInfo.GetCustomAttributes(typeof(TAttribute), true).Any(); 
    } 

    public static TAttribute GetAttribute<TAttribute>(this PropertyInfo propertyInfo) 
    { 
     var attributes = propertyInfo.GetCustomAttributes(typeof(TAttribute), true); 
     if (attributes.Length == 0) return default(TAttribute); 
     return (TAttribute)attributes[0]; 
    } 
} 

我還沒有嘗試過這種運行自己,但我希望它足以讓你開始。

+0

這工作完美! – Brian

+0

好聽。樂於幫助。 –