Rückgabe von IEnumerable <T> vs. IQueryable <T>

1084

Was ist der Unterschied zwischen der Rückkehr IQueryable<T>gegen IEnumerable<T>, wann sollte man über die anderen bevorzugt werden?

IQueryable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

IEnumerable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;
Stackoverflowuser
quelle

Antworten:

1778

Ja, beide führen zu einer verzögerten Ausführung .

Der Unterschied besteht darin, dass IQueryable<T>die Schnittstelle LINQ-to-SQL (LINQ.-to-irgendetwas wirklich) funktioniert. Wenn Sie also Ihre Abfrage auf einem weiter verfeinernIQueryable<T> , wird diese Abfrage nach Möglichkeit in der Datenbank ausgeführt.

In diesem IEnumerable<T>Fall handelt es sich um LINQ-to-Object. Dies bedeutet, dass alle Objekte, die mit der ursprünglichen Abfrage übereinstimmen, aus der Datenbank in den Speicher geladen werden müssen.

In Code:

IQueryable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

Dieser Code führt SQL aus, um nur Goldkunden auszuwählen. Der folgende Code führt andererseits die ursprüngliche Abfrage in der Datenbank aus und filtert dann die Nicht-Gold-Kunden im Speicher heraus:

IEnumerable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

Dies ist ein ziemlich wichtiger Unterschied, und IQueryable<T>wenn Sie daran arbeiten, können Sie in vielen Fällen nicht zu viele Zeilen aus der Datenbank zurückgeben. Ein weiteres gutes Beispiel tut Paging: Wenn Sie Takeund Skipauf IQueryable, werden Sie nur die Anzahl der angeforderten Zeilen erhalten; Wenn Sie dies tun, IEnumerable<T>werden alle Ihre Zeilen in den Speicher geladen.

driis
quelle
32
Tolle Erklärung. Gibt es Situationen, in denen IEnumerable IQueryable vorzuziehen wäre?
FJXX
8
Wir können also sagen, dass wenn wir IQueryable zum Abfragen von Speicherobjekten verwenden, diese keinen Unterschied zwischen IEnumerable und IQueryable darstellen.
Tarik
11
WARNUNG: Obwohl IQueryable aufgrund der angegebenen Optimierung eine verlockende Lösung sein kann, sollte es nicht über das Repository oder die Serviceschicht hinaus zugelassen werden. Dies dient zum Schutz Ihrer Datenbank vor dem Overhead, der durch das "Stapeln von LINQ-Ausdrücken" verursacht wird.
Yorro
48
@fjxx Ja. Wenn Sie Ihr ursprüngliches Ergebnis wiederholt filtern möchten (mehrere Endergebnisse). Wenn Sie dies auf der IQueryable-Schnittstelle tun, werden mehrere Roundtrips zur Datenbank ausgeführt. Wenn Sie dies auf IEnumerable tun, wird die Filterung im Speicher durchgeführt, wodurch die Daten schneller werden (es sei denn, die Datenmenge ist RIESIG).
Per Hornshøj-Schierbeck
34
Ein weiterer Grund, den Sie bevorzugen IEnumerable, IQueryableist, dass nicht alle LINQ-Vorgänge von allen LINQ-Anbietern unterstützt werden. Solange Sie wissen, was Sie tun, können Sie IQueryableso viele Anfragen wie möglich an den LINQ-Anbieter (LINQ2SQL, EF, NHibernate, MongoDB usw.) senden. Wenn Sie jedoch anderen Code mit Ihrem Code tun lassen, was immer er will, werden IQueryableSie möglicherweise in Schwierigkeiten geraten, da ein Client-Code irgendwo eine nicht unterstützte Operation verwendet. Ich bin mit der Empfehlung einverstanden, IQueryables nicht "in the wild" hinter dem Repository oder einer gleichwertigen Schicht freizugeben .
Avish
302

