8

EF5、作業単位、およびリポジトリ パターンを使用しています。特定のユーザーのデータへのアクセスに関するいくつかの制限を定義したいと考えています。データベースでは、EntityProperties と呼ばれるエンティティ名とそのプロパティを保持するテーブルと、PropertyValues と呼ばれるこれらのプロパティの値を保持する別のテーブルを設計しました。各 EntityProperty には 1 つ以上の PropertyValues があります。ビジネス層では、ユーザーがデータを要求するときに、制限が定義されている場合は、linq ステートメントにいくつかの条件を追加する必要があります。私がしていることは、「userId」によってエンティティ名とそのプロパティと値のリストを取得し、「Where」句をlinqクエリに追加することです。ただし、エンティティ名とそのプロパティは「文字列」型であるため、Reflection を使用して管理する必要があります。しかし、私はこの部分を知りません、そして私は' 与えられた一連の条件文字列から LINQ where 句を作成する方法がわからない。たとえば、ユーザーがリストの順序を要求し、ユーザー ID が 5 であるとします。最初にこれらのアクセス制限テーブルにクエリを実行すると、結果は次のようになります。

「オーダー」、「ID」、「74」

「オーダー」、「ID」、「77」

「オーダー」、「ID」、「115」

つまり、このユーザーにはこれら 3 つの注文のみが表示されますが、Orders テーブルにはさらに注文があります。したがって、LINQ クエリを使用して注文を取得する場合は、次のようになります。

var orders = from order in Context.Orders

私はそれを次のようなものに変える必要があります:

var orders = from order in Context.Orders

// 注文 ID は 74,77,115 の中にあるはずです

ただし、"Order" および "Id" 文字列から Order エンティティと Id プロパティを取得するには、リフレクションが必要です。したがって、2 つの質問:

文字列から強く型付けされる最良の方法は何ですか? より良いパフォーマンスで、これを行うためのより良い方法はありますか?

4

2 に答える 2

2

Ok。コメントを使用して、そのようなものを使用する可能性があります ( EntityPropertiestable に のコレクションであるナビゲーション プロパティがPropertyValuesあり、名前が付けられていると仮定します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
于 2013-07-04T12:45:36.687 に答える
0

私の答えは、アクセス モデルを少し変更してもよろしいかどうかによって異なります。私が作成したアプリケーションで同様の状況が発生しましたが、個人的には、ユーザー認証に基づいてレコードを正しくフィルター処理するために呼び出しコードに依存する必要があるという考えは好きではありません。

私のアプローチは、OData サービス パターンを使用して Entity Framework を呼び出すことでした。各リポジトリは、OData を介して個別に公開されます。

OData (WCFDataService) には、クエリの作成時にデータのフィルタリングをオンザフライで実行する QueryInterceptor があります。したがって、OData リポジトリに context.Orders(o => o.Id) を要求すると、そのユーザーが追加の句なしで表示を許可された注文のみが表示されます。

基本への適切なリンクはここにありますが、呼び出し元のユーザーを管理し、必要なフィルタリングを提供するには、いくつかの作業が必要です。すべてのレコード レベルでクエリ インターセプターを提供できます。

于 2013-07-04T08:22:57.463 に答える