2011-06-21 60 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

好听。乐于帮助。 –