Die beste Antwort ist gut, aber es werden keine Ausdrucksbäume erwähnt, die erklären, wie sich die beiden Schnittstellen unterscheiden. Grundsätzlich gibt es zwei identische Sätze von LINQ-Erweiterungen. Where(), Sum(), Count(), FirstOrDefault(), Etc alle zwei Versionen: eine , die Funktionen und eine , die Ausdrücke akzeptiert akzeptiert.

  • Die IEnumerableVersionssignatur lautet:Where(Func<Customer, bool> predicate)

  • Die IQueryableVersionssignatur lautet:Where(Expression<Func<Customer, bool>> predicate)

Sie haben wahrscheinlich beide verwendet, ohne es zu merken, da beide mit identischer Syntax aufgerufen werden:

zB Where(x => x.City == "<City>")funktioniert auf beiden IEnumerableundIQueryable

  • Bei Verwendung Where()einer IEnumerableSammlung übergibt der Compiler eine kompilierte Funktion anWhere()

  • Bei Verwendung Where()einer IQueryableSammlung übergibt der Compiler einen Ausdrucksbaum an Where(). Ein Ausdrucksbaum ist wie das Reflexionssystem, jedoch für Code. Der Compiler konvertiert Ihren Code in eine Datenstruktur, die beschreibt, was Ihr Code in einem leicht verdaulichen Format tut.

Warum sich mit diesem Ausdrucksbaum beschäftigen? Ich möchte nur Where()meine Daten filtern. Der Hauptgrund ist, dass sowohl die EF- als auch die Linq2SQL-ORMs Ausdrucksbäume direkt in SQL konvertieren können, wobei Ihr Code viel schneller ausgeführt wird.

Oh, das klingt nach einem kostenlosen Leistungsschub. Sollte ich AsQueryable()in diesem Fall überall verwenden? Nein, IQueryableist nur dann sinnvoll, wenn der zugrunde liegende Datenanbieter etwas damit anfangen kann. So etwas wie ein Stammgast ListumwandelnIQueryable konvertieren, haben Sie keinen Vorteil.

Jacob
quelle
9
IMO ist es besser als die akzeptierte Antwort. Eines verstehe ich jedoch nicht: IQueryable bietet keinen Nutzen für normale Objekte, OK, aber ist es in irgendeiner Weise schlimmer? Denn wenn es einfach keine Vorteile bringt, reicht es nicht aus, IEnumerable zu bevorzugen, sodass die Idee, IQueryable überall zu verwenden, weiterhin gültig ist.
Sergei Tachenov
1
Sergey, IQueryable erweitert IEnumerable, sodass Sie bei Verwendung von IQueryable mehr in den Speicher laden als eine IEnumerable-Instanziierung! Also hier ist ein Argument. ( stackoverflow.com/questions/12064828/… c ++ obwohl ich dachte, ich könnte das extrapolieren)
Viking
Ich stimme Sergei zu, dass dies die beste Antwort ist (obwohl die akzeptierte Antwort in Ordnung ist). Ich würde hinzufügen, dass meiner Erfahrung nach IQueryableFunktionen nicht so gut analysiert werden IEnumerable: Wenn Sie beispielsweise wissen möchten, welche Elemente von a DBSet<BookEntity>nicht in a enthalten sind List<BookObject>, wird dbSetObject.Where(e => !listObject.Any(o => o.bookEntitySource == e))eine Ausnahme ausgelöst : Expression of type 'BookEntity' cannot be used for parameter of type 'BookObject' of method 'Boolean Contains[BookObject] (IEnumerable[BookObject], BookObject)'. Ich musste hinzufügen .ToList()nach dbSetObject.
Jean-David Lanz
80

Ja, beide verwenden die verzögerte Ausführung. Lassen Sie uns den Unterschied mit dem SQL Server-Profiler veranschaulichen.

Wenn wir den folgenden Code ausführen:

MarketDevEntities db = new MarketDevEntities();

