Überlegungen zu LINQ (WCF Data Services)

Dieses Thema enthält Informationen zum Erstellen und Ausführen von LINQ-Abfragen bei der Verwendung des WCF Data Services-Clients und zu den Einschränkungen, die gelten, wenn Sie einen Datendienst, der Open Data Protocol (OData) implementiert, mithilfe von LINQ abfragen. Themenbereich zum Verfassen und Ausführen von Abfragen für einen OData-basierten Datendienst finden Sie unter Abfragen des Datendiensts (WCF Data Services).

Dieses Thema enthält folgende Abschnitte:

Verfassen von LINQ-Abfragen

LINQ ermöglicht es Ihnen, Abfragen für eine Auflistung von Objekten zu verfassen, die IEnumerable<T> implementiert. Sowohl das Dialogfeld Dienstverweis hinzufügen in Visual Studio als auch das Tool "DataSvcUtil.exe" dienen zum Generieren einer Darstellung eines OData-Diensts als Entitätscontainerklasse, die von DataServiceContext erbt, und von Objekten, die die zurückgegebenen Entitäten in Feeds darstellen. Diese Tools generieren auch Eigenschaften der Entitätscontainerklasse für die Auflistungen, die als Feeds vom Dienst verfügbar gemacht werden. Jede Eigenschaft der Klasse, die den Datendienst kapselt, gibt eine DataServiceQuery<TElement> zurück. Da die DataServiceQuery<TElement>-Klasse die von LINQ definierte IQueryable<T>-Schnittstelle implementiert, können Sie eine LINQ-Abfrage für vom Datendienst verfügbar gemachte Feeds verfassen. Diese Abfrage wird von der Clientbibliothek in einen Abfrageanforderungs-URI übersetzt, der bei der Ausführung an den Datendienst gesendet wird.

Wichtig

In der LINQ-Syntax können mehr Abfragen ausgedrückt werden als in der von OData-Datendiensten verwendeten URI-Syntax.Wenn die Abfrage keinem URI im Zieldatendienst zugeordnet werden kann, wird eine Ausnahme vom Typ NotSupportedException ausgelöst.Weitere Informationen finden Sie unter finden Sie in den Unsupported LINQ Methods in diesem Thema.

Das folgende Beispiel zeigt eine LINQ-Abfrage, die Orders mit Frachtkosten über $30 zurückgibt und die Ergebnisse nach dem Lieferdatum sortiert (beginnend mit dem aktuellsten Lieferdatum):

Dim selectedOrders = From o In context.Orders _
        Where (o.Freight > 30) _
        Order By o.ShippedDate Descending _
        Select o
var selectedOrders = from o in context.Orders
                     where o.Freight > 30
                     orderby o.ShippedDate descending 
                     select o;

Diese LINQ-Abfrage wird in den folgenden Abfrage-URI übersetzt, der für den auf Northwind basierenden Schnellstart-Datendienst ausgeführt wird:

https://localhost:12345/Northwind.svc/Orders?Orderby=ShippedDate&?filter=Freight gt 30

Weitere allgemeine Informationen zu LINQ finden Sie unter Language-Integrated Query (LINQ).

Mit LINQ können Sie Abfragen sowohl unter Verwendung der im obigen Beispiel gezeigten sprachspezifischen deklarativen Abfragesyntax als auch mit einem als Standardabfrageoperatoren bezeichneten Satz von Abfragemethoden verfassen. Eine Abfrage, die dem obigen Beispiel funktional entspricht, kann wie im folgenden Beispiel dargestellt auch nur mithilfe der methodenbasierten Syntax verfasst werden:

Dim selectedOrders = context.Orders _
                     .Where(Function(o) o.Freight.Value > 30) _
                     .OrderByDescending(Function(o) o.ShippedDate)
var selectedOrders = context.Orders
                    .Where(o => o.Freight > 30)
                    .OrderByDescending(o => o.ShippedDate);

Der WCF Data Services-Client kann beide Abfragearten in einen Abfrage-URI übersetzen, und Sie können eine LINQ-Abfrage erweitern, indem Sie Abfragemethoden an einen Abfrageausdruck anfügen. Wenn Sie LINQ-Abfragen verfassen, indem Sie Methodensyntax an einen Abfrageausdruck oder eine DataServiceQuery<TElement> anfügen, werden die Vorgänge dem Abfrage-URI in der Reihenfolge hinzugefügt, in der Methoden aufgerufen werden. Dies hat die gleiche Funktion wie das Aufrufen der AddQueryOption(String, Object)-Methode zum Hinzufügen der einzelnen Abfrageoptionen zum Abfrage-URI.

Ausführen von LINQ-Abfragen

Wenn bestimmte LINQ-Abfragemethoden wie First<TSource> oder Single<TSource> an die Abfrage angefügt werden, führt dies dazu, dass die Abfrage ausgeführt wird. Eine Abfrage wird auch ausgeführt, wenn Ergebnisse implizit aufgezählt werden, z. B. während einer foreach-Schleife oder wenn die Abfrage einer List-Auflistung zugewiesen wird. Weitere Informationen finden Sie unter Abfragen des Datendiensts (WCF Data Services).

Der Client führt eine LINQ-Abfrage in zwei Teilen aus. Nach Möglichkeit werden LINQ-Ausdrücke in einer Abfrage zuerst auf dem Client ausgewertet, und anschließend wird eine URI-basierte Abfrage generiert und zur Auswertung anhand der Daten im Dienst an den Datendienst gesendet. Weitere Informationen finden Sie im Abschnitt Client versus Server Execution unter Abfragen des Datendiensts (WCF Data Services).

Wenn eine LINQ-Abfrage nicht in einen OData-kompatiblen Abfrage-URI übersetzt werden kann, wird beim Ausführen der Abfrage eine Ausnahme ausgelöst. Weitere Informationen finden Sie unter Abfragen des Datendiensts (WCF Data Services).

LINQ-Abfragebeispiele

Die Beispiele in den folgenden Abschnitten veranschaulichen die Arten von LINQ-Abfragen, die für einen OData-Dienst ausgeführt werden können.

Filtern

In den LINQ-Abfragebeispielen in diesem Abschnitt werden Daten in dem vom Dienst zurückgegebenen Feed gefiltert.

Die folgenden Beispiele zeigen funktional gleichwertige Abfragen, durch die zurückgegebene Orders-Entitäten so gefiltert werden, dass nur Aufträge mit Frachtkosten über $30 zurückgegeben werden:

  • Verwendung der LINQ-Abfragesyntax:

    Dim filteredOrders = From o In context.Orders
                            Where o.Freight.Value > 30
                            Select o
    
    var filteredOrders = from o in context.Orders
                            where o.Freight > 30
                            select o;
    
  • Verwendung von LINQ-Abfragemethoden:

    Dim filteredOrders = context.Orders.Where(Function(o) o.Freight.Value > 0)
    
    var filteredOrders = context.Orders
        .Where(o => o.Freight > 30);
    
  • $filter-Option für die URI-Abfragezeichenfolge:

    ' Define a query for orders with a Freight value greater than 30.
    Dim filteredOrders _
                = context.Orders.AddQueryOption("$filter", "Freight gt 30M")
    
    // Define a query for orders with a Freight value greater than 30.
    var filteredOrders
        = context.Orders.AddQueryOption("$filter", "Freight gt 30M");
    

Die obigen Beispiele werden alle in den Abfrage-URI übersetzt: https://localhost:12345/northwind.svc/Orders()?$filter=Freight gt 30M.

