2014-02-28 18 views
7

Mein Problem ist, ich muss den Wert einer Eigenschaft in einer generischen Klasse abfragen. Die Eigenschaft ist mit einem Attribut gekennzeichnet.LINQ to Entities erkennt die Methode 'System.Object GetValue (...)'

Siehe den folgenden Code:

var rowKeyProperty = EFUtil.GetClassPropertyForRowKey<T>(); 
var tenantKeyProperty = EFUtil.GetClassPropertyForTenantKey<T>(); 

var queryResult = 
       objContext.CreateObjectSet<T>().Single(l => (((int) tenantKeyProperty.GetValue(l, null)) == tenantKey) && 
                  (((int)rowKeyProperty.GetValue(l, null)) == KeyValue)); 

Die rowKeyProperty und tenantKeyProperty sind vom Typ System.Reflection.PropertyInfo.

Ich verstehe, warum ich den Fehler bekomme. Wenn die linq-Abfrage in SQL übersetzt wird, kann sie die property.GetValue nicht verstehen.

Allerdings bin ich völlig ratlos, was die Arbeit hier angeht. Hat jemand Ideen, wie man das erreicht? Danke.

+4

Die einzige Möglichkeit, EF-kompatible Abfragen mithilfe von reflektierten Eigenschaften zu erstellen, besteht darin, die Ausdrucksbäume selbst zu erstellen. –

+0

Wie bestimmen Sie, welche Eigenschaften Sie benötigen? – Servy

+0

Der Eigenschaft ist ein Attribut zugeordnet. Ich rufe ein attribute.isdefined zurück und trage die Eigenschaft mit dem wahr. –

Antwort

15

Sie müssen tatsächlich die Expression Objekte aufzubauen, um den Ausdruck darstellen, dass Sie dies in diesem Fall den Ausdruck, den Sie wollen darstellen imitieren wollen, ist:

l => l.SomeProperty == SomeValue 

So müssen Sie jede Komponente aufbauen dieses Stück für Stück, von den Parameter erstellen, definieren den Gleichheitsoperator, den Zugriff auf Eigenschaften, den konstanten Wert usw.

public static Expression<Func<TItem, bool>> PropertyEquals<TItem, TValue>(
    PropertyInfo property, TValue value) 
{ 
    var param = Expression.Parameter(typeof(TItem)); 
    var body = Expression.Equal(Expression.Property(param, property), 
     Expression.Constant(value)); 
    return Expression.Lambda<Func<TItem, bool>>(body, param); 
} 

Nachdem Sie all das haben, können Sie es nennen sich die Daten, die Sie haben:

var queryResult = objContext.CreateObjectSet<T>() 
    .Where(PropertyEquals<T, int>(tenantKeyProperty, tenantKey)) 
    .Where(PropertyEquals<T, int>(rowKeyProperty, KeyValue)) 
    .Single(); 
+0

Bestätigt, dass dies die korrekte Abfrage in SQL Profiler generiert. Danke Servy. –

+1

Dies hat ein Problem, wenn Parameter eine DateTime ist. – muttley91

+0

@ muttley91 Und was ist das Problem, das du hast? – Servy

2

Anhang hier ... Nach @Servy Antwort und basierend auf this Thema mit einer netten Antwort von @TomBrothers, können Sie die gleiche Logik verwenden, um eine StartsWith (oder ähnlich) Funktion zu machen:

public static Expression<Func<TItem, bool>> PropertyStartsWith<TItem>(PropertyInfo propertyInfo, string value) 
{ 
    var param = Expression.Parameter(typeof(TItem)); 

    var m = Expression.MakeMemberAccess(param, propertyInfo); 
    var c = Expression.Constant(value, typeof(string)); 
    var mi = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }); 
    var body = Expression.Call(m, mi, c); 

    return Expression.Lambda<Func<TItem, bool>>(body, param); 
} 

In diesem Fall erzwingt es value, eine Zeichenfolge zu sein.

Verwandte Themen