IEnumerable<WebLog> first = db.WebLogs;
var second = first.Where(c => c.DurationSeconds > 10);
var third = second.Where(c => c.WebLogID > 100);
var result = third.Where(c => c.EmailAddress.Length > 11);

Console.Write(result.First().UserName);

Im SQL Server-Profiler finden wir einen Befehl gleich:

"SELECT * FROM [dbo].[WebLog]"

Es dauert ungefähr 90 Sekunden, um diesen Codeblock für eine WebLog-Tabelle mit 1 Million Datensätzen auszuführen.

Alle Tabellendatensätze werden also als Objekte in den Speicher geladen, und dann wird mit jedem .Where () ein weiterer Filter im Speicher für diese Objekte erstellt.

Wenn wir IQueryableanstelle von IEnumerableim obigen Beispiel (zweite Zeile) verwenden:

Im SQL Server-Profiler finden wir einen Befehl gleich:

"SELECT TOP 1 * FROM [dbo].[WebLog] WHERE [DurationSeconds] > 10 AND [WebLogID] > 100 AND LEN([EmailAddress]) > 11"

Es dauert ungefähr vier Sekunden, um diesen Codeblock mit auszuführen IQueryable.

IQueryable hat eine Eigenschaft namens, Expressiondie einen Baumausdruck speichert, der erstellt wird, wenn wir den resultin unserem Beispiel verwendeten verwenden (der als verzögerte Ausführung bezeichnet wird). Am Ende wird dieser Ausdruck in eine SQL-Abfrage konvertiert, die auf dem Datenbankmodul ausgeführt wird.

Kasper Roma
quelle
5
Dies lehrt mich beim Casting in IEnumerable, dass das zugrunde liegende IQueryable seine IQueryable-Erweiterungsmethode verliert.
Yiping
56

Beides führt zu einer verzögerten Ausführung, ja.

Welche der anderen vorgezogen wird, hängt davon ab, welche Datenquelle Ihnen zugrunde liegt.

Wenn Sie ein zurückgeben, IEnumerablewird die Laufzeit automatisch gezwungen, LINQ to Objects zum Abfragen Ihrer Sammlung zu verwenden.

Die Rückgabe von IQueryable(die IEnumerableübrigens implementiert wird) bietet die zusätzliche Funktionalität, um Ihre Abfrage in etwas zu übersetzen, das für die zugrunde liegende Quelle möglicherweise eine bessere Leistung erbringt (LINQ zu SQL, LINQ zu XML usw.).

Justin Niessner
quelle
30

Im Allgemeinen würde ich Folgendes empfehlen:

  • Zurückgeben, IQueryable<T>wenn Sie dem Entwickler ermöglichen möchten, die von Ihnen zurückgegebene Abfrage vor der Ausführung mithilfe Ihrer Methode zu verfeinern.

  • Geben IEnumerableSie zurück, wenn Sie eine Reihe von Objekten transportieren möchten, die aufgelistet werden sollen.

Stellen Sie sich vor IQueryable, was es ist - eine "Abfrage" nach Daten (die Sie verfeinern können, wenn Sie möchten). An IEnumerableist eine Reihe von Objekten (die bereits empfangen oder erstellt wurden), über die Sie aufzählen können.

Sebastianmehler
quelle
2
"Kann aufzählen", nicht "kann IEnumerable".
Casey
28

Es wurde bereits viel gesagt, aber auf technischere Weise zurück zu den Wurzeln:

  1. IEnumerable ist eine Sammlung von Objekten im Speicher, die Sie aufzählen können - eine In-Memory-Sequenz, die das Durchlaufen von Objekten ermöglicht (erleichtert das In- foreachLoop- Verfahren , obwohl Sie IEnumeratornur damit arbeiten können). Sie befinden sich in der Erinnerung wie sie ist.
  2. IQueryable ist ein Ausdrucksbaum , der irgendwann in etwas anderes übersetzt wird und über das Endergebnis aufzählen kann . Ich denke, das ist es, was die meisten Menschen verwirrt.