Sie können auch die Operatoren All<TSource>(IEnumerable<TSource>, Func<TSource, Boolean>) und Any<TSource>(IEnumerable<TSource>) verwenden, um Abfragen zu verfassen, die Entitäten auf der Grundlage von Auflistungseigenschaften filtern. In diesem Fall wird das Prädikat gegen Eigenschaften ausgewertet, die sowohl Auflistungen von zugehörigen Entitäten als auch Auflistungen von primitiven und komplexen Typen zurückgeben. Die folgende Abfrage gibt z. B. alle Mitarbeiter mit einem Gebiet zurück, das die angegebene Zeichenfolge enthält:

Dim filteredEmployees = From e In context.Employees _
                        Where e.Territories.Any(Function(t) t.TerritoryDescription.Contains(territory))
                        Select e
var filteredEmployees = from e in context.Employees
                        where e.Territories.Any(t => t.TerritoryDescription.Contains(territory))
                        select e;

In dieser Abfrage können Sie mit dem Any<TSource>(IEnumerable<TSource>)-Operator die m:n-Zuordnung zwischen Employees und Territories durchlaufen, um Mitarbeiter entsprechend einer Auswertung der zugehörigen Gebiete zu filtern. Weitere Informationen finden Sie im Beitrag Unterstützung für Any/All in WCF Data Services.

Sortieren

Die folgenden Beispiele zeigen funktional gleichwertige Abfragen, durch die zurückgegebene Daten in absteigender Reihenfolge nach dem Firmennamen und der Postleitzahl sortiert werden:

  • Verwendung der LINQ-Abfragesyntax:

    Dim sortedCustomers = From c In context.Customers
                                 Order By c.CompanyName Ascending,
                                 c.PostalCode Descending
                                 Select c
    
    var sortedCustomers = from c in context.Customers
                         orderby c.CompanyName ascending, 
                         c.PostalCode descending
                         select c;
    
  • Verwendung von LINQ-Abfragemethoden:

    Dim sortedCustomers = context.Customers.OrderBy(Function(c) c.CompanyName) _
    .ThenByDescending(Function(c) c.PostalCode)
    
    var sortedCustomers = context.Customers.OrderBy(c => c.CompanyName)
        .ThenByDescending(c => c.PostalCode);
    
  • $orderby-Option für die URI-Abfragezeichenfolge:

    Dim sortedCustomers = context.Customers _
                          .AddQueryOption("$orderby", "CompanyName, PostalCode desc")
    
    var sortedCustomers = context.Customers
        .AddQueryOption("$orderby", "CompanyName, PostalCode desc");
    

Die obigen Beispiele werden alle in den Abfrage-URI übersetzt: https://localhost:12345/northwind.svc/Customers()?$orderby=CompanyName,PostalCode desc.

Projektion

Die folgenden Beispiele zeigen funktional gleichwertige Abfragen, durch die zurückgegebene Daten in den enger gefassten CustomerAddress-Typ projiziert werden:

  • Verwendung der LINQ-Abfragesyntax:

    Dim projectedQuery = From c In context.Customers
                         Select New CustomerAddress With
                        {
                            .CustomerID = c.CustomerID,
                            .Address = c.Address,
                            .City = c.City,
                            .Region = c.Region,
                            .PostalCode = c.PostalCode,
                            .Country = c.Country
                        }
    
    var projectedQuery = from c in context.Customers
                select new CustomerAddress
                {
                    CustomerID = c.CustomerID,
                    Address = c.Address,
                    City = c.City,
                    Region = c.Region,
                    PostalCode = c.PostalCode,
                    Country = c.Country
                };
    
  • Verwendung von LINQ-Abfragemethoden:

    Dim projectedQuery = context.Customers.Where(Function(c) c.Country = "Germany") _
                .Select(Function(c) New CustomerAddress With
                {
                    .CustomerID = c.CustomerID,
                    .Address = c.Address,
                    .City = c.City,
                    .Region = c.Region,
                    .PostalCode = c.PostalCode,
                    .Country = c.Country
                })
    
    var projectedQuery = context.Customers.Where(c => c.Country == "Germany")
        .Select(c => new CustomerAddress
        {
            CustomerID = c.CustomerID, 
            Address = c.Address,
            City = c.City,
            Region = c.Region,
            PostalCode = c.PostalCode,
            Country = c.Country});                   
    

