簡體   English   中英

通過反射從某些給定字符串創建強類型LINQ查詢的最佳方法是什么

[英]What is the best way to create strongly typed LINQ queries from some given strings, via reflection

我正在使用EF5,工作單元和存儲庫模式。 我想為指定用戶訪問數據定義一些限制。 在數據庫中,我設計了一個表來保存我的實體名稱和它們的屬性,稱為EntityProperties,並設計了另一個表來保存這些屬性的值,稱為PropertyValues,每個EntityProperty具有一個或多個PropertyValues。 在業務層中,當用戶請求數據時,如果為其定義了任何限制,則應在linq語句中添加一些條件。 我要做的是通過“ userId”獲取實體名稱及其屬性和值的列表,然后在linq查詢中添加一些“ Where”子句。 但是,實體名稱及其屬性的類型為“字符串”,因此我應該使用反射來管理它們。 但是我不知道這部分,也不知道如何從給定的條件字符串集中創建LINQ where子句。 例如,假設用戶請求列表訂單,用戶ID為5。我首先查詢那些訪問限制表,結果是:

“訂單”,“編號”,“ 74”

“訂單”,“編號”,“ 77”

“訂單”,“編號”,“ 115”

這意味着該用戶應該只看到這三個訂單,而在“訂單”表中,我們有更多的訂單。 因此,如果我想使用LINQ查詢來獲取訂單,例如:

var orders = from order in Context.Orders

我需要將其變成類似:

var orders = from order in Context.Orders

//訂單編號應位於74,77,115

但是,從“ Order”和“ Id”字符串獲取Order實體和Id屬性需要反思。 因此有兩個問題:

從字符串獲得強類型的最佳方法是什么? 我有更好的方法來執行此操作嗎?

好。 有了這些注釋,我們可能會采用類似的方法(假設您在EntityProperties表中有一個導航屬性,該PropertyValuesPropertyValues的集合,並命名為PropertyValueList (如果沒有,則只需進行聯接而不是使用Include )。

這是一些示例代碼,確實很粗糙, 僅與Int32屬性一起使用 ,但這可能是解決方案的開始。

您還可以查看PredicateBuilder ...

無論如何

我使用“中間類”過濾器。

public class Filter
    {
        public string PropertyName { get; set; }
        public List<string> Values { get; set; }
    }

然后是一個幫助程序類,它將返回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;
    }
}

用法

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

var filteredOrders = orders.Filter(Context, 1);//where 1 is a userId

我的答案取決於您是否願意稍微更改訪問模型。 我在編寫的應用程序中也遇到過類似情況,而且我個人不喜歡必須依靠我的調用代碼來基於用戶身份驗證正確篩選出記錄的想法。

我的方法是使用OData服務模式調用我的實體框架,每個存儲庫都通過OData獨立公開。

OData(WCFDataService)具有QueryInterceptor,它們在進行查詢時對數據進行動態過濾。 因此,如果您向OData存儲庫詢問context.Orders(o => o.Id),則只會看到該用戶被允許查看的訂單,而沒有其他子句。

這里可以找到基本的鏈接,但是需要一些工作來管理主叫用戶並提供您可能需要的過濾。 您可以在每個記錄級別提供查詢攔截器。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM