2009-05-13 3 views
7

ich eine LINQ to SQL-Abfrage haben:LINQ to SQL Nehmen w/o Überspringen verursacht mehrere SQL-Anweisungen

from at in Context.Transaction 
select new { 
    at.Amount, 
    at.PostingDate, 
    Details = 
     from tb in at.TransactionDetail 
     select new { 
      Amount = tb.Amount, 
      Description = tb.Desc 
     } 
} 

Dies resultiert in einer SQL-Anweisung ausgeführt wird. Alles ist gut.

Wenn ich jedoch versuche, bekannte Typen aus dieser Abfrage zurückzugeben, auch wenn sie dieselbe Struktur wie die anonymen Typen haben, bekomme ich eine SQL-Anweisung für die oberste Ebene und dann eine zusätzliche SQL-Anweisung für jedes "Kind" einstellen.

Gibt es eine Möglichkeit, LINQ to SQL zu erhalten, um eine SQL-Anweisung auszustellen und bekannte Typen zu verwenden?

EDIT: Ich muss ein anderes Problem haben. Als ich eine sehr vereinfachte (aber immer noch hierarchische) Version meiner Anfrage in LINQPad einfügte und frisch erstellte bekannte Typen mit nur 2 oder 3 Mitgliedern verwendete, erhielt ich eine SQL-Anweisung. Ich werde posten und updaten, wenn ich mehr weiß.

EDIT 2: Dies scheint aufgrund eines Fehlers in Take zu sein. Siehe meine Antwort unten für Details.

Antwort

11

Zuerst - einige Gründe für den Take Bug.

Wenn Sie nur nehmen, verwendet der Abfrage-Übersetzer nur oben. Top10 wird nicht die richtige Antwort geben, wenn die Kardinalität durch die Teilnahme an einer Kindersammlung unterbrochen wird. Daher fügt der Abfrageübersetzer die untergeordnete Auflistung nicht hinzu (stattdessen werden die untergeordneten Elemente erneut abgefragt).

Wenn Sie überspringen und nehmen, dann die Abfrage Übersetzer beginnt mit einiger RowNumber Logik über die Eltern Reihen in ... diese rownumbers lassen Sie es 10 Eltern nehmen, auch wenn das wirklich 50 Datensätze aufgrund jeden Elternteil mit 5 Kindern .

Wenn Sie überspringen (0) und nehmen, Skip wird als eine Nicht-Operation durch den Übersetzer entfernt - es ist nur, wie Sie nie Skip gesagt haben.

Dies wird ein schwieriger konzeptioneller Sprung von wo Sie sind (Aufruf von Skip and Take) zu einer "einfachen Problemumgehung". Was wir tun müssen - ist, dass die Übersetzung an einem Punkt erzwungen wird, an dem der Übersetzer Skip (0) nicht als Nicht-Operation entfernen kann. Wir müssen Skip aufrufen und die übersprungene Nummer zu einem späteren Zeitpunkt angeben.

DataClasses1DataContext myDC = new DataClasses1DataContext(); 
    //setting up log so we can see what's going on 
myDC.Log = Console.Out; 

    //hierarchical query - not important 
var query = myDC.Options.Select(option => new{ 
    ID = option.ParentID, 
    Others = myDC.Options.Select(option2 => new{ 
    ID = option2.ParentID 
    }) 
}); 
    //request translation of the query! Important! 
var compQuery = System.Data.Linq.CompiledQuery 
    .Compile<DataClasses1DataContext, int, int, System.Collections.IEnumerable> 
    ((dc, skip, take) => query.Skip(skip).Take(take)); 

    //now run the query and specify that 0 rows are to be skipped. 
compQuery.Invoke(myDC, 0, 10); 

Daraus ergibt sich die folgende Abfrage:

SELECT [t1].[ParentID], [t2].[ParentID] AS [ParentID2], (
    SELECT COUNT(*) 
    FROM [dbo].[Option] AS [t3] 
    ) AS [value] 
FROM (
    SELECT ROW_NUMBER() OVER (ORDER BY [t0].[ID]) AS [ROW_NUMBER], [t0].[ParentID] 
    FROM [dbo].[Option] AS [t0] 
    ) AS [t1] 
LEFT OUTER JOIN [dbo].[Option] AS [t2] ON 1=1 
WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p1 + @p2 
ORDER BY [t1].[ROW_NUMBER], [t2].[ID] 
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [0] 
-- @p1: Input Int (Size = 0; Prec = 0; Scale = 0) [0] 
-- @p2: Input Int (Size = 0; Prec = 0; Scale = 0) [10] 
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1 

Und hier, wo wir gewinnen!

WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p1 + @p2 
+0

Das funktioniert, aber ich musste IEnumerable statt nur IEnumerable verwenden, um ToList aufrufen. Es ist auch eine halbe Sekunde schneller in meinem Testfall. Gute Antwort. – JohnOpincar

0

ich keine Chance zu versuchen, dies aber gegeben hätte, dass der anonyme Typ nicht Teil von LINQ ist eher ein C# Konstrukt Ich frage mich, ob Sie nutzen könnten:

from at in Context.Transaction 
select new KnownType(
    at.Amount, 
    at.PostingDate, 
    Details = 
     from tb in at.TransactionDetail 
     select KnownSubType(
       Amount = tb.Amount, 
       Description = tb.Desc 
     ) 
} 

Offensichtlich Einzelheiten wären müssen eine IEnumerable-Sammlung

Ich könnte kilometerweit breit sein, aber es könnte Ihnen zumindest eine neue Linie des Denkens zu verfolgen geben, die nicht schaden kann, also entschuldigen Sie bitte meine Wanderung.

2

Ich habe jetzt festgestellt, dass dies das Ergebnis eines schrecklichen Bugs ist. Der anonyme gegenüber dem bekannten Typ stellte sich nicht als Ursache heraus. Der wahre Grund ist Take.

Das folgende Ergebnis in 1 SQL-Anweisung:

query.Skip(1).Take(10).ToList(); 
query.ToList(); 

jedoch die folgenden zeigen die eine SQL-Anweisung pro Zeile Problem Elternteil.

query.Skip(0).Take(10).ToList(); 
query.Take(10).ToList(); 

Kann mir jemand eine einfache Problemumgehung dafür vorstellen?

EDIT: Die einzige Problemumgehung, die ich gefunden habe, ist zu überprüfen, ob ich auf der ersten Seite bin (IE Skip (0)) und dann zwei Anrufe machen, eins mit Take (1) und das andere mit Skip (1) .Nehmen (pageSize - 1) und addRange die Listen zusammen.

+0

Ist das nicht das Problem hier einfach, dass Sie die SQL-Ausführung mit dem ersten 'ToList()' und dann dem zweiten 'ToList() aufrufen' ruft eine Abfrage für jedes Element der Ergebnisse des ersten, die in jetzt sind Erinnerung? –

+0

Nein, das ist nicht das Problem. – JohnOpincar