2015-07-02 8 views
6

Próbuję skompilować zawiera wyrażenie.Nie istnieje żadna metoda "Zawiera" dla typu "System.Data.Linq.DataQuery`1 [System.Object] '

private Expression<Func<T, bool>> Contains<T>(string property, IEnumerable<dynamic> values, T item) 
{ 
    ParameterExpression pe = Expression.Parameter(item.GetType(), "c"); 
    Expression columnNameProperty = Expression.Property(pe, property); 
    var someValueContain = Expression.Constant(values, values.GetType()); 
    var convertExpression = Expression.Convert(columnNameProperty, typeof(Guid)); 
    Expression expression = Expression.Call(someValueContain, "Contains", new Type[] { }, convertExpression); 

    return Expression.Lambda<Func<T, bool>>(expression, pe); 
} 

w czasie wykonywania Mam ten wyjątek.

"Nie sposób 'zawiera' istnieje od rodzaju 'System.Data.Linq.DataQuery`1 [System.Object]'."

soultion było rzucić wartości parametrów do listy

private Expression<Func<T, bool>> Contains<T>(string property, IEnumerable<dynamic> values, T item) 
{ 
    ParameterExpression pe = Expression.Parameter(item.GetType(), "c"); 
    Expression columnNameProperty = Expression.Property(pe, property); 
    Guidvalues = values.Cast<Guid>().ToList(); 
    var someValueContain = Expression.Constant(Guidvalues, Guidvalues.GetType()); 
    var convertExpression = Expression.Convert(columnNameProperty, typeof(Guid)); 
    Expression expression = Expression.Call(someValueContain, "Contains", new Type[] { }, convertExpression); 

    return Expression.Lambda<Func<T, bool>>(expression, pe); 
} 

problem, że lista wartości jest ponad 10.000 więc wydajność była niska i mam ten wyjątek

„The Żądanie przychodzące ma zbyt wiele parametrów Serwer obsługuje maksymalnie 2100 parametrów o wartości . Zmniejsz liczbę parametrów i wyślij ponownie żądanie o wartości . "

ja jakiś sposób, aby zbudować dynamicznie lambda wyrażenie, które generują jak tego zapytania

select * from x where id in (select id from y) 
+1

Zawiera jest rzeczywiście metoda rozszerzenie na 'System.Linq.Queryable'. – Aron

+0

Dlaczego "wartości" 'DataQuery '? Gdzie straciłeś konkretny typ? – Luaan

Odpowiedz

3

To jest po prostu cukier syntaktyczny coraz lepszym od ciebie :)

Problemem jest to, że rzeczywiście jest Contains nie jest metodą na DataQuery<T> - jest to metoda statyczna w System.Linq.Queryable. Kompilator C# obsługuje to za pomocą metod rozszerzeń, ale to tylko kompilator C# - nie jest to funkcja IL, jest to funkcja języka C#. Więc kiedy jesteś manipulowania wyrażenia drzew lub emitujące surowe IL, trzeba obsłużyć siebie:

private Expression<Func<T, bool>> Contains<T, V> 
(string property, IQueryable<V> values, T item) 
{ 
     ParameterExpression pe = Expression.Parameter(item.GetType(), "c"); 
     Expression columnNameProperty = Expression.Property(pe, property); 
     var someValueContain = Expression.Constant(values, values.GetType()); 
     var convertExpression = Expression.Convert(columnNameProperty, typeof(V)); 
     Expression expression = 
      Expression.Call 
      (
      (
      ((Expression<Func<bool>>) 
      (() => Queryable.Contains(default(IQueryable<V>), default(V))) 
      ) 
      .Body as MethodCallExpression).Method, 
      someValueContain, 
      convertExpression 
     ); 

     return Expression.Lambda<Func<T, bool>>(expression, pe); 
} 

będę unikać dynamic w LINQ zapytań, a także - drogę używasz go, to nie ma lepszego niż mając IEnumerable<object>, a jeśli chcesz, żeby zawsze był zawsze Guid, po prostu ustaw go jako ogólny :)

Ta metoda zakłada, że ​​jakikolwiek typ kolumny jest wymienny z typem elementów w przeliczalnym przez ciebie przeliczniku, oczywiście, ale będzie działać dla każdego typu, nie tylko Guid.

Jednak to nie rozwiąże problemu sekund, który masz - jest to dość jednoznaczne. Przelicznik, który przechodzisz, ma zbyt dużo przedmiotów do przejścia. O ile Twój dostawca LINQ nie ma lepszego sposobu przekazywania wartości, musisz odwołać się do dzielenia zapytania na kilka oddzielnych zapytań, z których każdy może zawierać np. 1000 pozycji, a następnie łączenie wyników razem. O ile, oczywiście, moje przypuszczenie jest słuszne, a przechodzący przez ciebie values jest w rzeczywistości również kwerendą - w takim przypadku kod powinien działać dobrze.

EDIT:

Najlepszym sposobem znalazłem na uzyskanie prawidłowego MethodInfo to zestaw metod, takich jak ten:

public static MethodInfo Method<TR>(Expression<Func<TR>> expression) 
{ 
    return (expression.Body as MethodCallExpression).Method; 
} 

public static MethodInfo Method<T1, TR>(Expression<Func<T1, TR>> expression) 
{ 
    return (expression.Body as MethodCallExpression).Method; 
} 

(wygenerowany automatycznie tak samo rzeczywiste Func<...> delegatów)

Pozwala to uprościć uzyskiwanie informacji o sposobie:

Method((IQueryable<T> queryable, T item) => queryable.Contains(item)) 

Lub alternatywnie (w celu uniknięcia konieczności generowania wszystkich możliwych przeciążeń):

Method(() => default(IQueryable<T>).Contains(default(T))) 
+0

'(() => Queryable.Contains) .Method' Nigdy wcześniej nie widziałem tego podejścia - sprytnie! – Rob

+1

Nadal nie mogę zrozumieć, jak to działa .... – Aron

+0

@Rob Zacząłem używać go prawie wyłącznie (choć zwykle umieszczam go w "statycznym polu readonly" gdzieś) - nie tylko unikasz kłopotów ze znalezieniem poprawne przeciążenie, ale możesz również zmienić nazwy metod bezpiecznie itd. Trochę dodatkowej ochrony podczas kompilacji :)) Chociaż teraz, gdy na nią patrzę, faktycznie popełniłem błąd. Muszę to naprawić: D – Luaan

Powiązane problemy