Sie haben offensichtlich unterschiedliche Konnotationen.

IQueryablestellt einen Ausdrucksbaum (einfach eine Abfrage) dar, der vom zugrunde liegenden Abfrageanbieter in etwas anderes übersetzt wird, sobald Release-APIs aufgerufen werden, z. B. LINQ-Aggregatfunktionen (Summe, Anzahl usw.) oder ToList [Array, Dictionary,. ..]. Und IQueryableauch Objekte implementieren IEnumerable, IEnumerable<T>so dass , wenn sie eine Abfrage repräsentiert das Ergebnis dieser Abfrage könnte wiederholt werden. Dies bedeutet, dass IQueryable nicht nur Abfragen sein müssen. Der richtige Begriff ist, dass sie Ausdrucksbäume sind .

Wie diese Ausdrücke ausgeführt werden und woran sie sich wenden, hängt von den sogenannten Abfrageanbietern ab (Ausdrucksausführende, an die wir sie denken können).

In der Entity Framework- Welt ( dh dem mystischen zugrunde liegenden Datenquellenanbieter oder dem Abfrageanbieter) werden IQueryableAusdrücke in native T-SQL- Abfragen übersetzt. Nhibernatemacht ähnliche Dinge mit ihnen. Sie können Ihr eigenes schreiben, indem Sie den in LINQ ziemlich gut beschriebenen Konzepten folgen : Erstellen eines IQueryable Provider- Links, und Sie möchten möglicherweise eine benutzerdefinierte Abfrage-API für Ihren Product Store Provider-Service.

Im Grunde genommen werden IQueryableObjekte so lange konstruiert, bis wir sie explizit freigeben und das System anweisen, sie in SQL oder was auch immer umzuschreiben und die Ausführungskette zur Weiterverarbeitung herunterzusenden.

Wie bei einer verzögerten Ausführung ist es eine LINQFunktion, das Ausdrucksbaumschema im Speicher zu halten und es nur bei Bedarf in die Ausführung zu senden, wenn bestimmte APIs für die Sequenz aufgerufen werden (dieselbe Anzahl, ToList usw.).

Die ordnungsgemäße Verwendung von beiden hängt stark von den Aufgaben ab, denen Sie für den jeweiligen Fall gegenüberstehen. Für das bekannte Repository-Muster entscheide ich mich persönlich für die Rückgabe IList, IEnumerabledh über Listen (Indexer und dergleichen). Daher ist es mein Rat, IQueryablenur innerhalb von Repositorys und IEnumerable irgendwo anders im Code zu verwenden. Nicht über die Bedenken hinsichtlich der Testbarkeit zu sprechen, IQueryabledie zusammenbrechen und das Prinzip der Trennung von Bedenken ruinieren. Wenn Sie einen Ausdruck aus Repositorys zurückgeben, können Verbraucher mit der Persistenzschicht spielen, wie sie es wünschen.

Eine kleine Ergänzung zum Durcheinander :) (aus einer Diskussion in den Kommentaren)) Keines von ihnen ist ein Objekt im Speicher, da es sich nicht um echte Typen an sich handelt, sondern um Marker eines Typs - wenn Sie so tief gehen möchten. Aber es ist sinnvoll (und deshalb hat es sogar MSDN so ausgedrückt), IEnumerables als In-Memory-Sammlungen und IQueryables als Ausdrucksbäume zu betrachten. Der Punkt ist, dass die IQueryable-Schnittstelle die IEnumerable-Schnittstelle erbt, sodass die Ergebnisse dieser Abfrage aufgelistet werden können, wenn sie eine Abfrage darstellen. Durch die Aufzählung wird der einem IQueryable-Objekt zugeordnete Ausdrucksbaum ausgeführt. Tatsächlich können Sie also kein IEnumerable-Mitglied aufrufen, ohne das Objekt im Speicher zu haben. Es wird dort hineinkommen, wenn Sie es tun, wenn es nicht leer ist. IQueryables sind nur Abfragen, nicht die Daten.

