Linq to SQL with Unity拦截

问题描述

| 我在存储库模式中将Linq-to-sql与Unity一起使用。我试图在存储库方法“ 0”上添加一个用于对象安全的拦截器,以拦截调用并仅返回用户有权访问的实体。
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.ReturnValue
object
message.ReturnValue
实际上是
System.Data.Linq.Table<TEntity>
,但是我不想太受L2S的束缚),并且它在运行时所以我可以\不要将其转换回通用名称并替换“ 3”。 或者,我尝试了
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>
}
现在,我必须将
secured
转换为
IQueryable<ISecurable>
,但
typeof(IQueryable<TEntity>).IsAssignableFrom(secured.GetType())
返回false,换出返回值会引发异常,但就我所知,它似乎确实可以延迟执行。 (此外,我在设计时不了解Entity14ѭ中的TEntity,但我确实知道反射类型-但是我尝试使用我知道它在测试中的类声明。) 有什么办法可以修改返回结果吗?我被迫需要返回设计时不知道的泛型,从而使之不可能,但我也无法修改表达式(
((IQueryable)message.ReturnType).Expression
被声明为
Expression Expression { get; }
)。 有没有能以一种可行的方式向我指出的辉煌? tl; dr在运行时需要从
object
(即
Table<TEntity> : IQueryable<TEntity>
和附加
.Where(expression)
)返回
IQueryable<TEntity>
。     

解决方法

        您可以尝试在运行时创建动态表达式。只要您不使用\“ Select \”更改元素类型,就不必显式将IQueryable强制转换回其通用类型。 例:
    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];
    }
}
我没有尝试自己运行此程序,但希望它足以使您入门。