In diesem Beitrag werden zwei LINQ-Abfragen (LINQ-Queries) vergliechen, das gleiche Resultat zurück liefern aber unterschiedlich auf die Daten zugreifen. "Viele Wege führen nach Rom."
Das folgende Beispiel basiert auf SQL Server 2005 und AdventureWorks-Datenbank (Database). Für die Daten werden die Datenbanktabellen Contact und Employee eingesetzt.
Die Datenbanktabellen Employee und Contact sind über die Felder Employee.ContactID und Contact.ContactID miteinander verknüpft. Mithilfe dieser Verknüpfung können wir von einem Employee-Record aus den Contact-Record ermitteln und diese ausgeben.
Das LINQ to SQL-Designer Tool erkennt die Verknüpfung automatisch, wenn wir die beiden Tabelle mittels Drag & Drop auf die Designer-Oberfläche ziehen. Die Verknüpfung zwischen den beiden Tabellen wird von dem LINQ to SQL-Designer Tool generierten Code (siehe AdventureWorks.designer.cs-File) wie folgt dargestellt:
[Association(Name="Contact_Employee", Storage="_Contact", ThisKey="ContactID", OtherKey="ContactID", IsForeignKey=true)]Dieser Verknüpfung ermöglicht uns auf die Contact-Daten zuzugreifen, ohne das wir innerhalb der LINQ-Abfrage (LINQ-Query) auf die Contact-Tabelle refferenzieren müssen. Der Zugriff auf die Contact-Daten erfolgt über das Objekt emp (item.emp.Contact.FirstName), den wir in unseren select definiert haben (select new {emp}).
public Contact Contact
{
get
{
return this._Contact.Entity;
}
set
{
...
}
}
using System;Als Resultat erhalten wir die folgende Liste.
using System.Linq;
namespace LinqToSqlRecursiveSample0010
{
internal class Program
{
private static void Main(string[] args)
{
var db = new AdventureWorksDataContext();
var qry = from emp in db.Employees
select new {emp};
Console.WriteLine(String.Format("{0, 10}", "EmployeeID") + "|" +
String.Format("{0, 10}", "ContactID") + "|" +
String.Format("{0, -30}", "Title") + "|" +
String.Format("{0, -15}", "FirstName") + "|" +
String.Format("{0, -10}", "MiddleName") + "|" +
String.Format("{0, -15}", "LastName"));
foreach (var item in qry)
{
Console.WriteLine(String.Format("{0, 10}", item.emp.EmployeeID) + "|" +
String.Format("{0, 10}", item.emp.ContactID) + "|" +
String.Format("{0, -30}", item.emp.Title) + "|" +
String.Format("{0, -15}", item.emp.Contact.FirstName) + "|" +
String.Format("{0, -10}", item.emp.Contact.MiddleName) + "|" +
String.Format("{0, -15}", item.emp.Contact.LastName));
}
}
}
}
Wenn wir aber im Hintergrund die Datenbankzugriffe auswerten, stellen wir fest, dass die Applikation für jeden Contact-Record auf die Datenbank zugreift und die Daten aus der Datenbank ermittelt. Die Analyse mit dem SQL Server Profiler-Tool zeigt die Zugriffe wie folgt: Alle Employee-Daten werden mit einem SELECT-Statement aus der Datenbank (Database) gelesen.
SELECT [t0].[EmployeeID],
[t0].[NationalIDNumber],
[t0].[ContactID],
[t0].[LoginID],
[t0].[ManagerID],
[t0].[Title],
[t0].[BirthDate],
[t0].[MaritalStatus],
[t0].[Gender],
[t0].[HireDate],
[t0].[SalariedFlag],
[t0].[VacationHours],
[t0].[SickLeaveHours],
[t0].[CurrentFlag],
[t0].[rowguid],
[t0].[ModifiedDate]
FROM [HumanResources].[Employee] AS [t0]
Die Contact-Daten werden dann pro Employee-Record aus der Datenbank gelesen (siehe unten - @p0=1209 ist die ContactID).
exec sp_executesql N'SELECT [t0].[ContactID],
[t0].[NameStyle],
[t0].[Title],
[t0].[FirstName],
[t0].[MiddleName],
[t0].[LastName],
[t0].[Suffix],
[t0].[EmailAddress],
[t0].[EmailPromotion],
[t0].[Phone],
[t0].[PasswordHash],
[t0].[PasswordSalt],
[t0].[AdditionalContactInfo],
[t0].[rowguid],
[t0].[ModifiedDate]
FROM [Person].[Contact] AS [t0]
WHERE [t0].[ContactID] = @p0',N'@p0 int',@p0=1209
Wenn wir unseren LINQ-Query mit einem join wie folgt erweitern (join con in db.Contacts on emp.ContactID equals con.ContactID) und nicht nur die Employee-Daten sondern auch die Contact-Daten in unser select einschliessen (select new {emp, con}), werden wir feststellen, dass die Contact-Daten nicht mehr einzeln aus der Datenbank gelesen, sondern die Contact-Daten mit Employee-Daten zusammen mit einem SELECT-Statement aus der Datenbank ermittelt werden.
using System;
using System.Linq;
namespace LinqToSqlRecursiveSample0010
{
internal class Program
{
private static void Main(string[] args)
{
var db = new AdventureWorksDataContext();
var qry = from emp in db.Employees
join con in db.Contacts on emp.ContactID equals con.ContactID
select new {emp, con};
Console.WriteLine(String.Format("{0, 10}", "EmployeeID") + "|" +
String.Format("{0, 10}", "ContactID") + "|" +
String.Format("{0, -30}", "Title") + "|" +
String.Format("{0, -15}", "FirstName") + "|" +
String.Format("{0, -10}", "MiddleName") + "|" +
String.Format("{0, -15}", "LastName"));
foreach (var item in qry)
{
Console.WriteLine(String.Format("{0, 10}", item.emp.EmployeeID) + "|" +
String.Format("{0, 10}", item.emp.ContactID) + "|" +
String.Format("{0, -30}", item.emp.Title) + "|" +
String.Format("{0, -15}", item.con.FirstName) + "|" +
String.Format("{0, -10}", item.con.MiddleName) + "|" +
String.Format("{0, -15}", item.con.LastName));
}
}
}
}
Als Resultat erhalten wir weiterhin die gleichen Daten:
Und im SQL Server Profiler-Tool sehen wir, dass die Contact-Daten nicht mehr einzeln ermittelt werden:
Vom LINQ to SQL generierte SQL-Statement sieht wie folgt aus:
SELECT [t0].[EmployeeID],
[t0].[NationalIDNumber],
[t0].[ContactID],
[t0].[LoginID],
[t0].[ManagerID],
[t0].[Title],
[t0].[BirthDate],
[t0].[MaritalStatus],
[t0].[Gender],
[t0].[HireDate],
[t0].[SalariedFlag],
[t0].[VacationHours],
[t0].[SickLeaveHours],
[t0].[CurrentFlag],
[t0].[rowguid],
[t0].[ModifiedDate],
[t1].[ContactID] AS [ContactID2],
[t1].[NameStyle],
[t1].[Title] AS [Title2],
[t1].[FirstName],
[t1].[MiddleName],
[t1].[LastName],
[t1].[Suffix],
[t1].[EmailAddress],
[t1].[EmailPromotion],
[t1].[Phone],
[t1].[PasswordHash],
[t1].[PasswordSalt],
[t1].[AdditionalContactInfo],
[t1].[rowguid] AS [rowguid2],
[t1].[ModifiedDate] AS [ModifiedDate2]
FROM [HumanResources].[Employee] AS [t0]
INNER JOIN [Person].[Contact] AS [t1] ON [t0].[ContactID] = [t1].[ContactID]
Viel Spass...
No comments:
Post a Comment