Arman McHitarian
quelle
3
Der Kommentar, dass IEnumerables immer im Speicher sind, ist nicht unbedingt wahr. Die IQueryable-Schnittstelle implementiert die IEnumerable-Schnittstelle. Aus diesem Grund können Sie eine unformatierte IQueryable, die eine LINQ-zu-SQL-Abfrage darstellt, direkt an eine Ansicht übergeben, die eine IEnumerable erwartet! Es kann Sie überraschen, dass Ihr Datenkontext abgelaufen ist oder Probleme mit MARS (mehrere aktive Ergebnismengen) auftreten.
Tatsächlich können Sie also kein IEnumerable-Mitglied aufrufen, ohne das Objekt im Speicher zu haben. Es wird dort hineinkommen, wenn Sie es tun, wenn es nicht leer ist. IQueryables sind nur Abfragen, nicht die Daten. Aber ich verstehe deinen Standpunkt wirklich. Ich werde einen Kommentar dazu hinzufügen.
Arman McHitarian
@AlexanderPritchard Keines von ihnen ist ein Objekt im Speicher, da es sich nicht um echte Typen an sich handelt, sondern um Marker eines Typs - wenn Sie so tief gehen möchten. Aber es ist sinnvoll (und deshalb hat es sogar MSDN so ausgedrückt), IEnumerables als In-Memory-Sammlungen und IQueryables als Ausdrucksbäume zu betrachten. Der Punkt ist, dass die IQueryable-Schnittstelle die IEnumerable-Schnittstelle erbt, sodass die Ergebnisse dieser Abfrage aufgelistet werden können, wenn sie eine Abfrage darstellen. Durch die Aufzählung wird der einem IQueryable-Objekt zugeordnete Ausdrucksbaum ausgeführt.
Arman McHitarian
24

Im Allgemeinen möchten Sie den ursprünglichen statischen Typ der Abfrage beibehalten, bis es darauf ankommt.

Aus diesem Grund können Sie Ihre Variable als 'var' anstelle von entweder IQueryable<>oder definieren, IEnumerable<>und Sie werden wissen, dass Sie den Typ nicht ändern.

Wenn Sie mit einem beginnen IQueryable<>, möchten Sie es normalerweise so lange beibehalten, IQueryable<>bis es einen zwingenden Grund gibt, es zu ändern. Der Grund dafür ist, dass Sie dem Abfrageprozessor so viele Informationen wie möglich geben möchten. Wenn Sie beispielsweise nur 10 Ergebnisse verwenden Take(10)möchten (Sie haben angerufen ), möchten Sie, dass SQL Server darüber informiert wird, damit es seine Abfragepläne optimieren und Ihnen nur die Daten senden kann, die Sie verwenden.

Ein zwingender Grund, den Typ von IQueryable<>in zu ändern , IEnumerable<>könnte sein, dass Sie eine Erweiterungsfunktion aufrufen, die die Implementierung IQueryable<>in Ihrem bestimmten Objekt entweder nicht oder ineffizient verarbeiten kann. In diesem Fall möchten Sie den Typ möglicherweise in konvertieren IEnumerable<>(indem Sie ihn einer Variablen des Typs zuweisen IEnumerable<>oder AsEnumerablebeispielsweise die Erweiterungsmethode verwenden), damit die von Ihnen aufgerufenen Erweiterungsfunktionen diejenigen in der EnumerableKlasse anstelle der QueryableKlasse sind.

AJS
quelle
18

Es gibt einen Blog-Beitrag mit einem kurzen Beispiel für den Quellcode darüber, wie sich ein Missbrauch von IEnumerable<T>dramatisch auf die Leistung von LINQ-Abfragen auswirken kann: Entity Framework: IQueryable vs. IEnumerable .

