Consideraciones sobre LINQ (WCF Data Services)
En este tema se proporciona información sobre cómo se crean y ejecutan consultas LINQ cuando se usa el cliente Servicios de datos de Microsoft WCF y las limitaciones de uso de LINQ para consultar un servicio de datos que implementa Open Data Protocol (OData). Para obtener más información sobre cómo se crean y ejecutan consultas en un servicio de datos basado en OData, vea Consultar el servicio de datos (WCF Data Services).
Este tema contiene las siguientes secciones:
Redactar consultas LINQ
LINQ permite redactar consultas en una colección de objetos que implemente la interfaz IEnumerable<T>. Tanto el cuadro de diálogo Agregar referencia de servicio de Visual Studio como la herramienta DataSvcUtil.exe se usan para generar una representación de un servicio de OData como clase de contendor de entidades que hereda de la clase DataServiceContext, así como objetos que representen las entidades devueltas en fuentes. Estas herramientas también generan propiedades en la clase de contenedor de entidades de las colecciones que el servicio exponen como fuentes. Cada una de estas propiedades de la clase que encapsula el servicio de datos devuelve una clase DataServiceQuery<TElement>. Puesto que la clase DataServiceQuery<TElement> implementa la interfaz IQueryable<T> definida por LINQ, puede crear una consulta LINQ en fuentes expuestas por el servicio de datos, que la biblioteca cliente traduce en un URI de solicitud de consulta que se envía al servicio de datos en la ejecución.
Importante
El conjunto de consultas que se pueden expresar en la sintaxis de LINQ es más amplio que los habilitados en la sintaxis URI basada en OData que usan los servicios de datos.Cuando la consulta no se puede asignar a ningún URI del servicio de datos de destino, se produce una excepción NotSupportedException.Para obtener más información, vea Unsupported LINQ Methods en este tema.
El siguiente ejemplo es una consulta LINQ que devuelve Orders con un costo de flete de más de 30 $ y ordena los resultados por la fecha de envío, comenzando por la fecha de envío más reciente:
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;
Esta consulta LINQ se traduce en el siguiente URI de la consulta que se ejecuta en el servicio de base de datos del tutorial rápido basado en Northwind:
https://localhost:12345/Northwind.svc/Orders?Orderby=ShippedDate&?filter=Freight gt 30
Para obtener más información general acerca de LINQ, vea Language-Integrated Query (LINQ).
LINQ permite redactar consultas mediante el uso tanto de la sintaxis de consulta declarativa específica del lenguaje, mostrada en el ejemplo anterior, como de un conjunto de métodos de consulta denominados operadores de consulta estándar. Una consulta equivalente al ejemplo anterior se puede redactar mediante el uso de la sintaxis basada en métodos únicamente, como se muestra en el siguiente ejemplo:
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);
El cliente de Servicios de datos de Microsoft WCF puede traducir ambos tipos de consultas redactadas en un URI de la consulta y puede ampliar una consulta LINQ si anexa los métodos de consulta a una expresión de consulta. Cuando redacte consultas LINQ anexando la sintaxis del método a una expresión de consulta o a una clase DataServiceQuery<TElement>, las operaciones se agregan al URI de la consulta en el orden en el que se llama a los métodos. Esto es equivalente a llamar al método AddQueryOption(String, Object) para agregar cada opción de consulta al URI de la consulta.
Ejecutar consultas LINQ
Ciertos métodos de consulta LINQ, como los métodos First<TSource> o Single<TSource>, cuando se anexan a la consulta, provocan la ejecución de esta. También se ejecuta una consult6a cuando los resultados se enumeran implícitamente, como durante un bucle foreach o cuando la consulta se asigna a una colección List. Para obtener más información, vea Consultar el servicio de datos (WCF Data Services).
El cliente ejecuta una consulta LINQ en dos partes. Siempre que sea posible, las expresiones LINQ de una consulta primero se evalúan en el cliente y, a continuación, se generan y se envían al servicio de datos para su evaluación en los datos del servicio. Para obtener más información, vea la sección Client versus Server Execution de Consultar el servicio de datos (WCF Data Services).
Cuando no se puede traducir ninguna consulta LINQ de un URI de consulta conforme a OData, se produce una excepción al intentar la ejecución. Para obtener más información, vea Consultar el servicio de datos (WCF Data Services).
Ejemplos de consultas LINQ
Los ejemplos de las secciones siguientes muestran los tipos de consultas LINQ que se pueden ejecutar en un servicio de OData.
Filtrar
Los ejemplos de consultas LINQ de esta sección filtran los datos de la fuente devuelta por el servicio.
Los siguientes ejemplos son consultas equivalentes que filtran las entidades Orders devueltas para que solo se devuelvan los pedidos con un costo de flete mayor que 30 $:
Usar sintaxis de consulta LINQ:
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;
Usar métodos de consulta LINQ:
Dim filteredOrders = context.Orders.Where(Function(o) o.Freight.Value > 0)
var filteredOrders = context.Orders .Where(o => o.Freight > 30);
La opción $filter de la cadena de consulta del URI:
' 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");
Todos los ejemplos anteriores se traducen en el URI de consulta: https://localhost:12345/northwind.svc/Orders()?$filter=Freight gt 30M.
También puede usar los operadores All<TSource>(IEnumerable<TSource>, Func<TSource, Boolean>) y Any<TSource>(IEnumerable<TSource>) para crear consultas que filtren entidades basándose en propiedades de colección. En este caso, el predicado se evalúa con respecto a propiedades que devuelven tanto colecciones de entidades relacionadas como colecciones de tipos primitivos y complejos. Por ejemplo, la consulta siguiente devuelve todos los empleados que tengan un territorio cuya descripción contenga la cadena proporcionada:
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;
En esta consulta, el operador Any<TSource>(IEnumerable<TSource>) le permite recorrer la asociación varios a varios entre Employees y Territories para filtrar empleados según la evaluación de los territorios relacionados. Para obtener más información, vea la entrada de blog Compatibilidad con Any/All en WCF Data Services.
Ordenar
Los siguientes ejemplos muestran consultas equivalentes que ordenan ascendentemente los datos tanto por nombre de compañía como por código postal:
Usar sintaxis de consulta LINQ:
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;
Usar métodos de consulta LINQ:
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);
Opción $orderby de la cadena de consulta del URI):
Dim sortedCustomers = context.Customers _ .AddQueryOption("$orderby", "CompanyName, PostalCode desc")
var sortedCustomers = context.Customers .AddQueryOption("$orderby", "CompanyName, PostalCode desc");
Todos los ejemplos anteriores se traducen en el URI de la consulta: https://localhost:12345/northwind.svc/Customers()?$orderby=CompanyName,PostalCode desc.
Proyección
Los siguientes ejemplos muestran las consultas equivalentes que proyectan los datos devueltos en el tipo CustomerAddress más restringido:
Usar sintaxis de consulta LINQ:
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 };
Usar métodos de consulta LINQ:
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});
Nota
La opción de consulta $select no se puede agregar a ningún URI de la consulta mediante el uso del método AddQueryOption(String, Object).Se recomienda usar el método Select<TSource, TResult>(IEnumerable<TSource>, Func<TSource, TResult>) de LINQ para que el cliente genere la opción de consulta $select en el URI de solicitud.
Los dos ejemplos anteriores se traducen en el URI de la consulta: "https://localhost:12345/northwind.svc/Customers()?$filter=Country eq 'GerGerm'&$select=CustomerID,Address,City,Region,PostalCode,Country".
Paginación del cliente
En los siguientes ejemplos se muestran las consultas equivalentes que solicita una página de las entidades de pedidos ordenados que incluye 25 pedidos, omitiendo los 50 primeros pedidos:
Aplicar métodos de consulta a una consulta LINQ:
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);
opciones $skip y $top de cadena de consulta de URI):
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);
Los dos ejemplos anteriores se traducen en el URI de la consulta: https://localhost:12345/northwind.svc/Orders()?$orderby=OrderDate desc&$skip=50&$top=25.
Expandir
Cuando se carga un servicio de datos de OData, puede solicitar que las entidades relacionadas con la entidad que sea el destino de la consulta se incluyan en la fuente devuelta. Se llama al método Expand(String) en la clase DataServiceQuery<TElement> para el conjunto de entidades que sean el destino de la consulta LINQ, con el nombre del conjunto de entidades relacionado proporcionado como el parámetro path. Para obtener más información, vea Cargar contenido aplazado (WCF Data Services).
En los siguientes ejemplos se muestran las formas equivalentes de usar el método Expand(String) en una consulta:
En la sintaxis de las consultas LINQ:
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;
Con los métodos de consulta LINQ:
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");
Los dos ejemplos anteriores se traducen en el URI de la consulta: https://localhost:12345/northwind.svc/Orders()?$filter=CustomerID eq 'ALFKI'&$expand=Order_Details.
Métodos LINQ no admitidos
La siguiente tabla contiene las clases de métodos LINQ que no se admiten y que no se pueden incluir en una consulta ejecutada en un servicio de OData:
Tipo de operación |
Métodos no admitidos |
---|---|
Operadores de conjuntos |
No se admiten los siguientes operadores de conjuntos en DataServiceQuery<TElement>: |
Operaciones de ordenación |
No se admiten los siguientes operadores de ordenación que requieran la interfaz IComparer<T> en una clase DataServiceQuery<TElement>: |
Métodos de proyección y filtrado |
No se admiten ninguno de los siguientes operadores de proyección y filtrado que acepten un argumento posicional en una clase DataServiceQuery<TElement>: |
Operadores de agrupación |
No se admite ningún operador de agrupación en una clase DataServiceQuery<TElement>, que incluya lo siguiente: Los operadores de agrupación se deben ejecutar en el cliente. |
Operadores de agregación |
No se admite ninguna operación de agregado en una clase DataServiceQuery<TElement>, que incluya lo siguiente: Las operaciones de agregado se deben ejecutar en el cliente o las debe encapsular una operación de servicio. |
Operadores de paginación |
No se admiten los siguientes operadores de paginación en una clase DataServiceQuery<TElement>: Nota Los operadores de paginación que se ejecuten en una secuencia vacía devuelven NULL. |
Operadores adicionales |
No se admiten los siguientes operadores adicionales en una clase DataServiceQuery<TElement>: |
Funciones de expresión admitidas
Se admiten los siguientes métodos y propiedades de Common Language Runtime (CLR) porque se pueden traducir en una expresión de consulta para su inclusión en el URI de solicitud en un servicio de OData:
Miembro de la clase String |
Función de OData admitida |
---|---|
string concat(string p0, string p1) |
|
bool substringof(string p0, string p1) |
|
bool endswith(string p0, string p1) |
|
int indexof(string p0, string p1) |
|
int length(string p0) |
|
string replace(string p0, string find, string replace) |
|
string substring(string p0, int pos) |
|
string substring(string p0, int pos, int length) |
|
string tolower(string p0) |
|
string toupper(string p0) |
|
string trim(string p0) |
Miembro de la estructura DateTime1 |
Función de OData admitida |
---|---|
int day(DateTime p0) |
|
int hour(DateTime p0) |
|
int minute(DateTime p0) |
|
int month(DateTime p0) |
|
int second(DateTime p0) |
|
int year(DateTime p0) |
1Las propiedades de fecha y hora equivalentes de la clase Microsoft.VisualBasic.DateAndTime, así como el método DatePart de Visual Basic también se admiten.
Miembro de Math |
Función de OData admitida |
---|---|
decimal ceiling(decimal p0) |
|
double ceiling(double p0) |
|
decimal floor(decimal p0) |
|
double floor(double p0) |
|
decimal round(decimal p0) |
|
double round(double p0) |
Miembro de Expression |
Función de OData admitida |
---|---|
bool isof(type p0) |
Además, el cliente quizá pueda evaluar las funciones CLR adicionales en el cliente. Se produce una excepción NotSupportedException para cualquier expresión que no se pueda evaluar en el cliente y que no se pueda traducir en un URI de solicitud válido para su evaluación en el servidor.
Requisitos de control de versiones
La compatibilidad con LINQ tiene los siguientes requisitos de versiones del protocolo OData:
- La compatibilidad con los operadores All<TSource>(IEnumerable<TSource>, Func<TSource, Boolean>) y Any<TSource>(IEnumerable<TSource>) necesita que tanto el cliente como el servicio de datos admitan la versión 3.0 del protocolo OData y versiones posteriores.
Para obtener más información, vea Control de versiones del servicio de datos (Servicios de datos de Microsoft WCF).
Vea también
Conceptos
Consultar el servicio de datos (WCF Data Services)
Proyecciones de consultas (WCF Data Services)
Materialización de objetos (WCF Data Services)