通过反射从某些给定字符串创建强类型LINQ查询的最佳方法是什么

Meh*_*iny 8 c# linq reflection design-patterns entity-framework-5

我正在使用EF5,工作单元和存储库模式.我想为访问指定用户的数据定义一些限制.在数据库中,我设计了一个表来保存我的实体名称及其属性(称为EntityProperties),另一个表来保存这些属性的值,这些属性称为PropertyValues,每个EntityProperty都有一个或多个PropertyValues.在用户请求数据的业务层中,如果为他定义了任何限制,则应在linq语句中添加一些条件.我所做的是通过'userId'获取实体名称及其属性和值的列表,然后我在linq查询中添加一些'Where'子句.但是,实体名称及其属性的类型为"String",因此我应该使用Reflection来管理它们.但我不知道这一部分,我不知道 知道如何从给定的条件字符串集创建LINQ where子句.例如,假设用户请求列表顺序,用户ID为5.我首先查询这些访问限制表,结果是:

"订单","Id","74"

"秩序","Id","77"

"订单","Id","115"

这意味着该用户只能看到这三个订单,而在Orders表中,我们有更多的订单.所以,如果我想使用LINQ查询来获取订单,例如:

var orders = from order in Context.Orders
Run Code Online (Sandbox Code Playgroud)

我需要把它变成这样的东西:

var orders = from order in Context.Orders
Run Code Online (Sandbox Code Playgroud)

//其中订单ID应为74,77,115

但是,从"Order"和"Id"字符串到Order实体和Id属性需要反射.因此有两个问题:

从字符串中获取强类型的最佳方法是什么?我有更好的方法来做到这一点,表现更好吗?

Rap*_*aus 2

好的。通过注释,我们可能会采用类似的方法(假设您在EntityProperties表中有一个导航属性,它是 , 的集合PropertyValues,并命名PropertyValueList(如果没有,只需进行连接而不是使用Include)。

这是一些示例代码,非常质朴,仅适用于 Int32 属性,但这可能是解决方案的开始。

您还可以查看PredicateBuilder ...

反正

我使用“中级”过滤器。

public class Filter
    {
        public string PropertyName { get; set; }
        public List<string> Values { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)

然后是一个辅助类,它将返回一个IQueryable<T>,但是...已过滤

public static class FilterHelper {

    public static IQueryable<T> Filter(this IQueryable<T> queryable, Context context, int userId) {
        var entityName = typeof(T).Name;
        //get all filters for the current entity by userId, Select the needed values as a `List<Filter>()`
        //this could be done out of this method and used as a parameter
        var filters = context.EntityProperties
                      .Where(m => m.entityName == entityName && m.userId = userId)
                      .Include(m => m.PropertyValueList)
                      .Select(m => new Filter {
                          PropertyName = m.property,
                          Values = m.PropertyValueList.Select(x => x.value).ToList()
                      })
                      .ToList();

        //build the expression
        var parameter = Expression.Parameter(typeof(T), "m");

        var member = GetContains( filters.First(), parameter);
        member = filters.Skip(1).Aggregate(member, (current, filter) => Expression.And(current, GetContains(filter, parameter)));
        //the final predicate
        var lambda = Expression.Lambda<Func<T, bool>>(member, new[] { parameter });
        //use Where with the final predicate on your Queryable
        return queryable.Where(lambda);
    }

//this will build the "Contains" part
private static Expression GetContains(Filter filter, Expression expression)
    {
        Expression member = expression;
        member = Expression.Property(member, filter.PropertyName);
        var values = filter.Values.Select(m => Convert.ToInt32(m));

        var containsMethod = typeof(Enumerable).GetMethods().Single(
            method => method.Name == "Contains"
                      && method.IsGenericMethodDefinition
                      && method.GetParameters().Length == 2)
                      .MakeGenericMethod(new[] { typeof(int) });
        member = Expression.Call(containsMethod, Expression.Constant(values), member);
        return member;
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

var orders = from order in Context.Orders
             select order;

var filteredOrders = orders.Filter(Context, 1);//where 1 is a userId
Run Code Online (Sandbox Code Playgroud)