Wenn wir tiefer graben und in die Quellen schauen, können wir sehen, dass es offensichtlich verschiedene Erweiterungsmethoden gibt, für die IEnumerable<T>:

// Type: System.Linq.Enumerable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Enumerable
{
    public static IEnumerable<TSource> Where<TSource>(
        this IEnumerable<TSource> source, 
        Func<TSource, bool> predicate)
    {
        return (IEnumerable<TSource>) 
            new Enumerable.WhereEnumerableIterator<TSource>(source, predicate);
    }
}

und IQueryable<T>:

// Type: System.Linq.Queryable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Queryable
{
    public static IQueryable<TSource> Where<TSource>(
        this IQueryable<TSource> source, 
        Expression<Func<TSource, bool>> predicate)
    {
        return source.Provider.CreateQuery<TSource>(
            Expression.Call(
                null, 
                ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(
                    new Type[] { typeof(TSource) }), 
                    new Expression[] 
                        { source.Expression, Expression.Quote(predicate) }));
    }
}

Der erste gibt einen aufzählbaren Iterator zurück, und der zweite erstellt eine Abfrage über den in der IQueryableQuelle angegebenen Abfrageanbieter .

Olexander Ivanitskyi
quelle
11

Ich bin kürzlich auf ein Problem mit IEnumerablev gestoßen IQueryable. Der verwendete Algorithmus führte zuerst eine IQueryableAbfrage durch, um eine Reihe von Ergebnissen zu erhalten. Diese wurden dann an eine foreachSchleife übergeben, wobei die Elemente als Entity Framework (EF) -Klasse instanziiert wurden. Diese EF-Klasse wurde dann in der fromKlausel einer Linq to Entity-Abfrage verwendet, wodurch das Ergebnis erhalten wurde IEnumerable.

Ich bin ziemlich neu bei EF und Linq for Entities, daher hat es eine Weile gedauert, um herauszufinden, was der Engpass war. Mit MiniProfiling habe ich die Abfrage gefunden und dann alle einzelnen Vorgänge in eine einzelne IQueryableLinq for Entities-Abfrage konvertiert . Die Ausführung IEnumerabledauerte 15 Sekunden und die IQueryableAusführung 0,5 Sekunden. Es waren drei Tabellen beteiligt, und nachdem ich dies gelesen habe, glaube ich, dass die IEnumerableAbfrage tatsächlich ein Kreuzprodukt mit drei Tabellen bildete und die Ergebnisse filterte.

Versuchen Sie, IQueryables als Faustregel zu verwenden und Ihre Arbeit zu profilieren, um Ihre Änderungen messbar zu machen.

sscheider
quelle
Der Grund war, dass die IQueryable-Ausdrücke in EF in ein natives SQL konvertiert und direkt in der Datenbank ausgeführt werden, während die IEnumerable-Listen speicherinterne Objekte sind. Sie werden irgendwann aus der Datenbank abgerufen, wenn Sie Aggregatfunktionen wie Count, Sum oder To ... aufrufen und anschließend im Speicher arbeiten. IQueryables bleiben auch im Speicher stecken, sobald Sie eine dieser APIs aufgerufen haben. Andernfalls können Sie den Ausdruck über den Stapel der Ebenen weitergeben und bis zum API-Aufruf mit den Filtern herumspielen. Gut gestaltetes DAL als gut gestaltetes Repository wird diese Art von Problemen lösen;)
Arman McHitarian
10

Ich möchte einige Dinge aufgrund scheinbar widersprüchlicher Antworten (hauptsächlich in Bezug auf IEnumerable) klarstellen.

(1) IQueryableerweitert die IEnumerableSchnittstelle. (Sie können eine IQueryablean etwas senden , das IEnumerableohne Fehler erwartet .)

