Ich versuche , um zu bestimmen , wie man zählt die passenden Zeilen auf einem Tisch die EntityFramework verwenden.
Das Problem ist, dass jede Zeile möglicherweise viele Megabyte Daten enthält (in einem Binärfeld). Natürlich wäre das SQL ungefähr so:
SELECT COUNT(*) FROM [MyTable] WHERE [fkID] = '1';
Ich könnte alle Zeilen laden und dann die Anzahl finden mit:
var owner = context.MyContainer.Where(t => t.ID == '1');
owner.MyTable.Load();
var count = owner.MyTable.Count();
Das ist aber grob ineffizient. Gibt es einen einfacheren Weg?
EDIT: Danke an alle. Ich habe die Datenbank aus einem privaten Anhang verschoben, damit ich die Profilerstellung ausführen kann. Das hilft, verursacht aber Verwirrungen, die ich nicht erwartet hatte.
Und meine realen Daten sind etwas tiefer, ich werde Lastwagen verwenden, die Paletten mit Kisten mit Gegenständen tragen - und ich möchte nicht, dass der Lastwagen verlässt, es sei denn, es ist mindestens ein Gegenstand darin.
Meine Versuche sind unten gezeigt. Der Teil, den ich nicht bekomme, ist, dass CASE_2 niemals auf den DB-Server (MSSQL) zugreift.
var truck = context.Truck.FirstOrDefault(t => (t.ID == truckID));
if (truck == null)
return "Invalid Truck ID: " + truckID;
var dlist = from t in ve.Truck
where t.ID == truckID
select t.Driver;
if (dlist.Count() == 0)
return "No Driver for this Truck";
var plist = from t in ve.Truck where t.ID == truckID
from r in t.Pallet select r;
if (plist.Count() == 0)
return "No Pallets are in this Truck";
#if CASE_1
/// This works fine (using 'plist'):
var list1 = from r in plist
from c in r.Case
from i in c.Item
select i;
if (list1.Count() == 0)
return "No Items are in the Truck";
#endif
#if CASE_2
/// This never executes any SQL on the server.
var list2 = from r in truck.Pallet
from c in r.Case
from i in c.Item
select i;
bool ok = (list.Count() > 0);
if (!ok)
return "No Items are in the Truck";
#endif
#if CASE_3
/// Forced loading also works, as stated in the OP...
bool ok = false;
foreach (var pallet in truck.Pallet) {
pallet.Case.Load();
foreach (var kase in pallet.Case) {
kase.Item.Load();
var item = kase.Item.FirstOrDefault();
if (item != null) {
ok = true;
break;
}
}
if (ok) break;
}
if (!ok)
return "No Items are in the Truck";
#endif
Und das aus CASE_1 resultierende SQL wird durch sp_executesql geleitet , aber:
SELECT [Project1].[C1] AS [C1]
FROM ( SELECT cast(1 as bit) AS X ) AS [SingleRowTable1]
LEFT OUTER JOIN (SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(cast(1 as bit)) AS [A1]
FROM [dbo].[PalletTruckMap] AS [Extent1]
INNER JOIN [dbo].[PalletCaseMap] AS [Extent2] ON [Extent1].[PalletID] = [Extent2].[PalletID]
INNER JOIN [dbo].[Item] AS [Extent3] ON [Extent2].[CaseID] = [Extent3].[CaseID]
WHERE [Extent1].[TruckID] = '....'
) AS [GroupBy1] ) AS [Project1] ON 1 = 1
[ Ich habe nicht wirklich Lastwagen, Fahrer, Paletten, Koffer oder Gegenstände; Wie Sie aus der SQL ersehen können, sind die Beziehungen zwischen LKW und Palette sowie zwischen Palette und Fall viele zu viele - obwohl ich nicht denke, dass dies wichtig ist. Meine realen Objekte sind immateriell und schwerer zu beschreiben, deshalb habe ich die Namen geändert. ]]
quelle
Antworten:
Abfragesyntax:
Methodensyntax:
Beide generieren dieselbe SQL-Abfrage.
quelle
SelectMany()
? Wird es gebraucht? Würde es ohne es nicht richtig funktionieren?MyContainer.Where(o => o.ID == '1')
)Ich denke du willst so etwas
(bearbeitet, um Kommentare wiederzugeben)
quelle
var count = context.MyTable.Count(t => t.MyContainer.ID == '1');
nicht lang und hässlich:var count = (from o in context.MyContainer where o.ID == '1' from t in o.MyTable select t).Count();
Aber es hängt vom Codierungsstil ab ...Soweit ich weiß, lädt die ausgewählte Antwort immer noch alle zugehörigen Tests. Laut diesem MSDN-Blog gibt es einen besseren Weg.
http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx
Speziell
quelle
Find(1)
Anfrage erforderlich . Erstellen Sie einfach die Entität und hängen Sie sie an den Kontext an:var princess = new PrincessEntity{ Id = 1 }; context.Princesses.Attach(princess);
Das ist mein Code:
Stellen Sie sicher, dass die Variable als IQueryable definiert ist. Wenn Sie dann die Count () -Methode verwenden, führt EF so etwas aus
Wenn die Datensätze als IEnumerable definiert sind, fragt die generierte SQL ansonsten die gesamte zurückgegebene Tabelle ab und zählt die zurückgegebenen Zeilen.
quelle
Nun, selbst das
SELECT COUNT(*) FROM Table
wird ziemlich ineffizient sein, insbesondere bei großen Tabellen, da SQL Server wirklich nur einen vollständigen Tabellenscan (Clustered Index Scan) durchführen kann.Manchmal ist es gut genug, eine ungefähre Anzahl von Zeilen aus der Datenbank zu kennen, und in einem solchen Fall kann eine solche Aussage ausreichen:
Dadurch wird die dynamische Verwaltungsansicht überprüft und die Anzahl der Zeilen und die Tabellengröße anhand einer bestimmten Tabelle extrahiert. Dazu werden die Einträge für den Heap (index_id = 0) oder den Clustered-Index (index_id = 1) summiert.
Es ist schnell, einfach zu bedienen, aber es ist nicht garantiert 100% genau oder aktuell. In vielen Fällen ist dies jedoch "gut genug" (und belastet den Server erheblich weniger).
Vielleicht würde das auch bei Ihnen funktionieren? Um es in EF zu verwenden, müssten Sie dies natürlich in einem gespeicherten Prozess zusammenfassen oder einen direkten Aufruf "SQL-Abfrage ausführen" verwenden.
Marc
quelle
Verwenden Sie die ExecuteStoreQuery- Methode des Entitätskontexts. Dadurch wird vermieden, dass die gesamte Ergebnismenge heruntergeladen und in Objekte deserialisiert wird, um eine einfache Zeilenzählung durchzuführen.
quelle
int count = context.MyTable.Count(m => m.MyContainerID == '1')
ähnelt das generierte SQL genau dem, was Sie tun, aber der Code ist viel besser. Es werden keine Entitäten als solche in den Speicher geladen. Probieren Sie es in LINQPad aus, wenn Sie möchten - es zeigt Ihnen die SQL, die unter der Decke verwendet wird.Ich denke das sollte funktionieren ...
quelle