Hinweis

Die $select-Abfrageoption kann einem Abfrage-URI nicht mit der AddQueryOption(String, Object)-Methode hinzugefügt werden.Es wird empfohlen, die Select<TSource, TResult>(IEnumerable<TSource>, Func<TSource, TResult>)-LINQ-Methode zu verwenden, damit der Client die $select-Abfrageoption im Anforderungs-URI generiert.

Die obigen Beispiele werden beide in den Abfrage-URI übersetzt: "https://localhost:12345/northwind.svc/Customers()?$filter=Country eq 'GerGerm'&$select=CustomerID,Address,City,Region,PostalCode,Country".

Clientpaging

Die folgenden Beispiele zeigen funktional gleichwertige Abfragen, durch die eine Seite sortierter Order-Entitäten angefordert wird, die 25 Aufträge enthält und auf der die ersten 50 Aufträge übersprungen werden:

  • Anwendung von Abfragemethoden auf eine LINQ-Abfrage:

    Dim pagedOrders = (From o In context.Orders
                       Order By o.OrderDate Descending
                       Select o) _
                   .Skip(50).Take(25)
    
    var pagedOrders = (from o in context.Orders
                          orderby o.OrderDate descending
                         select o).Skip(50).Take(25);
    
  • $skip- und $top-Optionen für die URI-Abfragezeichenfolge:

    Dim pagedOrders = context.Orders _
                      .AddQueryOption("$orderby", "OrderDate desc") _
                      .AddQueryOption("$skip", 50) _
                      .AddQueryOption("$top", 25) _
    
    var pagedOrders = context.Orders
        .AddQueryOption("$orderby", "OrderDate desc")
        .AddQueryOption("$skip", 50)
        .AddQueryOption("$top", 25);
    

Die obigen Beispiele werden beide in den Abfrage-URI übersetzt: https://localhost:12345/northwind.svc/Orders()?$orderby=OrderDate desc&$skip=50&$top=25.

Erweitern

Beim Abfragen eines OData-Datendiensts können Sie anfordern, dass mit der Zielentität der Abfrage verknüpfte Entitäten in den zurückgegebenen Feed eingeschlossen werden. Die Expand(String)-Methode wird in der DataServiceQuery<TElement> für die in der LINQ-Abfrage angegebene Entität aufgerufen, und der Name der verknüpften Entitätenmenge wird als path-Parameter angegeben. Weitere Informationen finden Sie unter Laden von verzögertem Inhalt (WCF Data Services).

Die folgenden Beispiele zeigen funktional gleichwertige Möglichkeiten zur Verwendung der Expand(String)-Methode in einer Abfrage:

  • In der LINQ-Abfragesyntax:

    Dim ordersQuery = From o In context.Orders.Expand("Order_Details")
                         Where o.CustomerID = "ALFKI"
                         Select o
    
    var ordersQuery = from o in context.Orders.Expand("Order_Details")
                         where o.CustomerID == "ALFKI"
                         select o;
    
  • Mit LINQ-Abfragemethoden:

    Dim ordersQuery = context.Orders.Expand("Order_Details") _
                              .Where(Function(o) o.CustomerID = "ALFKI")
    
    var ordersQuery = context.Orders.Expand("Order_Details")
                      .Where(o => o.CustomerID == "ALFKI");
    

Die obigen Beispiele werden beide in den Abfrage-URI übersetzt: https://localhost:12345/northwind.svc/Orders()?$filter=CustomerID eq 'ALFKI'&$expand=Order_Details.

Nicht unterstützte LINQ-Methoden

Die folgende Tabelle enthält die Klassen von LINQ-Methoden, die nicht unterstützt werden und nicht in eine Abfrage für einen OData-Dienst eingeschlossen werden können:

Vorgangstyp 

Nicht unterstützte Methode

Mengenoperatoren