(2) Sowohl IQueryableals auch IEnumerableLINQ versuchen, beim Durchlaufen der Ergebnismenge verzögert zu laden. (Beachten Sie, dass die Implementierung in Schnittstellenerweiterungsmethoden für jeden Typ angezeigt wird.)

Mit anderen Worten, IEnumerablessind nicht ausschließlich "in-memory". IQueryableswerden nicht immer in der Datenbank ausgeführt. IEnumerablemuss Dinge in den Speicher laden (einmal abgerufen, möglicherweise träge), da es keinen abstrakten Datenprovider gibt. IQueryablesVerlassen Sie sich auf einen abstrakten Anbieter (wie LINQ-to-SQL), obwohl dies auch der In-Memory-Anbieter von .NET sein kann.

Beispiel für einen Anwendungsfall

(a) Rufen Sie die Liste der Datensätze IQueryableaus dem EF-Kontext ab. (Es sind keine Datensätze im Speicher.)

(b) Übergeben Sie das IQueryablean eine Ansicht, deren Modell ist IEnumerable. (Gültig. IQueryableErweitert IEnumerable.)

(c) Durchlaufen und über die Ansicht auf die Datensätze, untergeordneten Entitäten und Eigenschaften des Datensatzes zugreifen. (Kann Ausnahmen verursachen!)

Mögliche Probleme

(1) Die IEnumerableVersuche zum verzögerten Laden und Ihr Datenkontext ist abgelaufen. Ausnahme ausgelöst, da der Anbieter nicht mehr verfügbar ist.

(2) Entity Framework-Entity-Proxys sind aktiviert (Standardeinstellung), und Sie versuchen, auf ein verwandtes (virtuelles) Objekt mit einem abgelaufenen Datenkontext zuzugreifen. Gleich wie (1).

(3) Mehrere aktive Ergebnissätze (MARS). Wenn Sie das IEnumerablein einem foreach( var record in resultSet )Block durchlaufen und gleichzeitig versuchen, darauf zuzugreifen record.childEntity.childProperty, kann es sein, dass Sie MARS erhalten, weil sowohl der Datensatz als auch die relationale Entität verzögert geladen werden. Dies führt zu einer Ausnahme, wenn sie in Ihrer Verbindungszeichenfolge nicht aktiviert ist.

Lösung

  • Ich habe festgestellt, dass das Aktivieren von MARS in der Verbindungszeichenfolge unzuverlässig funktioniert. Ich schlage vor, Sie vermeiden MARS, es sei denn, es ist gut verstanden und ausdrücklich erwünscht.

Führen Sie die Abfrage aus und speichern Sie die Ergebnisse durch Aufrufen. resultList = resultSet.ToList() Dies scheint der einfachste Weg zu sein, um sicherzustellen, dass sich Ihre Entitäten im Speicher befinden.

In Fällen, in denen Sie auf verwandte Entitäten zugreifen, benötigen Sie möglicherweise noch einen Datenkontext. Entweder das, oder Sie können Entitäts-Proxys und explizit Includeverwandte Entitäten von Ihrem deaktivieren DbSet.


quelle
9

Der Hauptunterschied zwischen "IEnumerable" und "IQueryable" besteht darin, wo die Filterlogik ausgeführt wird. Einer wird auf der Clientseite (im Speicher) und der andere auf der Datenbank ausgeführt.

Zum Beispiel können wir ein Beispiel betrachten, in dem wir 10.000 Datensätze für einen Benutzer in unserer Datenbank haben und sagen wir nur 900, die aktive Benutzer sind. Wenn wir also in diesem Fall "IEnumerable" verwenden, werden zuerst alle 10.000 Datensätze in den Speicher und geladen Wendet dann den IsActive-Filter darauf an, der schließlich die 900 aktiven Benutzer zurückgibt.

