2015-07-02 4 views
6

Ich versuche, enthält Ausdruck enthält.Keine Methode 'Enthält' existiert auf Typ '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); 
} 

zur Laufzeit habe ich diese Ausnahme.

"Keine Methode 'Enthält' auf Typ existiert 'System.Data.Linq.DataQuery`1 [System.Object]'."

war die soultion Werte werfen Parameter

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

das Problem, dass die Werteliste mehr als 10000 ist so die Leistung war gering, und ich habe diese Ausnahme

„Die zur Liste eingehende Anfrage hat zu viele Parameter. Der Server unterstützt maximal 2100 Parameter. Reduzieren Sie die Anzahl der Parameter und senden Sie die Anfrage erneut an . "

ich es eine Möglichkeit, dynamisch Lambda-Ausdruck zu erstellen, die wie diese Abfrage zu generieren

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

Enthält ist eigentlich eine Erweiterung Methode auf 'System.Linq.Queryable'. – Aron

+0

Warum ist 'values'' DataQuery '? Wo hast du den Betontyp verloren? – Luaan

Antwort

3

Dies ist nur syntaktischer Zucker bekommen, desto besser von euch :)

Das Problem, dass Contains in der Tat ist, ist keine Methode auf DataQuery<T> - es ist eine statische Methode in System.Linq.Queryable. Der C# -Compiler handhabt das für Sie über Erweiterungsmethoden, aber das ist nur der C# -Compiler - es ist kein Feature von IL, es ist eine Eigenschaft von C#. Wenn Sie also Ausdruck Bäume sind zu manipulieren oder roh IL emittieren, haben Sie es zu handhaben selbst:

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

ich vermeiden würde dynamic in LINQ-Abfragen als auch - wie Sie es verwenden, es ist nicht besser als IEnumerable<object> mit, und wenn Sie es wollen Guid sowieso immer sein, macht es einfach generic :)

Diese Methode, dass unabhängig von Typ der Spalte umwandelbar ist auf die Art der Elemente in den zählbar Sie vorbei geht davon aus, natürlich, aber es wird für jeden Typ funktionieren, nicht nur Guid.

Allerdings löst dies nicht das zweite Problem, das Sie haben - es ist ziemlich eindeutig. Die Aufzählung, die du passierst, hat viel zu viele Gegenstände, die du passieren kannst. Wenn Ihr LINQ-Anbieter keine bessere Möglichkeit hat, die Werte zu übergeben, müssen Sie auf die Aufteilung der Abfrage in mehrere separate Abfragen zurückgreifen, von denen jede z. 1000 Elemente, und dann die Ergebnisse wieder zusammenfügen. Es sei denn natürlich, meine Vermutung ist richtig, und die values, die Sie übergeben, ist in der Tat eine Abfrage auch - in diesem Fall sollte der Code gut funktionieren.

EDIT:

Die beste Weise, die ich die richtige MethodInfo zu bekommen ist eine Reihe von Methoden wie diese gefunden habe:

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

(automatisch generiert die gleiche Art und Weise die tatsächlichen Func<...> Delegierten sind)

Dies vereinfacht das Abrufen der Methodeninformationen wie folgt:

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

Oder alternativ (zu vermeiden, dass alle möglichen Überlastungen erzeugen):

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

'(() => Queryable.Contains) .Method' Habe diesen Ansatz noch nie zuvor gesehen - clever! – Rob

+1

Ich kann immer noch nicht herausfinden, wie das funktioniert ... – Aron

+0

@Rob Ich benutze es fast ausschließlich (obwohl ich es normalerweise in einem 'statischen Readonly'-Feld irgendwo) - nicht nur vermeiden Sie Ärger mit der Suche die richtige Überladung, aber man kann die Methoden auch sicher umbenennen etc. Ein bisschen extra Kompilierzeitschutz :)) Obwohl ich es jetzt sehe, habe ich dort einen Fehler gemacht. Ich muss es reparieren: D – Luaan

Verwandte Themen