Die folgenden festgelegten Operatoren werden für eine DataServiceQuery<TElement> nicht unterstützt:

Sortierungsoperatoren

Die folgenden Sortierungsoperatoren, die IComparer<T> erfordern, werden für eine DataServiceQuery<TElement> nicht unterstützt:

Projektions- und Filterungsoperatoren

Die folgenden Projektions- und Filterungsoperatoren, die ein Positionsargument akzeptieren, werden für eine DataServiceQuery<TElement> nicht unterstützt:

Gruppierungsoperatoren

Alle Gruppierungsoperatoren werden nicht für eine DataServiceQuery<TElement> unterstützt. Dazu zählen folgende Operatoren:

Gruppierungsvorgänge müssen auf dem Client ausgeführt werden.

Aggregatoperatoren

Alle Aggregatoperatoren werden nicht für eine DataServiceQuery<TElement> unterstützt. Dazu zählen folgende Operatoren:

Aggregatvorgänge müssen entweder auf dem Client ausgeführt oder von einem Dienstvorgang gekapselt werden.

Pagingoperatoren

Die folgenden Pagingoperatoren werden nicht für eine DataServiceQuery<TElement> unterstützt:

Hinweis

Pagingoperatoren, die für eine leere Sequenz ausgeführt werden, geben NULL zurück.

Andere Operatoren

Die folgenden anderen Operatoren werden nicht für eine DataServiceQuery<TElement> unterstützt:

  1. Empty<TResult>

  2. Range

  3. Repeat<TResult>

  4. ToDictionary

  5. ToLookup

Unterstützte Ausdrucksfunktionen

Die folgenden CLR-Methoden und -Eigenschaften (Common Language Runtime) werden unterstützt, da sie zum Einschließen in den Anforderungs-URI für einen OData-Dienst in einen Abfrageausdruck übersetzt werden können:

String-Member

Unterstützte OData-Funktion

Concat

string concat(string p0, string p1)

Contains

bool substringof(string p0, string p1)

EndsWith

bool endswith(string p0, string p1)

IndexOf

int indexof(string p0, string p1)

Length

int length(string p0)

Replace

string replace(string p0, string find, string replace)

Substring

string substring(string p0, int pos)

Substring

string substring(string p0, int pos, int length)

ToLower

string tolower(string p0)

ToUpper

string toupper(string p0)

Trim

string trim(string p0)

DateTime-Member1

Unterstützte OData-Funktion

Day

int day(DateTime p0)

Hour

int hour(DateTime p0)

Minute

int minute(DateTime p0)

Month

int month(DateTime p0)

Second

int second(DateTime p0)

Year

int year(DateTime p0)

1Die entsprechenden Time- und Date-Eigenschaften von Microsoft.VisualBasic.DateAndTime und die DatePart-Methode in Visual Basic werden ebenfalls unterstützt.

Math-Member

Unterstützte OData-Funktion

Ceiling

decimal ceiling(decimal p0)

Ceiling

double ceiling(double p0)

Floor

decimal floor(decimal p0)

Floor

double floor(double p0)

Round

decimal round(decimal p0)

Round

double round(double p0)

Expression-Member

Unterstützte OData-Funktion

TypeIs

bool isof(type p0)

Der Client kann möglicherweise auch weitere CLR-Funktionen auf dem Client auswerten. Für einen Ausdruck, der nicht auf dem Client ausgewertet und nicht in einen gültigen URI für die Auswertung auf dem Server übersetzt werden kann, wird eine NotSupportedException ausgelöst.

Versionsanforderungen

Die LINQ-Unterstützung stellt die folgenden Anforderungen an die OData-Protokollversion:

Weitere Informationen finden Sie unter Datendienst-Versionskontrolle (WCF Data Services).

Siehe auch

Konzepte

Abfragen des Datendiensts (WCF Data Services)

Abfrageprojektionen (WCF Data Services)

Objektmaterialisierung (WCF Data Services)

Andere Ressourcen

OData: URI-Konventionen