Wenn wir jedoch im selben Fall "IQueryable" verwenden, wird der IsActive-Filter direkt auf die Datenbank angewendet, der direkt von dort die 900 aktiven Benutzer zurückgibt.

Referenz Verbindung

Tabish Usman
quelle
Welches ist optimiert und leicht in Bezug auf die Leistung?
Sitecore Sam
@Sam "IQueryable" wird in Bezug auf Optimierung und geringes Gewicht bevorzugt.
Tabish Usman
6

Wir können beide auf die gleiche Weise verwenden, und sie unterscheiden sich nur in der Leistung.

IQueryable wird nur auf effiziente Weise für die Datenbank ausgeführt. Dies bedeutet, dass eine vollständige Auswahlabfrage erstellt wird und nur die zugehörigen Datensätze abgerufen werden.

Zum Beispiel möchten wir die Top 10 Kunden nehmen, deren Name mit 'Nimal' beginnt. In diesem Fall wird die Auswahlabfrage als generiert select top 10 * from Customer where name like ‘Nimal%’.

Wenn wir jedoch IEnumerable verwenden, sieht die Abfrage wie folgt aus select * from Customer where name like ‘Nimal%’und die Top Ten werden auf C # -Codierungsebene gefiltert (alle Kundendatensätze werden aus der Datenbank abgerufen und an C # übergeben).

user3710357
quelle
5

Zusätzlich zu den ersten 2 wirklich guten Antworten (von driis & von Jacob):

Die IEnumerable-Schnittstelle befindet sich im System.Collections-Namespace.

Das IEnumerable-Objekt stellt einen Datensatz im Speicher dar und kann diese Daten nur vorwärts weiterleiten. Die durch das IEnumerable-Objekt dargestellte Abfrage wird sofort und vollständig ausgeführt, sodass die Anwendung Daten schnell empfängt.

Wenn die Abfrage ausgeführt wird, lädt IEnumerable alle Daten, und wenn wir sie filtern müssen, erfolgt die Filterung selbst auf der Clientseite.

Die IQueryable-Schnittstelle befindet sich im System.Linq-Namespace.

Das IQueryable-Objekt bietet Remotezugriff auf die Datenbank und ermöglicht Ihnen das Navigieren durch die Daten entweder in direkter Reihenfolge von Anfang bis Ende oder in umgekehrter Reihenfolge. Beim Erstellen einer Abfrage ist das zurückgegebene Objekt IQueryable, die Abfrage wird optimiert. Infolgedessen wird während der Ausführung weniger Speicher verbraucht, weniger Netzwerkbandbreite, aber gleichzeitig kann es etwas langsamer verarbeitet werden als eine Abfrage, die ein IEnumerable-Objekt zurückgibt.

Was auszusuchen?

Wenn Sie den gesamten Satz zurückgegebener Daten benötigen, ist es besser, IEnumerable zu verwenden, das die maximale Geschwindigkeit bietet.

Wenn Sie NICHT den gesamten Satz zurückgegebener Daten benötigen, sondern nur einige gefilterte Daten, ist es besser, IQueryable zu verwenden.

Gleb B.
quelle
0

Darüber hinaus ist es interessant festzustellen, dass Sie Ausnahmen erhalten können, wenn Sie IQueryableanstelle von IEnumerable:

Folgendes funktioniert einwandfrei, wenn productses sich um ein handelt IEnumerable:

products.Skip(-4);

Wenn productses sich jedoch um eine handelt IQueryableund versucht wird, auf Datensätze aus einer DB-Tabelle zuzugreifen, wird folgende Fehlermeldung angezeigt:

Der in einer OFFSET-Klausel angegebene Offset darf nicht negativ sein.

Dies liegt daran, dass die folgende Abfrage erstellt wurde:

SELECT [p].[ProductId]
FROM [Products] AS [p]
ORDER BY (SELECT 1)
OFFSET @__p_0 ROWS

und OFFSET können keinen negativen Wert haben.

David Klempfner
quelle