2013-07-04 8 views
9

Używam EF5, jednostki pracy i wzorca repozytorium. Chcę zdefiniować pewne ograniczenia dostępu do danych dla określonych użytkowników. W bazie danych zaprojektowałem tabelę, aby zachować nazwy encji i ich właściwości, które są nazywane EntityProperties, i inną tabelę, aby zachować wartości tych właściwości, które są nazywane wartościami właściwości PropertyValues, a każda właściwość EntityProperty ma jedną lub więcej wartości PropertyValues. W warstwie biznesowej, gdy użytkownik żąda danych, jeśli zdefiniowano dla niego jakieś ograniczenie, należy dodać pewne warunki do instrukcji linq. Co mogę zrobić, to uzyskać listę nazw encji i ich właściwości i wartości przez "userId", a następnie dodaję klauzulę "Where" do zapytania linq. Jednak nazwy encji i ich właściwości są typu "String", dlatego też powinienem używać Reflection do zarządzania nimi. Ale nie znam tej części i nie wiem jak utworzyć LINQ gdzie klauzula z danego zestawu ciągów warunkowych. Na przykład, załóżmy, że użytkownik zażąda listy rozkazów i identyfikator użytkownika to 5. raz pierwszy kwerendy tych tabel ograniczenia dostępu, a wynik jest:Jaki jest najlepszy sposób tworzenia silnie typowanych zapytań LINQ z niektórych łańcuchów, poprzez odbicie

„Zamówienie”, „ID”, „74”

"Zamówienie", "ID", "77"

"Zamówienie", "ID", "115"

oznacza to, że użytkownik powinien zobaczyć tylko te trzy rozkazy, natomiast w zamówieniach tabeli, my mieć więcej zamówień. Więc jeśli chcę użyć kwerendy LINQ dostać rozkazy, jak:

var orders = from order in Context.Orders 

muszę przekształcić go w coś takiego:

var orders = from order in Context.Orders 

// gdzie kolejność id powinno być 74, 77,115

Jednak uzyskanie zamówienia na właściwość entity i id z ciągu "Order" i "Id" wymaga refleksji. Tak więc dwa pytania:

Jaki jest najlepszy sposób, aby uzyskać silnie wpisane z ciągów? Czy jest lepszy sposób, aby to zrobić, z lepszą wydajnością?

+0

Jakim ograniczeniom trzeba w bazie? Pokazujesz "Zawiera" na polu 'int'. Ale czy będziesz mieć inne ograniczenia? –

+0

Wszelkie ograniczenia dotyczące dowolnej właściwości, na przykład: użytkownik z identyfikatorem 5 ma tylko dostęp do zamówień, których cena wynosi "1200" –

+0

Ok. I jak by to było przedstawione w twoim ograniczonym stole? –

Odpowiedz

2

Ok.Z komentarzami możemy pójść na coś takiego (zakładając, że masz właściwość nawigacji w tabeli EntityProperties, która jest zbiorem PropertyValues, i o nazwie PropertyValueList (jeśli nie masz, po prostu wykonaj połączenie, zamiast używać Include).

Oto przykładowy kod, naprawdę rustykalny, działa tylko z właściwościami Int32, ale może to być początek rozwiązania.

można również spojrzeć na PredicateBuilder ...

Zresztą

Używam filtra "klasy pośredniej".

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

Następnie klasa pomocnika, który zwróci IQueryable<T>, ale ... filtrowane

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; 
    } 
} 

Wykorzystanie

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

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

Moja odpowiedź zależy od tego, czy z przyjemnością zmienisz nieco model dostępu. Mam podobną sytuację w aplikacji, którą napisałem i osobiście nie podoba mi się pomysł polegania na moim kodzie wywołującym, aby poprawnie odfiltrować rekordy w oparciu o uwierzytelnianie użytkowników.

Moje podejście polegało na użyciu wzorca usługi OData do wywoływania mojej struktury Entity, każdy z repozytoriów jest odsłonięty przez OData niezależnie.

OData (WCFDataService) ma QueryInterceptors, które wykonują na bieżąco filtrowanie danych podczas wykonywania zapytania. Tak więc, jeśli poprosiłeś o repozytorium OData dla context.Orders (o => o.Id) zobaczysz tylko zamówienia, które ten użytkownik mógł zobaczyć bez dodatkowych klauzul.

Dobry link do podstaw znajduje się pod numerem here, ale wymaga to trochę pracy, aby zarządzać użytkownikiem wywołującym i zapewniać filtrowanie, którego możesz potrzebować. Możesz dostarczyć przechwytywacz zapytań na każdym poziomie rekordu.

+0

Właśnie przeczytałem twoje pytanie, a problem z ciągami nie został tutaj opisany. Chociaż pozostawię tę odpowiedź jako sugestię, jak wykonać filtrowanie na podstawie użytkownika. –

Powiązane problemy