verwendetes Datenbanksystem: EntityFramework 4 / SqlServer2008
Hallo zusammen!
Ich arbeite mich gerade in Linq-Abfragen gegen ein EF4-Model ein und bin
beim Untersuchen verschiedener Queries mit dem Sql Profiler auf eine Frage gestoßen:
Es geht um eine einfache Kontakte - Adressen - Abfrage.
Die Frage ist, wenn ich in der Abfrage per 'Include' die Adressen mitlade, warum kann ich dann nicht über ein Select nur bestimmt Spalten des Contact-Entities laden? Sobald Include verwendet wird, werden immer auch alle Spalten vom Contact-Entity geladen
var result = from c in context.Contacts.Include("Addresses")
select new { c.LastName, c.ContactID, c.Addresses};
SQL-Profiler:
SELECT
[Project1].[ContactID] AS [ContactID],
[Project1].[LastName] AS [LastName],
[Project1].[FirstName] AS [FirstName],
[Project1].[Title] AS [Title],
[Project1].[AddDate] AS [AddDate],
[Project1].[ModifiedDate] AS [ModifiedDate],
[Project1].[C1] AS [C1],
[Project1].[addressID] AS [addressID],
[Project1].[Street1] AS [Street1],
[Project1].[Street2] AS [Street2],
[Project1].[City] AS [City],
[Project1].[StateProvince] AS [StateProvince],
[Project1].[CountryRegion] AS [CountryRegion],
[Project1].[PostalCode] AS [PostalCode],
[Project1].[AddressType] AS [AddressType],
[Project1].[ContactID1] AS [ContactID1],
[Project1].[ModifiedDate1] AS [ModifiedDate1]
FROM ( SELECT
[Extent1].[ContactID] AS [ContactID],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName],
[Extent1].[Title] AS [Title],
[Extent1].[AddDate] AS [AddDate],
[Extent1].[ModifiedDate] AS [ModifiedDate],
[Extent2].[addressID] AS [addressID],
[Extent2].[Street1] AS [Street1],
[Extent2].[Street2] AS [Street2],
[Extent2].[City] AS [City],
[Extent2].[StateProvince] AS [StateProvince],
[Extent2].[CountryRegion] AS [CountryRegion],
[Extent2].[PostalCode] AS [PostalCode],
[Extent2].[AddressType] AS [AddressType],
[Extent2].[ContactID] AS [ContactID1],
[Extent2].[ModifiedDate] AS [ModifiedDate1],
CASE WHEN ([Extent2].[addressID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
FROM [dbo].[Contact] AS [Extent1]
LEFT OUTER JOIN [dbo].[Address] AS [Extent2] ON [Extent1].[ContactID] = [Extent2].[ContactID]
) AS [Project1]
ORDER BY [Project1].[ContactID] ASC, [Project1].[C1] ASC
Macht sich das in der Performance nicht ab einer bestimmten Zeilen- und Spaltenanzahl bemerkbar, wenn einfach immer alle Spalten geladen werden?
Lässt sich dies mit Include überhaupt einschränken?
Ist es hier alternativ besser ein Nested-Query zu verwenden?
Vielen Dank schon mal für Ideen 😃
~ There's no knowledge that is not power~
Wenn du nicht viele (N)VarChar(MAX) Felder hast, ist die Perfomrmance nur dann betroffen, wenn du wirklich viele Spalten hast.
Und viele Spalten in einer Tabelle deuten meist auf suboptimale Normalisierung hin.
Hi,
rein theoretisch ist es mit dem EF kein Problem nur bestimmte Spalten zu laden - eben durch
select new {...}
Ich vermute aber, dass Du Deine Entitäten mit dem GUI Designer angelegt hast, in dessen Schema definiert ist, welche Spalten geladen werden - nämlich alle.
Gruß
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Vll hilft dir das auch etwas bei der Performance (Lazy Loading)
Klick Mich
Grüße
rein theoretisch ist es mit dem EF kein Problem nur bestimmte Spalten zu laden - eben durch
select new {...}
Hi,
so allgemein formuliert ist es nicht richtig. Wenn er Objektgraphen mit Include() erzeugen will muß das Root-Entity vollständig sein. War zumindest in EF1 so. Wenn er nur "Daten aus einer einzelnen Tabelle" benötigt kann er mit anonymen Objekten / EntityDataReader arbeiten.
vMacht sich das in der Performance nicht ab einer bestimmten Zeilen- und Spaltenanzahl bemerkbar, wenn einfach immer alle Spalten geladen werden? Ich habe in einer App alle 5000 Patienten aller Stationen aller Krankenhäuser eines Trägers geladen, das Übertragen der Daten und deren Materialisierung hat 2 sec gedauert. Ich würde vorschlagen dass Du mit Deiner Lösung weitermachst und erst optimierst wenn Probleme auftauchen.
Danke schon mal für eure Antworten.
Vll hilft dir das auch etwas bei der Performance (Lazy Loading)
Klick Mich Lazy Loading steht auf false.
In meinem (Lern)Beispiel wollte ich mich mit eager loading auseinandersetzen.
...so allgemein formuliert ist es nicht richtig. Wenn er Objektgraphen mit Include() erzeugen will muß das Root-Entity vollständig sein. War zumindest in EF1 so. Wenn er nur "Daten aus einer einzelnen Tabelle" benötigt kann er mit anonymen Objekten / EntityDataReader arbeiten.
Das ist ja das was mich wundert, dass sobald ich ein include setze, dass Root-Entity vollständig zurückgegeben wird.
Wenn ich anstelle des Includes ein Nested Querie verwende, werden auch im Root-Entity nur die im Select-Block angegebenen Spalten ausgelesen:
var result = from c in context.Contacts.Include("Addresses")
select new { c.LastName,
c.ContactID,
City = ((from a in c.Addresses
where a.ContactID == c.ContactID
select a.City).FirstOrDefault()) };
SQL-Profiler:
SELECT
[Extent1].[ContactID] AS [ContactID],
[Extent1].[LastName] AS [LastName],
(SELECT TOP (1)
[Extent2].[City] AS [City]
FROM [dbo].[Address] AS [Extent2]
WHERE ([Extent1].[ContactID] = [Extent2].[ContactID]) AND ([Extent2].[ContactID] = [Extent1].[ContactID])) AS [C1]
FROM [dbo].[Contact] AS [Extent1]
Ich habe in einer App alle 5000 Patienten aller Stationen aller Krankenhäuser eines Trägers geladen, das Übertragen der Daten und deren Materialisierung hat 2 sec gedauert. Ich würde vorschlagen dass Du mit Deiner Lösung weitermachst und erst optimierst wenn Probleme auftauchen. Prinzipiell hast du recht - auch wenn ich alles immer so perfekt wie möglich machen möchte - ich denke die Geschwindigkeitsunterschiede wird man auch bei mehreren Datensätzen nicht merken.
Wundern tut mich die Sache jedoch trotzdem noch 😭
~ There's no knowledge that is not power~
Hi,
Das ist ja das was mich wundert, dass sobald ich ein include setze, dass Root-Entity vollständig zurückgegeben wird.
Ein ORM hat nunmal die Aufgabe dem Client die Illusion eines unendlichen, nichtflüchtigen Workspace seiner Objekte zu geben. Du willst -so hast Du es modelliert- genau diese Objekte haben und nicht nur mit Fragmenten arbeiten.
Wenn Du Enitities z.B. für Reporting "shapen" willst dann nimm doch diese nested queries oder die Join-Syntax.
Wenn Du es verwenden willst um beispielsweise ein teures Memofeld auszublenden was Du jetzt nicht benötigst, wird es schwierig, da Property-LazyLoading nicht unterstützt wird. Möglicherweise hilft vertikale Partitionierung der Entities oder Vererbung mit TablePerHierarchy.
Wenn ich anstelle des Includes ein Nested Querie verwende, werden auch im Root-Entity nur die im Select-Block angegebenen Spalten ausgelesen: Mag sein, aber die Daten sind dann nicht mehr aktualisierbar.