2013-07-04 9 views
9

Ich benutze EF5, Arbeitseinheit und Repository-Muster. Ich möchte einige Einschränkungen beim Zugriff auf Daten für bestimmte Benutzer definieren. In der Datenbank habe ich eine Tabelle entworfen, um meine Entitätsnamen und ihre Eigenschaften zu behalten, die EntityProperties genannt wird, und eine andere Tabelle, um die Werte dieser Eigenschaften zu behalten, die PropertyValues ​​genannt wird, und jede EntityProperty hat einen oder mehrere PropertyValues. In Business-Schicht, wenn Benutzer Daten anfordert, wenn für ihn eine Einschränkung definiert ist, sollten der linq-Anweisung einige Bedingungen hinzugefügt werden. Was ich mache, ist, die Liste der Entity-Namen und ihrer Eigenschaften und Werte mit 'userId' zu erhalten, dann füge ich eine 'Where'-Klausel zur linq-Abfrage hinzu. Die Entitätsnamen und ihre Eigenschaften haben jedoch den Typ "String". Daher sollte Reflection verwendet werden, um sie zu verwalten. Aber ich kenne diesen Teil nicht, und ich weiß nicht, wie LINQ where-Klausel aus einer gegebenen Reihe von Bedingungszeichenfolgen erstellen. Zum Beispiel, nehmen wir an, dass ein Benutzer die Liste Befehle anfordert, und Benutzer-ID ist 5. Ich zuerst die Zugriffsbeschränkung Tabellen abfragen, und das Ergebnis ist:Was ist der beste Weg, stark typisierte LINQ-Abfragen aus einigen gegebenen Zeichenfolgen über Reflexion

„Order“, „Id“, „74“

"Order", "Id", "77"

"Order", "Id", "115"

Es bedeutet, dass die Nutzer nur diese drei Aufträge sehen sollten, während Tabelle in Bestellungen, wir habe mehr Aufträge. Also, wenn ich eine LINQ-Abfrage auf Aufträge zu bekommen, wie verwenden möchten:

var orders = from order in Context.Orders 

muss ich es zu etwas machen, wie:

var orders = from order in Context.Orders 

// wo Auftrags-ID in 74 sein sollte, 77,115

Die Reihenfolge der Entitäten und Id-Eigenschaften aus "Strings" und "Id" muss jedoch unbedingt berücksichtigt werden. Also zwei Fragen:

Was ist der beste Weg, stark aus Strings getippt werden? Gibt es einen besseren Weg für mich, dies mit besserer Leistung zu tun?

+0

Welche Einschränkungen würden Sie in der Basis haben? Sie zeigen ein 'Contains' in einem' int'-Feld an. Aber wirst du andere Einschränkungen haben? –

+0

Eine Einschränkung für eine Eigenschaft zum Beispiel: Benutzer mit der ID 5 darf nur auf Aufträge zugreifen, deren Preis gleich "1200" ist. –

+0

Ok. Und wie wäre das in Ihrer Begrenzungstabelle dargestellt? –

Antwort

2

Ok.Mit den Kommentaren könnten wir uns für so etwas entscheiden (vorausgesetzt, Sie haben eine Navigationseigenschaft in EntityProperties Tabelle, die eine Sammlung von PropertyValues ist und PropertyValueList genannt wird (wenn Sie nicht haben, tun Sie einfach eine Verknüpfung statt Include zu verwenden). Hier

ist ein Beispielcode, wirklich rustikal, arbeiten nur mit Int32 Eigenschaften, aber dies könnte der Beginn einer Lösung sein.

Sie auch bei PredicateBuilder aussehen ...

Wie dem auch sei

Ich benutze einen "Zwischenklasse" -Filter.

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

Dann wird eine Hilfsklasse, die ein IQueryable<T> zurückkehren, aber ...

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

Nutzung

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

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

Meine Antwort hängt davon ab, ob Sie Ihr Zugriffsmodell leicht ändern möchten. Ich habe eine ähnliche Situation in einer Anwendung, die ich geschrieben habe, und persönlich mag ich die Idee nicht, mich auf meinen Anrufcode zu verlassen, um die Aufzeichnungen basierend auf der Benutzerauthentifizierung korrekt herauszufiltern.

Meine Herangehensweise war, ein OData-Servicemuster zum Aufruf in mein Entity Framework zu verwenden, wobei jedes der Repositories unabhängig über OData verfügbar gemacht wird.

Ein OData (WCFDataService) verfügt über QueryInterceptors, mit denen Ihre Daten während der Abfrage gefiltert werden. Wenn Sie also das OData-Repository nach context.Orders (o => o.Id) gefragt haben, sehen Sie nur die Befehle, die dieser Benutzer ohne zusätzliche Klauseln sehen durfte.

Ein guter Link zu den Grundlagen ist here gefunden, aber es erfordert etwas Arbeit, den anrufenden Benutzer zu verwalten und bieten die Filterung, die Sie möglicherweise benötigen. Sie können den Abfrageabfanger auf jeder Datensatzebene bereitstellen.

+0

Ich habe gerade Ihre Frage erneut gelesen und das Problem mit Strings wurde hier nicht angesprochen. Obwohl ich diese Antwort als einen Vorschlag über die Durchführung der Filterung auf der Grundlage des Benutzers verlassen werde. –