it-swarm.com.de

Aufrufen einer Methode in einer Linq-Abfrage

Ich möchte eine Spalte mit dem Namen 'S' in meine Tabelle einfügen, die einen Zeichenfolgenwert abruft, der auf einem Wert basiert, der aus einer Tabellenspalte stammt.

Zum Beispiel: for each ID (a.z) Ich möchte, dass der String-Wert in einer anderen Tabelle gespeichert wird. Der Zeichenfolgenwert wird von einer anderen Methode zurückgegeben, die ihn über eine Linq-Abfrage abruft.

  • Kann man eine Methode von Linq aufrufen?
  • Soll ich alles in der gleichen Abfrage machen?

Dies ist die Struktur der Informationen, die ich erhalten muss:

a.z ist die ID im ersten Quadrat in Tabelle # 1, von dieser ID bekomme ich eine weitere ID in Tabelle # 2, und davon kann ich meinen Zeichenfolgewert abrufen, den ich unter Spalte 'S' anzeigen muss.
enter image description here

var q = (from a in v.A join b in v.B
    on a.i equals b.j
    where a.k == "aaa" && a.h == 0
    select new {T = a.i, S = someMethod(a.z).ToString()})
    return q;

Die Zeile S = someMethod(a.z).ToString() verursacht den folgenden Fehler: 

Das Objekt vom Typ 'System.Data.Linq.SqlClient.SqlColumn' kann nicht umgewandelt werden Geben Sie 'System.Data.Linq.SqlClient.SqlMethodCall' ein.

30
user990635

Sie müssen Ihren Methodenaufruf im Linq-to-Objects-Kontext ausführen, da dieser Methodenaufruf auf der Datenbankseite keinen Sinn ergibt. Sie können dies mit AsEnumerable() tun. Grundsätzlich wird der Rest der Abfrage mit Linq-to-Objects und Ihnen als In-Memory-Collection ausgewertet kann Methodenaufrufe wie erwartet verwenden:

var q = (from a in v.A join b in v.B
        on a.i equals b.j
        where a.k == "aaa" && a.h == 0
        select new {T = a.i, Z = a.z })
        .AsEnumerable()
        .Select(x => new { T = x.T, S = someMethod(x.Z).ToString() })
50
BrokenGlass

Sie möchten es in zwei Aussagen aufteilen. Geben Sie die Ergebnisse aus der Abfrage zurück (was die Datenbank betrifft), und führen Sie die Ergebnisse in einem separaten Schritt ein zweites Mal auf, um die Übersetzung in die neue Objektliste umzuwandeln. Diese zweite "Abfrage" wird die Datenbank nicht beeinträchtigen, sodass Sie die Funktion someMethod() verwenden können.

Linq-to-Entities ist ein bisschen seltsam, da es den Übergang zur Datenbankabfrage von C # extrem nahtlos macht: Sie müssen sich jedoch immer daran erinnern, dass "dieses C # in SQL übersetzt wird." Als Ergebnis müssen Sie sich fragen: "Kann all dieses C # tatsächlich als SQL ausgeführt werden?" Wenn dies nicht möglich ist - wenn Sie someMethod() darin aufrufen - wird Ihre Abfrage Probleme haben. Und die übliche Lösung ist, es aufzuteilen.

(Die andere Antwort von @BrokenGlass mit .AsEnumerable() ist im Grunde eine andere Möglichkeit, genau dies zu tun.)

10
Ken Smith

Das ist eine alte Frage, aber ich sehe, dass niemand einen "Hack" erwähnt, der das Aufrufen von Methoden während der Auswahl ohne Wiederholung erlaubt. Die Idee ist, Konstruktor und Konstruktor zu verwenden, Sie können aufrufen, was Sie möchten (zumindest funktioniert es gut in LINQ mit NHibernate, nicht sicher über LINQ2SQL oder EF, aber ich denke, es sollte das gleiche sein) Für das Benchmark-Programm sieht es so aus, als sei der wiederholte Ansatz in meinem Fall etwa zweimal langsamer als der Konstruktor-Ansatz, und ich denke, es ist kein Wunder - meine Geschäftslogik war minimal, also sind Iteration und Speicherzuordnung wichtig.

Ich wünschte auch, es gäbe eine bessere Möglichkeit zu sagen, dass dies oder das nicht versucht werden sollte, auf einer Datenbank ausgeführt zu werden.

// Here are the results of selecting sum of 1 million ints on my machine:
// Name    Iterations      Percent    
// reiterate       294     53.3575317604356%      
// constructor     551     100%

public class A
{
    public A()
    {            
    }

    public A(int b, int c)
    {
        Result = Sum(b, c);
    }

    public int Result { get; set; }

    public static int Sum(int source1, int source2)
    {
        return source1 + source2;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var range = Enumerable.Range(1, 1000000).ToList();

        BenchmarkIt.Benchmark.This("reiterate", () =>
            {
                var tst = range
                    .Select(x => new { b = x, c = x })
                    .AsEnumerable()
                    .Select(x => new A
                    {
                        Result = A.Sum(x.b, x.c)
                    })
                    .ToList();
            })
            .Against.This("constructor", () =>
            {
                var tst = range
                    .Select(x => new A(x, x))
                    .ToList();
            })
            .For(60)
            .Seconds()
            .PrintComparison();

        Console.ReadKey();
    }
}
0
Giedrius