11 Manual de WCF Data Services

WCF Data Service WCF Data Services WCF Data Services (anteriormente conocido como "ADO.NET Data Services") es un compon

Views 53 Downloads 0 File size 1MB

Report DMCA / Copyright

DOWNLOAD FILE

Recommend stories

Citation preview

WCF Data Service

WCF Data Services WCF Data Services (anteriormente conocido como "ADO.NET Data Services") es un componente de .NET Framework que permite crear servicios que utilizan Open Data Protocol (OData) para exponer y utilizar datos a través de web o de una intranet utilizando la semántica de transferencia de estado de representación (REST). OData expone los datos como recursos direccionables a través de identificadores uniformes de recursos (URI). Para tener acceso a los datos y cambiarlos se utilizan los verbos HTTP estándar GET, PUT, POST y DELETE. OData usa las convenciones de entidad-relación de Entity Data Model para exponer los recursos como conjuntos de entidades que están relacionadas por medio de asociaciones. WCF Data Services usa el protocolo de OData para direccionar y actualizar los recursos. De esta manera, puede tener acceso a estos servicios desde cualquier cliente que admita OData . OData permite solicitar datos a los recursos y escribir en ellos mediante formatos de transferencia conocidos: JavaScript Object Notation (JSON), un formato de intercambio de datos basado en texto muy usado en la aplicación AJAX, y Atom, conjunto de estándares para intercambiar y actualizar datos como XML. WCF Data Services puede exponer datos procedentes de varios orígenes como fuentes de OData . Las herramientas de Visual Studio facilitan la creación de un servicio basado en OData mediante el uso de un modelo de datos de ADO.NET Entity Framework. También puede crear fuentes de OData basadas en clases de Common Language Runtime (CLR) e incluso en datos enlazados en tiempo de ejecución o datos sin tipo. WCF Data Services también incluye dos conjuntos de bibliotecas de cliente, uno para las aplicaciones cliente de .NET Framework generales y otro específicamente para las aplicaciones basadas en Silverlight. Estas bibliotecas de cliente proporcionan un modelo de programación basado en objetos cuando se tiene acceso a una fuente de OData desde entornos como .NET Framework y Silverlight.

1. Novedades en WCF Data Services En esta versión de WCF Data Services se admite la siguiente funcionalidad nueva de la versión 2.0 de Open Data Protocol (OData) : Contar las entidades de un conjunto de entidades Un nuevo segmento de ruta de acceso $count le permite recibir solo el número total de recursos devueltos por un URI. Una nueva opción de consulta $inlinecount le permite recibir el mismo número total de recursos junto con sus datos en una sola respuesta. La biblioteca cliente de .NET Framework ahora permite acceder a esta información de número de fila en una respuesta de la consulta de la aplicación. Proyecciones de consultas Los resultados de la consulta ya se pueden modificar para incluir solo un subconjunto de propiedades mediante el uso de la nueva opción de consulta $select. La biblioteca cliente de .NET Framework ahora admite proyecciones mediante el uso de la cláusula select (Select en Visual Basic) en una consulta LINQ. La siguiente funcionalidad la proporciona la versión de WCF Data Services de .NET Framework 4: Paginación controlada por servidor Un servicio de datos ya se puede configurar para devolver los recursos solicitados como conjunto de respuestas paginadas. La biblioteca cliente de .NET Framework ahora permite administrar respuestas paginadas. Enlace de datos Una nueva clase DataServiceCollection proporciona enlace simplificado de los datos del servicio de datos a los controles de Windows Presentation Foundation (WPF). Esta clase hereda de la clase ObservableCollection para actualizar automáticamente los datos enlazados cuando se efectúan cambios en los datos de los controles enlazados. Transmitir en secuencias recursos binarios Una entidad se puede definir como entrada de vínculo multimedia, con un vínculo a un recurso multimedia asociado. De esta forma, puede recuperar y guardar datos de objetos binarios grandes independientemente de la entidad a la que pertenezcan. Puede crear un servicio de datos que devuelva datos de propiedad binarios como flujo en vez de cargar primero la entidad completa,

MCT: Luis Dueñas

Pag 1 de 128

WCF Data Service incluidos los datos binarios, en la memoria. Para ello, implemente la interfaz IDataServiceStreamProvider. La biblioteca cliente de .NET Framework ahora permite obtener y establecer propiedades binarias como flujo de datos. Personalización de fuentes WCF Data Services permite personalizar las fuentes devueltas por el servicio de datos mediante la definición de una asignación de entidad-propiedad alternativa de una carga Atom. Proveedores de servicios de datos personalizados Mediante la implementación de un conjunto de nuevas interfaces de proveedor de servicio de datos, puede usar diversos tipos de datos con un servicio de datos, aunque cambie el modelo de datos durante la ejecución.

2. Información general acerca de WCF Data Services WCF Data Services permite la creación y la utilización de servicios de datos para web o para una intranet mediante Open Data Protocol (OData) . OData permite exponer datos como recursos direccionables a través de identificadores uniformes de recursos (URI). De esta forma, puede acceder y cambiar datos mediante el uso de la semántica de transferencia de estado de representación (REST), específicamente los verbos HTTP estándar de GET, PUT, POST y DELETE. En este tema se proporciona información general sobre los modelos y los procedimientos definidos por OData y también las funciones proporcionadas por WCF Data Services para aprovechar las ventajas de OData en aplicaciones basadas en .NET Framework.

Direccionamiento de datos como recursos OData expone los datos como recursos direccionables a través de identificadores uniformes de recursos (URI). Las rutas de acceso de los recursos se construyen según las convenciones del modelo entidadrelación de Entity Data Model. En este modelo, las entidades representan unidades operacionales de datos en un dominio de aplicación, como clientes, pedidos, elementos y productos. En OData , los recursos de entidades se direccionan como conjunto de entidades que contiene instancias de tipos de entidad. Por ejemplo, el URI

http://services.odata.org/Northwind/Northwind.svc/Customers('ALFKI')/ Orders devuelve todos los pedidos del servicio de datos Northwind relacionados con el cliente con un valor CustomerID de ALFKI. Las expresiones de consulta permiten realizar operaciones de consulta tradicionales en los recursos, como filtrarlos, ordenarlos y paginarlos. Por ejemplo, el URI http://services.odata.org/Northwind/Northwind.svc/

Customers('ALFKI')/Orders?$filter=Freight gt 50 filtra los recursos para devolver solo los pedidos cuyo costo de flete sea mayor que 50 dólares.

Interoperabilidad en el acceso a los datos OData se basa en los protocolos estándar de Internet para lograr que los servicios de datos puedan interoperar con aplicaciones que no usan .NET Framework. Dado que es posible usar los URI estándar para direccionar los datos, una aplicación puede obtener acceso a los datos y modificarlos usando la semántica de la Transferencia de estado de representación (REST), específicamente los verbos HTTP estándar GET, PUT, POST y DELETE. Esto le permite tener acceso a estos servicios desde cualquier cliente que pueda analizar y tener acceso a los datos que se transmiten mediante los protocolos HTTP estándar. OData define un conjunto de extensiones del protocolo de publicación Atom (AtomPub). Es compatible con solicitudes y respuestas HTTP en más de un formato de datos para albergar distintas aplicaciones y plataformas cliente. Una fuente OData puede representar los datos en formato Atom, JavaScript Object Notation (JSON) y como XML sin formato. Aunque Atom es el formato predeterminado, el formato de la fuente se especifica en el encabezado de la solicitud HTTP. Cuando se publican los datos como fuente de OData , WCF Data Services se basa en otros servicios de Internet existentes para operaciones como el almacenamiento en memoria caché y la autenticación. Para ello, WCF Data Services se integra con los servicios y aplicaciones de hospedaje existentes, como ASP.NET, Windows Communication Foundation (WCF) e Internet Information Services (IIS).

Independencia de almacenamiento MCT: Luis Dueñas

Pag 2 de 128

WCF Data Service Aunque los recursos se direccionan basándose en un modelo entidad-relación, WCF Data Services expone fuentes de OData sin tener en cuenta el origen de datos subyacente. Después de que WCF Data Services acepte una solicitud HTTP para un recurso identificado mediante un URI, se deserializa la solicitud y se pasa una representación de la misma a un proveedor de WCF Data Services . Este proveedor traduce la solicitud en un formato específico del origen de datos y la ejecuta en el origen de datos subyacente. WCF Data Services logra la independencia de almacenamiento separando el modelo conceptual que direcciona los recursos indicados por OData del esquema específico del origen de datos subyacente. WCF Data Services se integra con ADO.NET Entity Framework para que puedan crearse servicios de datos que exponen datos relacionales. Puede usar las herramientas de Entity Data Model para crear un modelo de datos que contiene recursos direccionables como entidades y al mismo tiempo definir la asignación entre este modelo y las tablas de la base de datos subyacente. WCF Data Services también le permite crear servicios de datos que exponen cualquier estructura de datos que devuelva una implementación de la interfaz IQueryable. Esto le permite crear servicios de datos que exponen los datos procedentes de tipos de .NET Framework. Si también se implementa la interfaz IUpdatable, se admiten las operaciones de creación, actualización y eliminación. Para obtener una ilustración de cómo se integra WCF Data Services con estos proveedores de datos, vea el diagrama arquitectónico que se muestra más adelante en este tema.

Lógica de negocios personalizada WCF Data Services hace más fácil agregar la lógica de negocios personalizada a un servicio de datos a través de operaciones de servicio e interceptores. Las operaciones de servicio son métodos definidos en el servidor direccionables a través de URI con el mismo formato que los recursos de datos. Las operaciones de servicio también pueden usar la sintaxis de las expresiones de consulta para filtrar, ordenar y paginar los datos devueltos por una operación. Por ejemplo, el URI

http://localhost:12345/Northwind.svc/GetOrdersByCity?city='London'&$orderby=OrderDate&$top=10& $skip=10 representa una llamada a una operación de servicio denominada GetOrdersByCity en el servicio de datos de Northwind que devuelve los pedidos para los clientes residentes en Londres, con los resultados paginados ordenados por OrderDate. Los interceptores permiten integrar la lógica de la aplicación personalizada en el procesamiento de los mensajes de solicitud o respuesta de un servicio de datos. Se llama a los interceptores cuando se produce una acción de consulta, inserción, actualización o eliminación en el conjunto de entidades especificado. Después, un interceptor puede modificar los datos, aplicar la directiva de autorización o incluso terminar la operación. Los métodos de interceptor se deben registrar explícitamente para un conjunto de entidades determinado expuesto por un servicio de datos.

Bibliotecas de cliente OData define un conjunto de modelos uniformes para interactuar con los servicios de datos. Esto proporciona una oportunidad de crear componentes reutilizables que se basen en estos servicios, como las bibliotecas del lado cliente que permiten usar servicios de datos más fácilmente. WCF Data Services incluye bibliotecas de cliente para aplicaciones cliente basadas en .NET Framework o en Silverlight. Estas bibliotecas de cliente le permiten interactuar con los servicios de datos mediante objetos de .NET Framework. También admiten consultas basadas en objetos y consultas LINQ, carga de objetos relacionados, seguimiento de cambios y resolución de identidades. Además de las bibliotecas de cliente de OData incluidas con .NET Framework y con Silverlight, hay otras bibliotecas de cliente que le permiten usar una fuente de OData en aplicaciones cliente, como PHP, AJAX y las aplicaciones Java.

Información general de la arquitectura En el siguiente diagrama se muestra la arquitectura de WCF Data Services para exponer fuentes de OData y utilizarlas en bibliotecas de cliente habilitadas para OData :

MCT: Luis Dueñas

Pag 3 de 128

WCF Data Service

MCT: Luis Dueñas

Pag 4 de 128

WCF Data Service

3. Introducción a WCF Data Services Los temas de esta sección le ayudan a comprender rápidamente Open Data Protocol (OData) y cómo usar WCF Data Services para exponer y utilizar fuentes OData mediante la explicación de las tecnologías subyacentes. En esta sección se incluyen contenido conceptual y un tutorial rápido.

3.1. Exponer los datos como servicio WCF Data Services se integra con Visual Studio para permitirle definir con más facilidad servicios para exponer los datos como fuentes Open Data Protocol (OData) . La creación de un servicio de datos que expone una fuente OData supone la realización de los siguientes pasos básicos: 1.

Definirel modelo de datos. WCF Data Services admite de forma nativa los modelos de datos basados en ADO.NET Entity Framework. WCF Data Services también admite modelos de datos que están basados en los objetos de Common Language Runtime (CLR) que devuelven una instancia de la interfaz IQueryable. Esto permite implementar servicios de datos que están basados en listas, matrices y colecciones en .NET Framework. Para habilitar las operaciones de creación, actualización y eliminación sobre estas estructuras de datos, también debe implementar la interfaz IUpdatable. Para escenarios más avanzados, WCF Data Services incluye un conjunto de proveedores que le permiten definir un modelo de datos basado en tipos de datos enlazados en tiempo de ejecución.

2.

Crear el servicio de datos. El servicio de datos más básico expone una clase que hereda de la clase DataService, con un tipo T que es el nombre completo del espacio de nombres del contenedor de la entidad.

3.

Configurar el servicio de datos. De forma predeterminada, WCF Data Services deshabilita el acceso a los recursos que expone un contenedor de entidades. La interfaz DataServiceConfiguration le permite configurar el acceso a los recursos y las operaciones del servicio, especificar la versión admitida de OData , así como definir otros comportamientos de todo el servicio, como los comportamientos de las operaciones por lotes o el número máximo de entidades que pueden devolverse en una única respuesta.

3.2. Acceso a recursos del servicio de datos WCF Data Services admite Open Data Protocol (OData) para exponer los datos como una fuente con recursos direccionables a través de identificadores uniformes de recursos (URI). Estos recursos se representan según las convenciones del modelo entidad-relación de Entity Data Model. En este modelo, las entidades representan unidades operacionales de datos que son tipos de datos en un dominio de aplicación, como clientes, pedidos, elementos y productos. El acceso a los datos de entidad y la modificación de los mismos se realiza usando la semántica de Representational State Transfer (REST), específicamente los verbos HTTP estándar GET, PUT, POST y DELETE.

Direccionar recursos En OData , podrá direccionar los datos expuestos por el modelo de datos utilizando un URI. Por ejemplo, el siguiente URI devuelve una fuente que representa el conjunto de entidades Customers, que contiene las entradas de todas las instancias del tipo de entidad Customer:

http://services.odata.org/Northwind/Northwind.svc/Customers Las entidades tienen propiedades especiales denominadas claves de entidad. Una clave de entidad se utiliza para identificar una entidad única de manera unívoca en un conjunto de entidades. Esto le permite direccionar una instancia concreta de un tipo de entidad en el conjunto de entidades. Por ejemplo, el siguiente URI devuelve la entrada de una instancia concreta del tipo de entidad Customer que tiene el valor de clave ALFKI:

http://services.odata.org/Northwind/Northwind.svc/Customers('ALFKI')

MCT: Luis Dueñas

Pag 5 de 128

WCF Data Service También se pueden direccionar individualmente las propiedades primitivas y complejas de una instancia de entidad. Por ejemplo, el siguiente URI devuelve un elemento XML que contiene el valor de propiedad

ContactName para una entidad Customer concreta: http://services.odata.org/Northwind/Northwind.svc/Customers('ALFKI')/ContactName Al incluir el extremo $value en el URI anterior, en el mensaje de respuesta solamente se devuelve el valor de la propiedad primitiva. En el siguiente ejemplo solo se devuelve la cadena "Maria Anders" sin el elemento XML:

http://services.odata.org/Northwind/Northwind.svc/Customers('ALFKI')/ContactName/$value Las relaciones entre entidades se definen en el modelo de datos mediante asociaciones. Estas asociaciones le permiten direccionar entidades relacionadas mediante propiedades de navegación de una instancia de entidad. Una propiedad de navegación puede devolver una única entidad relacionada, en el caso de una relación de varios a uno, o un conjunto de entidades relacionadas, en el caso de una relación de uno a varios. Por ejemplo, el siguiente URI devuelve una fuente que representa el conjunto de todas las entidades Orders relacionadas con una entidad Customer concreta:

http://services.odata.org/Northwind/Northwind.svc/Customers('ALFKI')/Orders Las relaciones, que son con frecuencia bidireccionales, están representadas por un par de propiedades de navegación. Al contrario de la relación mostrada en el ejemplo anterior, el siguiente URI devuelve una referencia a la entidad Customer a la que pertenece una entidad Order concreta:

http://services.odata.org/Northwind/Northwind.svc/Orders(10643)/Customer OData también le permite direccionar recursos basándose en los resultados de expresiones de consulta. Esto permite filtrar conjuntos de recursos en función de una expresión evaluada. Por ejemplo, el siguiente URI filtra los recursos para devolver a la entidad Customer especificada solo las entidades Orders (pedidos) que se han distribuido desde el 22 de septiembre de 1997:

http://services.odata.org/Northwind/Northwind.svc/Customers('ALFKI')/Orders?$filter=ShippedDate gt datetime'1997-09-22T00:00:00'

Opciones de consulta del sistema OData define un conjunto de opciones de consulta del sistema que puede utilizar para realizar operaciones de consulta tradicionales en los recursos, como filtrar, ordenar y paginar. Por ejemplo, el siguiente URI devuelve el conjunto de todas las entidades Order, junto con las entidades Order_Detail relacionadas, cuyo código postal no termina en 100:

http://services.odata.org/Northwind/Northwind.svc/Orders?$filter=not endswith(ShipPostalCode,'100')&$expand=Order_Details&$orderby=ShipCity Las entradas de la fuente devuelta se ordenan también en función del valor de la propiedad ShipCity de los pedidos. WCF Data Services admite las siguientes opciones de consulta del sistema de OData : Opción de Descripción consulta $orderby

Define un criterio de ordenación predeterminado para las entidades de la fuente devuelta. La siguiente consulta ordena la fuente de los clientes devuelta por provincia y ciudad:

http://services.odata.org/Northwind/Northwind.svc/Customers?$orderby=Country,City $top

Especifica el número de entidades que se incluirán en la fuente devuelta. En el siguiente ejemplo se omiten los 10 primeros clientes y, a continuación, se devuelven los 10 siguientes:

http://services.odata.org/Northwind/Northwind.svc/Customers?$skip=10&$top=10 $skip

Especifica el número de entidades que se omitirán antes de empezar a devolver las entidades en la fuente. En el siguiente ejemplo se omiten los 10 primeros clientes y, a continuación, se devuelven los 10 siguientes:

MCT: Luis Dueñas

Pag 6 de 128

WCF Data Service http://services.odata.org/Northwind/Northwind.svc/Customers?$skip=10&$top=10

$filter

Define una expresión que filtra las entidades devueltas en la fuente en función de criterios concretos. Esta opción de consulta admite un conjunto de operadores de comparación lógicos, operadores aritméticos y funciones de consulta predefinidas que se utilizan para evaluar la expresión de filtro. En el siguiente ejemplo se devuelven todos los pedidos cuyo código postal no termina en 100:

http://services.odata.org/Northwind/Northwind.svc/Orders?$filter=not endswith(ShipPostalCode,'100')

$expand

Especifica qué entidades relacionadas devuelve la consulta. Las entidades relacionadas se incluyen como una fuente o una entrada alineada con la entidad devuelta por la consulta. En el siguiente ejemplo se devuelve el pedido del cliente 'ALFKI' junto con los detalles de artículos de cada pedido:

http://services.odata.org/Northwind/Northwind.svc/Customers('ALFKI')/Orders? $expand=Order_Details $format

Especifica el formato de la fuente devuelta. El formato predeterminado de la fuente es Atom. Especifica una proyección que define las propiedades de la entidad devueltas en la proyección. De forma predeterminada, todas las propiedades de una entidad se devuelven en una fuente. La

$select

siguiente consulta devuelve solo tres propiedades de la entidad Customer:

http://services.odata.org/Northwind/Northwind.svc/Customers?$select=CustomerID,Compa nyName,City $inlinecou Solicita que un recuento del número de entidades devuelto en la fuente se incluya con la fuente. nt

Direccionar relaciones Además de direccionar conjuntos de entidades e instancias de entidades, OData también le permite direccionar las asociaciones que representan las relaciones entre las entidades. Esta funcionalidad es necesaria para crear o cambiar una relación entre dos instancias de entidades, como el expedidor relacionado con un pedido determinado de la base de datos de ejemplo Northwind. OData admite un operador $link para direccionar específicamente las asociaciones entre las entidades. Por ejemplo, el URI siguiente se especifica en un mensaje de solicitud PUT de HTTP para que se use otro expedidor para el pedido especificado.

http://services.odata.org/Northwind/Northwind.svc/Orders(10643)/$links/Shipper

Utilizar la fuente devuelta El URI de un recurso de OData permite direccionar datos de entidad expuestos por el servicio. Al registrar un URI en el campo de dirección de un explorador web, se devuelve una representación de la fuente de OData del recurso solicitado. Aunque un explorador web puede ser útil para probar que un recurso del servicio de datos devuelve los datos esperados, normalmente el acceso a los servicios de datos de producción -que también pueden crear, actualizar y eliminar datos- se realiza mediante código de aplicación o lenguajes de scripting en una página web.

3.3. Usar un servicio de datos en una aplicación cliente Podrá tener acceso a un servicio que exponga una fuente de Open Data Protocol (OData) proporcionando un URI a un explorador web. El URI proporciona la dirección de un recurso y los mensajes de solicitud se envían a estas direcciones para obtener acceso o cambiar los datos subyacentes que el recurso representa. El explorador emite un comando GET de HTTP y devuelve el recurso solicitado como una fuente de OData . Aunque un explorador web puede ser útil para probar que un servicio de OData devuelve los datos esperados, normalmente el acceso a los servicios de OData de producción que le permiten crear, actualizar y eliminar datos también se realiza mediante código de aplicación o lenguajes de scripts en una página web.

MCT: Luis Dueñas

Pag 7 de 128

WCF Data Service En este tema se proporciona información general acerca de cómo obtener acceso a las fuentes de OData desde una aplicación cliente.

Obtener acceso y cambiar datos mediante la semántica REST OData contribuye a garantizar la interoperabilidad entre los servicios que exponen las fuentes de OData y las aplicaciones que utilizan las fuentes de OData . Las aplicaciones tienen acceso a los datos y los cambian en un servicio basado en OData enviando mensajes de solicitud de una acción HTTP concreta y con un URI que direcciona un recurso de entidad sobre el que se debería realizar la acción. Cuando sea obligatorio proporcionar datos de la entidad, se proporcionan mediante una carga específicamente codificada en el cuerpo del mensaje.

Acciones HTTP OData admite las siguientes acciones HTTP para realizar operaciones de creación, lectura, actualización y eliminación en los datos de la entidad que el recurso direccionado representa: GET de HTTP: es la acción predeterminada cuando se tiene acceso a un recurso desde un explorador. No se proporciona ninguna carga en el mensaje de solicitud y se devuelve un método de respuesta con una carga que contiene los datos solicitados. POST de HTTP: inserta los nuevos datos de entidad en el recurso proporcionado. Los datos que se van a insertar se proporcionan en la carga del mensaje de solicitud. La carga del mensaje de respuesta contiene los datos de la entidad recién creada. Esto incluye los valores de clave generados automáticamente. El encabezado también contiene el URI que direcciona el nuevo recurso de entidad. DELETE de HTTP: elimina los datos de entidad que representa el recurso especificado. En los mensajes de solicitud o respuesta no hay presente ninguna carga. PUT de HTTP: reemplaza los datos de la entidad existentes en el recurso solicitado con nuevos datos que se proporcionan en la carga del mensaje de solicitud. MERGE de HTTP: debido a las ineficacias a la hora de ejecutar una acción DELETE seguida de una acción INSERT en el origen de datos únicamente para cambiar los datos de entidad, OData presenta una nueva acción MERGE de HTTP. La carga del mensaje de solicitud contiene las propiedades que se deben cambiar en el recurso de entidad direccionado. Dado que MERGE de HTTP no se define en la especificación HTTP, esto puede requerir un procesamiento adicional para enrutar una solicitud MERGE de HTTP a través de servidores que no usan.

Formatos de carga Para una solicitud PUT, POST o MERGE de HTTP, la carga de un mensaje de solicitud contiene los datos de entidad que el usuario envía al servicio de datos. El contenido de la carga depende del formato de datos del mensaje. Las respuestas HTTP a todas las acciones, excepto DELETE, también contienen este tipo de carga. OData admite los siguientes formatos de carga para tener acceso y cambiar los datos con el servicio: Atom: codificación de mensajes basada en XML definida por OData como una extensión del Protocolo de publicación Atom (AtomPub) para habilitar el intercambio de datos sobre HTTP para fuentes web, podcasts, wikis y funcionalidad de Internet basada en XML. JSON: JavaScript Object Notation (JSON) es un formato de intercambio de datos ligero que está basado en un subconjunto del lenguaje de programación JavaScript. El formato de mensaje de la carga se solicita en el encabezado del mensaje de la solicitud HTTP.

Obtener acceso y cambiar datos mediante bibliotecas cliente WCF Data Services incluye bibliotecas cliente que permiten utilizar más fácilmente una fuente de OData en .NET Framework y las aplicaciones cliente basadas en Silverlight. Estas bibliotecas simplifican el envío y

MCT: Luis Dueñas

Pag 8 de 128

WCF Data Service recepción de los mensajes HTTP. También traducen la carga del mensaje en objetos CLR que representan los datos de entidad. Las bibliotecas cliente representan las dos clases principales DataServiceContext y DataServiceQuery. Estas clases le permiten consultar un servicio de datos y, a continuación, trabajar con los datos de entidad devueltos como objetos CLR. Puede usar el cuadro de diálogo Agregar referencia de servicio de Visual Studio para agregar una referencia a un servicio de datos. Esta herramienta solicita metadatos de servicio desde un servicio de datos al que se ha hecho referencia y genera el DataServiceContext que representa un servicio de datos, además de generar las clases de servicio de datos cliente que representan las entidades. Existen bibliotecas de programación que le permiten utilizar una fuente de OData en otros tipos de aplicaciones cliente.

3.4. Tutorial rápido Este tutorial rápido le ayudará a familiarizarse con WCF Data Services y Open Data Protocol (OData) a través de una serie de tareas relacionadas con los temas de Introducción a WCF Data Services.

3.4.1. Crear el servicio de datos En esta tarea, creará un servicio de datos de ejemplo que exponga una fuente de Open Data Protocol (OData) basada en la base de datos de ejemplo Northwind. Esta tarea supone la realización de los siguientes pasos básicos: 1.

Cree una aplicación web ASP.NET.

2.

Defina el modelo de datos usando las herramientas de Entity Data Model.

3.

Agregue el servicio de datos a la aplicación web.

4.

Habilite el acceso al servicio de datos.

Nota: La aplicación web ASP.NET que crea al completar esta tarea se ejecuta en el servidor de desarrollo de ASP.NET proporcionado por Visual Studio. Para facilitar la prueba y la solución de problemas del servicio de datos durante el desarrollo, puede ejecutar la aplicación que hospeda el servicio de datos mediante Internet Information Services (IIS).

Para crear la aplicación web ASP.NET 1. 2. 3. 4. 5. 6.

En Visual Studio, en el menú Archivo, seleccione Nuevo y, a continuación, seleccione Proyecto. En el cuadro de diálogo Nuevo proyecto, seleccione Visual Basic o Visual C# como el lenguaje de programación. En el recuadro Plantillas, seleccione Aplicación web ASP.NET. Nota: si usa Visual Studio Web Developer, tendrá que crear un nuevo sitio web en lugar de una nueva aplicación web. Escriba NorthwindService como nombre del proyecto. Haga clic en Aceptar. (Opcional) Especifique un número de puerto específico para la aplicación web. Nota: en el resto del tutorial rápido se usa el número de puerto 12345. a.

En el Explorador de soluciones, haga clic con el botón secundario en el nombre del proyecto de ASP.NET que acaba de crear y, a continuación, haga clic en Propiedades.

b.

Seleccione la pestaña Web y, a continuación, establezca el valor del cuadro de testo Puerto específico en 12345.

Para definir el modelo de datos

MCT: Luis Dueñas

Pag 9 de 128

WCF Data Service 1. 2. 3. 4. 5.

En el Explorador de soluciones, haga clic con el botón secundario en el nombre del proyecto de ASP.NET y, a continuación, seleccione Agregar nuevo elemento. En el cuadro de diálogo Agregar nuevo elemento, seleccione ADO.NET Entity Data Model. Como nombre del modelo de datos, escriba Northwind.edmx. En el Asistente para Entity Data Model, seleccione Generar desde la base de datos y, a continuación, haga clic en Siguiente. Para conectar el modelo de datos a la base de datos efectúe uno de los pasos siguientes y después haga clic en Siguiente: o Si no tiene una conexión de base de datos ya configurada, haga clic en Nueva conexión y cree una conexión nueva. Esta instancia de SQL Server debe tener adjuntada la base de datos de ejemplo Northwind. o bien

o

6. 7.

Si tiene una conexión de base de datos ya configurada para conectarse a la base de datos Northwind, seleccione esa conexión en la lista de conexiones.

En la página final del asistente, seleccione las casillas de todas las tablas de la base de datos y desactive las casillas correspondientes a las vistas y los procedimientos almacenados. Haga clic en Finalizar para cerrar el asistente. Nota: Este modelo de datos generado expone las propiedades de clave externa en los tipos de entidad. Los modelos de datos creados mediante Visual Studio 2008 no incluyen estas propiedades de clave externa. Debido a esto, debe actualizar las clases del servicio de datos de cliente de cualquier aplicación cliente que se haya creado para tener acceso al servicio de datos de Northwind que se creó con Visual Studio 2008 antes de intentar tener acceso a esta versión del servicio de datos de Northwind.

Para crear el servicio de datos 1. 2. 3.

En el Explorador de soluciones, haga clic con el botón secundario en el nombre del proyecto de ASP.NET y, a continuación, haga clic en Agregar nuevo elemento. En el cuadro de diálogo Agregar nuevo elemento, seleccione Servicio de datos de ADO.NET. Como nombre del servicio, escriba Northwind. Visual Studio crea los archivos de código y marcado XML para el nuevo servicio. De forma predeterminada, se abre la ventana del editor de código. En el Explorador de soluciones, el servicio tendrá el nombre Northwind, con la extensión .svc.cs o .svc.vb.

4.

En el código para el servicio de datos, reemplace el comentario /* TODO: put your data source

class name here */ de la definición de la clase que define el servicio de datos por el tipo que es el contenedor de entidades del modelo de datos, que en este caso es NorthwindEntities. La definición de la clase debería ser como la siguiente: Public Class Northwind Inherits DataService(Of NorthwindEntities)

Para habilitar el acceso a los recursos del servicio de datos 1.

En el código del servicio de datos, reemplace el código de marcador de posición de la función

InitializeService por el siguiente: ' Grant only the rights needed to support the client application. config.SetEntitySetAccessRule("Orders", EntitySetRights.AllRead _ Or EntitySetRights.WriteMerge _

MCT: Luis Dueñas

Pag 10 de 128

WCF Data Service Or EntitySetRights.WriteReplace) config.SetEntitySetAccessRule("Order_Details", EntitySetRights.AllRead _ Or EntitySetRights.AllWrite) config.SetEntitySetAccessRule("Customers", EntitySetRights.AllRead)

De esta forma, los clientes autorizados pueden tener acceso de lectura y escritura a los recursos para los conjuntos de entidades especificados. Nota: Cualquier cliente que pueda tener acceso a la aplicación ASP.NET también puede tener acceso a los recursos expuestos por el servicio de datos. En un servicio de datos de producción, para evitar el acceso no autorizado a los recursos también debería proteger la aplicación.

3.4.2. Obtener acceso al servicio desde un explorador Web En esta tarea, iniciará WCF Data Services desde Visual Studio y, opcionalmente, deshabilitará la lectura de fuentes en el explorador web. A continuación, recuperará el documento de definición de servicio y tendrá acceso a los recursos del servicio de datos enviando solicitudes HTTP GET a través de un explorador web a los recursos expuestos. Nota: De forma predeterminada, Visual Studio asigna automáticamente un número de puerto al URI localhost en el equipo. En esta tarea se usa el número de puerto 12345 en los ejemplos de URI.

Para solicitar el documento de servicio predeterminado utilizando Internet Explorer 1.

Seleccione Opciones de Internet en el menú Herramientas de Internet Explorer, haga clic en la pestaña Contenido, en la opción Configuración de la sección Fuentes y desactive Activar la vista de lectura de fuentes. De este modo, se garantiza que la lectura de fuentes queda deshabilitada. Si no deshabilita esta funcionalidad, el explorador web tratará el documento codificado como AtomPub devuelto como si fuera una fuente XML en lugar de mostrar los datos XML sin formato. Nota: Si el explorador no puede mostrar la fuente como datos XML sin formato, todavía debería poder ver la fuente como el código fuente de la página.

2. 3.

En Visual Studio, presione la tecla F5 para iniciar la depuración de la aplicación. Abra un explorador web en el equipo local. En la barra de direcciones, escriba el siguiente URI:

http://localhost:12345/northwind.svc De esta forma, se devuelve el documento de servicio predeterminado, que contiene una lista de los conjuntos de entidades expuestos por este servicio de datos.

Para tener acceso a los recursos del conjunto de entidades desde un explorador web 1.

En el campo de la barra de direcciones del explorador web, escriba el URI siguiente:

http://localhost:12345/northwind.svc/Customers De esta forma, se devuelve un conjunto de todos los clientes de la base de datos de ejemplo Northwind. 2.

En el campo de la barra de direcciones del explorador web, escriba el URI siguiente:

MCT: Luis Dueñas

Pag 11 de 128

WCF Data Service http://localhost:12345/northwind.svc/Customers('ALFKI') De esta forma, se devuelve una instancia de la entidad para un cliente concreto, ALFKI. 3.

En el campo de la barra de direcciones del explorador web, escriba el URI siguiente:

http://localhost:12345/northwind.svc/Customers('ALFKI')/Orders De esta forma, se recorre la relación entre clientes y pedidos para devolver un conjunto de todos los pedidos para ese cliente, ALFKI. 4.

En el campo de la barra de direcciones del explorador web, escriba el URI siguiente:

http://localhost:12345/northwind.svc/Customers('ALFKI')/Orders?$filter=OrderID eq 10643 De esta forma, se filtran los pedidos que pertenecen a un cliente determinado, ALFKI, de modo que solo se devuelve un pedido concreto dependiendo del valor proporcionado para OrderID.

3.4.3. Crear la aplicación cliente de .NET Framework Esta es la tarea final del tutorial rápido de WCF Data Services . En esta tarea, agregará una aplicación de consola a la solución, agregará una referencia a la fuente de Open Data Protocol (OData) en esta nueva aplicación cliente y obtendrá acceso a la fuente de OData desde la aplicación cliente usando las clases del servicio de datos del cliente y las bibliotecas de cliente generadas. Nota: No es necesaria una aplicación cliente basada en .NET Framework para obtener acceso a una fuente de datos. Cualquier componente de aplicación que utilice una fuente de OData puede tener acceso al servicio de datos.

Para crear la aplicación cliente mediante Visual Studio 1. 2. 3. 4.

En el Explorador de soluciones, haga clic con el botón secundario en la solución, después haga clic en Agregar y, a continuación, en Nuevo proyecto. En Tipos de proyecto, haga clic en Windows y, a continuación, seleccione Aplicación WPF en el recuadro Templates. Escriba NorthwindClient como nombre del proyecto y haga clic en Aceptar. Abra el archivo Window1.xaml y reemplace el código XAML por el código siguiente:







MCT: Luis Dueñas

Pag 12 de 128

WCF Data Service

Order:

Save Changes

Close



Para agregar al proyecto una referencia al servicio de datos 1.

Haga clic con el botón secundario en el proyecto NorthwindClient, haga clic en Agregar referencia de servicio y, a continuación, haga clic en Detectar. De este modo se muestra el servicio de datos de Northwind que creó en la primera tarea.

2.

En el cuadro de texto Espacio de nombres, escriba Northwind y, a continuación, haga clic en Aceptar. De este modo se agrega un nuevo archivo de código al proyecto, que contiene las clases de datos que se usan para obtener acceso e interactuar con los recursos del servicio de datos como objetos. Las clases de datos se crean en el espacio de nombres NorthwindClient.Northwind.

Para tener acceso a los datos del servicio de datos en la aplicación WPF 1. 2.

En el Explorador de soluciones, en NorthwindClient, haga clic con el botón secundario en el proyecto y haga clic en Agregar referencia. En el cuadro de diálogo Agregar referencia, haga clic en la pestaña .NET, seleccione el ensamblado System.Data.Services.Client.dll y, a continuación, haga clic en Aceptar. En el Explorador de soluciones, en NorthwindClient, abra la página de código para el archivo Window1.xaml y agregue la instrucción using siguiente (Imports en Visual Basic). Imports System.Data.Services.Client Imports NorthwindClient.Northwind

3.

Inserte el código siguiente que consulta al servicio de datos y enlaza el resultado a una instancia de DataServiceCollection en la clase Window1: Nota: Debe reemplazar el nombre de host localhost:12345 por el servidor y el puerto en que se hospeda la instancia del servicio de datos de Northwind. Private context As NorthwindEntities Private customerId As String = "ALFKI" ' Replace the host server and port number with the values ' for the test server hosting your Northwind data service instance. Private svcUri As Uri = New Uri("http://localhost:12345/Northwind.svc")

MCT: Luis Dueñas

Pag 13 de 128

WCF Data Service Private Sub Window1_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs) Try ' Instantiate the DataServiceContext. context = New NorthwindEntities(svcUri) ' Define a LINQ query that returns Orders and ' Order_Details for a specific customer. Dim ordersQuery = From o In context.Orders.Expand("Order_Details") _ Where o.Customer.CustomerID = customerId _ Select o ' Create an DataServiceCollection(Of T) based on ' execution of the LINQ query for Orders. Dim customerOrders As DataServiceCollection(Of Order) = New _ DataServiceCollection(Of Order)(ordersQuery) ' Make the DataServiceCollection the binding source for the Grid. Me.orderItemsGrid.DataContext = customerOrders Catch ex As Exception MessageBox.Show(ex.ToString()) End Try End Sub

4.

Inserte el código siguiente que sirve para guardar los cambios en la clase Window1: Private Sub buttonSaveChanges_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Try ' Save changes made to objects tracked by the context. context.SaveChanges() Catch ex As DataServiceRequestException MessageBox.Show(ex.ToString()) End Try End Sub Private Sub buttonClose_Click(ByVal sender As Object, ByVal a As RoutedEventArgs) Me.Close() End Sub

Para compilar y ejecutar la aplicación NorthwindClient 1. 2.

En el Explorador de soluciones, haga clic con el botón secundario en el proyecto NorthwindClient y seleccione Establecer como proyecto de inicio. Presione F5 para iniciar la aplicación. Se compila la solución y se inicia la aplicación cliente. Los datos se recuperan del servicio y se muestran en la consola.

3.

Edite un valor de la columna Cantidad de la cuadrícula de datos y, a continuación, haga clic en Guardar. Los cambios se guardan en el servicio de datos. Nota: Esta versión de la aplicación NorthwindClient no admite agregar ni eliminar entidades.

3.5. Escenarios de las aplicaciones WCF Data Services admite un conjunto básico de escenarios para exponer y utilizar datos cuando fuentes de Open Data Protocol (OData) . Este tema remite a los temas importantes en estos escenarios.

MCT: Luis Dueñas

Pag 14 de 128

WCF Data Service Exponga datos relacionales de una base de datos como una fuente de OData . Tutorial rápido (WCF Data Services) Exponer los datos como servicio (WCF Data Services) Cómo: Crear un servicio de datos mediante un origen de datos de ADO.NET Entity Framework (WCF Data Services) Exponga clases de datos de CLR arbitrarias como una fuente de OData . Exponer los datos como servicio (WCF Data Services) Cómo: Crear un servicio de datos mediante el proveedor de reflexión (WCF Data Services) Proveedores de servicios de datos (WCF Data Services) Utilice una fuente de OData en una aplicación cliente basada en .NET Framework. Tutorial rápido (WCF Data Services) Usar un servicio de datos en una aplicación cliente (WCF Data Services) Biblioteca de cliente de WCF Data Services Utilice una fuente de OData en una aplicación cliente basada en Silverlight. WCF Data Services (Silverlight) Operaciones asincrónicas (WCF Data Services) How to: Bind Data Service Data to Controls (WCF Data Services/Silverlight) Utilice una fuente de OData en una aplicación cliente basada en AJAX. Usar un servicio de datos en una aplicación cliente (WCF Data Services) OData Cree una solución de datos de extremo a extremo que use OData para transferir datos entre el cliente y el servidor. Tutorial rápido (WCF Data Services) Usar un servicio de datos en una aplicación cliente (WCF Data Services) Biblioteca de cliente de WCF Data Services Cree una aplicación cliente basada en .NET Framework utiliza una fuente de OData de forma asincrónica para evitar los problemas de latencia en el cliente. Cómo: Ejecutar consultas de servicio de datos asincrónicos (WCF Data Services) Operaciones asincrónicas (WCF Data Services) WCF Data Services (Silverlight) Exponga y utilice una fuente de OData con un objeto binario grande al que se obtiene acceso y se cambia como una secuencia. Proveedores de transmisión por secuencias (WCF Data Services) Trabajar con datos binarios (Servicios de datos de WCF) Enlace fuentes de OData a controles en una aplicación de Windows Presentation Framework (WPF). Enlazar datos a controles (WCF Data Services) Cómo: Enlazar datos a los elementos de Windows Presentation Foundation (WCF Data Services) Cómo: Enlazar datos mediante un origen de datos del proyecto (WCF Data Services) Intercepte los mensajes que entran al servicio de datos para realizar la validación de datos y el filtrado de consultas basado en roles.

MCT: Luis Dueñas

Pag 15 de 128

WCF Data Service Cómo: Interceptar mensajes del servicio de datos (WCF Data Services) Interceptores (WCF Data Services) Cree extremos en un servicio de datos para habilitar comportamientos de servicio personalizados. Cómo: Definir una operación de servicio (WCF Data Services) Operaciones de servicio (WCF Data Services)

4. Definir WCF Data Services En esta sección se describe cómo crear y configurar WCF Data Services para exponer datos como fuente Open Data Protocol (OData) .

4.1. Configurar el servicio de datos Con WCF Data Services , puede crear servicios de datos que expongan fuentes de Open Data Protocol (OData) . Los datos de estas fuentes pueden provenir de distintos orígenes de datos. WCF Data Services usa proveedores de datos para exponer estos datos como fuente de OData . Estos proveedores incluyen un proveedor de Entity Framework , un proveedor de reflexión y un conjunto de interfaces de proveedor de servicio de datos personalizados. La implementación del proveedor define el modelo de datos del servicio. En WCF Data Services , un servicio de datos es una clase que hereda de la clase DataService, donde el tipo del servicio de datos es el contenedor de entidades del modelo de datos. Este contenedor de entidades tiene una o varias propiedades que devuelven una interfaz IQueryable, que se usa para tener acceso a los conjuntos de entidades del modelo de datos. Los miembros de la clase DataServiceConfiguration definen los comportamientos del servicio de datos junto con los miembros de la clase DataServiceBehavior, a la que se tiene acceso desde la propiedad DataServiceBehavior de la clase DataServiceConfiguration. La clase DataServiceConfiguration se proporciona al método InitializeService que implementa el servicio de datos, como en la siguiente implementación de un servicio de datos de Northwind: ' This method is called only once to initialize service-wide policies. Public Shared Sub InitializeService(ByVal config As DataServiceConfiguration) ' Set the access rules of feeds exposed by the data service, which is ' based on the requirements of client applications. config.SetEntitySetAccessRule("Customers", EntitySetRights.ReadSingle) config.SetEntitySetAccessRule("Employees", EntitySetRights.ReadSingle) config.SetEntitySetAccessRule("Orders", EntitySetRights.AllRead _ And EntitySetRights.WriteAppend _ And EntitySetRights.WriteMerge) config.SetEntitySetAccessRule("Order_Details", EntitySetRights.All) config.SetEntitySetAccessRule("Products", EntitySetRights.ReadMultiple) ' Set page size defaults for the data service. config.SetEntitySetPageSize("Orders", 20) config.SetEntitySetPageSize("Order_Details", 50) config.SetEntitySetPageSize("Products", 50) ' Paging requires v2 of the OData protocol. config.DataServiceBehavior.MaxProtocolVersion = _ System.Data.Services.Common.DataServiceProtocolVersion.V2 End Sub

Configuración del servicio de datos La clase DataServiceConfiguration permite especificar los comportamientos siguientes del servicio de datos. Miembro Comportamiento

MCT: Luis Dueñas

Pag 16 de 128

WCF Data Service AcceptCountRequests

Permite deshabilitar las solicitudes de recuento que se envían al servicio de datos usando el segmento de ruta de acceso $count y la opción de consulta $inlinecount.

AcceptProjectionRequests

Permite deshabilitar la compatibilidad con la proyección de los datos en las solicitudes que se envían al servicio de datos usando la opción de consulta $select.

EnableTypeAccess

Permite exponer un tipo de datos en los metadatos para un proveedor de metadatos dinámico definido mediante la interfaz IDataServiceMetadataProvider.

EnableTypeConversion

Permite especificar si el motor en tiempo de ejecución del servicio de datos debe convertir el tipo contenido de la carga en el tipo de propiedad real que se especifica en la solicitud.

Permite especificar si se invocan o no interceptores de cambio registrados InvokeInterceptorsOnLinkDelete en las entidades relacionadas cuando se elimina un vínculo de relación entre dos entidades. MaxBatchCount

Permite limitar el número de conjuntos de cambios y operaciones de consulta que se permiten en un solo lote.

MaxChangesetCount

Permite limitar el número máximo de cambios que se pueden incluir en un solo conjunto de cambios.

MaxExpandCount

Permite limitar el tamaño de una respuesta limitando el número de entidades relacionadas que pueden incluirse en una sola solicitud utilizando el operador de consulta $expand.

MaxExpandDepth

Permite limitar el tamaño de una respuesta limitando la profundidad del gráfico de entidades relacionadas que pueden incluirse en una sola solicitud utilizando el operador de consulta $expand.

MaxObjectCountOnInsert

Permite limitar el número de entidades que se van a insertar que puede contener una sola solicitud POST.

MaxProtocolVersion

Define la versión del protocolo Atom utilizado por el servicio de datos. Cuando el valor de la propiedad MaxProtocolVersion se establece en un valor menor que el valor máximo de la enumeración DataServiceProtocolVersion, la funcionalidad más reciente de WCF Data Services no está disponible para los clientes que tengan acceso al servicio de datos.

MaxResultsPerCollection

Permite limitar el tamaño de una respuesta limitando el número de entidades de cada conjunto de entidades que se devuelve como fuente de distribución de datos.

RegisterKnownType

Agrega un tipo de datos a la lista de tipos reconocidos por el servicio de datos.

SetEntitySetAccessRule

Establece los derechos de acceso para los recursos del conjunto de entidades que están disponibles en el servicio de datos. Se puede proporcionar el valor asterisco (* para el parámetro de nombre para establecer el acceso en el mismo nivel para todos los conjuntos de entidades restantes. Se recomienda establecer el acceso a los conjuntos de entidades para proporcionar el acceso con privilegios mínimos a los recursos del servicio de datos requeridos por las aplicaciones cliente.

SetEntitySetPageSize

Establece el tamaño de página máximo de un recurso de conjunto de entidades.

Establece los derechos de acceso para las operaciones de servicio definidas en el servicio de datos. Se puede proporcionar el valor asterisco (*) para el parámetro de nombre con objeto de establecer el acceso en el mismo nivel SetServiceOperationAccessRule para todas las operaciones de servicio. Se recomienda establecer el acceso a las operaciones de servicio para proporcionar el acceso con privilegios mínimos a los recursos del servicio de datos requeridos por las aplicaciones cliente.

MCT: Luis Dueñas

Pag 17 de 128

WCF Data Service

UseVerboseErrors

Esta propiedad de configuración le permite solucionar más fácilmente los problemas de un servicio de datos devolviendo más información en el mensaje de respuesta del error. Esta opción no está pensada para su uso en un entorno de producción.

Requisitos mínimos de acceso a recursos En la siguiente tabla se detallan los derechos mínimos del conjunto de entidades que se deben conceder para ejecutar una operación concreta. Los ejemplos de ruta de acceso se basan en el servicio de datos de Northwind que se crea al completar el tutorial rápido. Dado que la enumeración EntitySetRights y la enumeración ServiceOperationRights se definen usando la clase FlagsAttribute, puede usar un operador OR lógico con el fin de especificar varios permisos para una operación o un conjunto de entidades únicos. Ruta de acceso/acción GET DELETE MERGE POST PUT

/Customers

ReadMultiple No compatible

/Customers('ALFKI')

ReadSingle

No compatible

ReadSingle y ReadSingle y ReadSingle y no disponible WriteDelete WriteMerge WriteReplace

Customers:

Customers: ReadSingle /Customers('ALFKI')/Orders

-y-

No compatible

No compatible

Orders: ReadMultiple Customers: ReadSingle /Customers('ALFKI')/ Orders(10643)

-y-

Orders: ReadSingle Customers: ReadSingle /Orders(10643)/Customer

-y-

Orders: ReadSingle

Orders

-y-

ReadSingle y WriteMerge o WriteReplace -y-

No compatible

Orders : y WriteAppend Customers: ReadSingle

Customers: ReadSingle

-y-

-y-

Orders:

Orders:

Customers: ReadSingle No compatible

-y-

Orders:

ReadSingle y ReadSingle y WriteDelete WriteMerge

ReadSingle y WriteReplace

Customers: Customers: Customers: ReadSingle y ReadSingle y WriteAppend WriteDelete WriteMerge; -yNo -y-ycompatible Orders:

Orders:

ReadSingle

ReadSingle

Customers: ReadSingle /Customers('ALFKI')/$links/

No compatible

WriteAppend

No compatible

No compatible

Orders: ReadMultiple

Orders: WriteAppend y ReadSingle Customers: ReadSingle y WriteMerge o WriteReplace -y-

No compatible

Orders: ReadSingle

/Customers('ALFKI')/$links/ MCT: Luis Dueñas

Customers:

Customers:

No

No compatible No

Pag 18 de 128

WCF Data Service Orders(10643)

ReadSingle -y-

Orders: ReadSingle

ReadSingle y compatible WriteMerge o WriteReplace -y-

Orders: ReadSingle Customers: ReadSingle

Customers: ReadSingle /Orders(10643)/$links/ Customer

/Customers/$count /Customers('ALFKI')/ ContactName /Customers('ALFKI')/Address/ StreetAddress/$value 1 /Customers('ALFKI')/ ContactName/$value

compatible

Customers: ReadSingle;

Orders: -yReadSingle y - y -yNo compatible WriteMerge o WriteReplace Orders: Orders: Orders: ReadSingle y ReadSingle y ReadSingle WriteMerge WriteReplace No No ReadMultiple No compatible No compatible compatible compatible ReadSingle

No compatible WriteMerge No compatible WriteReplace

ReadSingle

WriteDelete

ReadSingle

ReadSingle y WriteMerge No compatible WriteReplace WriteDelete

/Customers('ALFKI')/$value 2 ReadSingle

No compatible

No compatible

No compatible

No compatible

No compatible

No compatible WriteReplace

No compatible

No compatible

No Customers: WriteAppend compatible

No compatible

No compatible

No compatible

Customers: ReadSingle /Customers?$select=Orders/ *&$expand=Orders

-y-

Orders: ReadMultiple Customers: ReadSingle /Customers('ALFKI')?$select= Orders/ *&$expand=Orders

-y-

No compatible

Orders: ReadMultiple 1 En este ejemplo, Address representa una propiedad de tipo complejo de la entidad Customers que tiene una propiedad denominada StreetAddress. El modelo utilizado por los servicios de datos de Northwind no define este tipo complejo explícitamente. Cuando el modelo de datos se define con el proveedor de Entity Framework , puede usar las herramientas de Entity Data Model para definir este tipo complejo. 2 Se admite este URI cuando una propiedad que devuelve un objeto binario grande (BLOB) se define como el recurso multimedia que pertenece a una entidad que es una entrada de vínculo multimedia que, en este caso, es Customers.

Requisitos de control de versiones

MCT: Luis Dueñas

Pag 19 de 128

WCF Data Service Los siguientes comportamientos de configuración del servicio de datos requieren la versión 2 del protocolo OData o versiones posteriores: Compatibilidad para las solicitudes de recuento. Compatibilidad para la opción de consulta $select de proyección.

4.1.1. Cómo: Desarrollar un servicio de datos WCF que se ejecuta en IIS En este tema se muestra cómo utilizar WCF Data Services para crear un servicio de datos basado en la base de datos de ejemplo Northwind que está hospedada en una aplicación web ASP.NET que se ejecuta en Internet Information Services (IIS). Nota: Para crear el servicio de datos de Northwind, debe tener instalada la base de datos de ejemplo Northwind en su equipo local. En este tema se muestra cómo crear un servicio de datos utilizando el proveedor de Entity Framework. Están disponibles otros proveedores de servicios de datos. Una vez creado el servicio, debe proporcionar acceso a los recursos del servicio de datos de forma explícita.

Para crear la aplicación web ASP.NET que se ejecuta en IIS 1. 2. 3. 4. 5. 6. 7. 8. 9.

En Visual Studio, en el menú Archivo, seleccione Nuevo y, a continuación, seleccione Proyecto. En el cuadro de diálogo Nuevo proyecto, seleccione Visual Basic o Visual C# como el lenguaje de programación. En el recuadro Plantillas, seleccione Aplicación web ASP.NET. Nota: si usa Visual Studio Web Developer, tendrá que crear un nuevo sitio web en lugar de una nueva aplicación web. Escriba NorthwindService como nombre del proyecto. Haga clic en Aceptar. En el menú Proyecto, seleccione Propiedades de NorthwindService. Seleccione la pestaña Web y a continuación, haga clic en Usar servidor web de IIS local. Haga clic en Crear directorio virtual y, a continuación, en Aceptar. Ejecute el siguiente comando en el símbolo del sistema: "%windir%\Microsoft.NET\Framework\v3.0\Windows Communication Foundation\ServiceModelReg.exe" -i

De este modo, se garantiza que Windows Communication Foundation (WCF) se registra en el equipo. 10. Mediante SQL Server Management Studio o la utilidad sqlcmd.exe, ejecute el siguiente comando de Transact-SQL con la instancia de SQL Server que tenga la base de datos Northwind asociada: CREATE LOGIN [NT AUTHORITY\NETWORK SERVICE] FROM WINDOWS; GO

De este modo, se crea un inicio de sesión en la instancia de SQL Server para la cuenta de Windows utilizada para ejecutar IIS. Esto permite a IIS conectarse a la instancia de SQL Server. 11. Con la base de datos Northwind asociada, ejecute los siguientes comandos de Transact-SQL: USE Northwind GO CREATE USER [NT AUTHORITY\NETWORK SERVICE] FOR LOGIN [NT AUTHORITY\NETWORK SERVICE] WITH DEFAULT_SCHEMA=[dbo]; GO ALTER LOGIN [NT AUTHORITY\NETWORK SERVICE] WITH DEFAULT_DATABASE=[Northwind]; GO EXEC sp_addrolemember 'db_datareader', 'NT AUTHORITY\NETWORK SERVICE' GO EXEC sp_addrolemember 'db_datawriter', 'NT AUTHORITY\NETWORK SERVICE' GO

MCT: Luis Dueñas

Pag 20 de 128

WCF Data Service Esto otorga derechos al inicio de sesión nuevo, lo que permite a IIS leer y escribir datos en la base de datos Northwind.

Para definir el modelo de datos 1. 2. 3. 4. 5.

6. 7.

En el Explorador de soluciones, haga clic con el botón secundario en el nombre del proyecto de ASP.NET y, a continuación, seleccione Agregar nuevo elemento. En el cuadro de diálogo Agregar nuevo elemento, seleccione ADO.NET Entity Data Model. Como nombre del modelo de datos, escriba Northwind.edmx. En el Asistente para Entity Data Model, seleccione Generar desde la base de datos y, a continuación, haga clic en Siguiente. Para conectar el modelo de datos a la base de datos efectúe uno de los pasos siguientes y después haga clic en Siguiente: o Si no tiene una conexión de base de datos ya configurada, haga clic en Nueva conexión y cree una conexión nueva. Esta instancia de SQL Server debe tener adjuntada la base de datos de ejemplo Northwind. o bien o Si tiene una conexión de base de datos ya configurada para conectarse a la base de datos Northwind, seleccione esa conexión en la lista de conexiones. En la página final del asistente, seleccione las casillas de todas las tablas de la base de datos y desactive las casillas correspondientes a las vistas y los procedimientos almacenados. Haga clic en Finalizar para cerrar el asistente. Nota: Este modelo de datos generado expone las propiedades de clave externa en los tipos de entidad. Los modelos de datos creados mediante Visual Studio 2008 no incluyen estas propiedades de clave externa. Debido a esto, debe actualizar las clases del servicio de datos de cliente de cualquier aplicación cliente que se haya creado para tener acceso al servicio de datos de Northwind que se creó con Visual Studio 2008 antes de intentar tener acceso a esta versión del servicio de datos de Northwind.

Para crear el servicio de datos 1. 2. 3.

En el Explorador de soluciones, haga clic con el botón secundario en el nombre del proyecto de ASP.NET y, a continuación, seleccione Agregar nuevo elemento. En el cuadro de diálogo Agregar nuevo elemento, seleccione Servicio de datos de ADO.NET. Como nombre del servicio, escriba Northwind. Visual Studio crea los archivos de código y marcado XML para el nuevo servicio. De forma predeterminada, se abre la ventana del editor de código. En el Explorador de soluciones, el servicio tendrá el nombre Northwind, con la extensión .svc.cs o .svc.vb.

4.

En el código para el servicio de datos, reemplace el comentario /* TODO: put your data source

class name here */ de la definición de la clase que define el servicio de datos por el tipo que es el contenedor de entidades del modelo de datos, que en este caso es NorthwindEntities. La definición de la clase debería ser como la siguiente: Public Class Northwind Inherits DataService(Of NorthwindEntities)

4.1.2. Cómo: Habilitar el acceso al servicio de datos En WCF Data Services , debe permitir explícitamente el acceso a los recursos expuestos por un servicio de datos. Esto significa que después de crear un nuevo servicio de datos, debe proporcionar explícitamente acceso a los recursos individuales como conjuntos de entidades. En este tema se muestra cómo permitir el acceso de lectura y escritura a cinco conjuntos de entidades del servicio de datos de Northwind que se crea

MCT: Luis Dueñas

Pag 21 de 128

WCF Data Service al completar el tutorial rápido. Dado que la enumeración EntitySetRights se define utilizando FlagsAttribute, puede utilizar un operador OR lógico para especificar varios permisos para un único conjunto de entidades. Nota: Cualquier cliente que pueda tener acceso a la aplicación ASP.NET también puede tener acceso a los recursos expuestos por el servicio de datos. En un servicio de datos de producción, para evitar el acceso no autorizado a los recursos, también debería proteger la aplicación.

Para habilitar el acceso al servicio de datos En el código del servicio de datos, reemplace el código de marcador de posición de la función

InitializeService por el siguiente: ' Grant only the rights needed to support the client application. config.SetEntitySetAccessRule("Orders", EntitySetRights.AllRead _ Or EntitySetRights.WriteMerge _ Or EntitySetRights.WriteReplace) config.SetEntitySetAccessRule("Order_Details", EntitySetRights.AllRead _ Or EntitySetRights.AllWrite) config.SetEntitySetAccessRule("Customers", EntitySetRights.AllRead)

De esta forma, los clientes pueden tener acceso de lectura y escritura a los conjuntos de entidades de Orders y Order_Details, y acceso de solo lectura a los conjuntos de entidades de Customers.

4.1.3. Como: Habilitar la paginación de los resultados del servicio de datos WCF Data Services le permite limitar el número de entidades devueltas por una consulta al servicio de datos. Los límites de página se definen en el método que se llama cuando se inicializa el servicio y se pueden establecer por separado para cada conjunto de entidades. Si está habilitada la paginación, la última entrada de la fuente contiene un vínculo a la página de datos siguiente. En este tema se muestra cómo modificar un servicio de datos para habilitar la paginación de los conjuntos de entidades Customers y Orders devueltos. En el ejemplo de este tema se usa el servicio de datos de ejemplo Northwind.

Cómo habilitar la paginación de los conjuntos de entidades Customers y Orders devueltos En el código del servicio de datos, reemplace el código de marcador de posición de la función InitializeService por el siguiente: ' Set page size defaults for the data service. config.SetEntitySetPageSize("Orders", 20) config.SetEntitySetPageSize("Order_Details", 50) config.SetEntitySetPageSize("Products", 50) ' Paging requires v2 of the OData protocol. config.DataServiceBehavior.MaxProtocolVersion = _ System.Data.Services.Common.DataServiceProtocolVersion.V2

4.2. Proveedores de servicios de datos WCF Data Services admite varios modelos de proveedor para exponer los datos como una fuente Open Data Protocol (OData) . En este tema se proporciona información con el fin de permitirle seleccionar el proveedor de WCF Data Services más adecuado para su origen de datos.

Proveedores de orígenes de datos MCT: Luis Dueñas

Pag 22 de 128

WCF Data Service WCF Data Services admite los siguientes proveedores para definir el modelo de datos de un servicio de datos. Proveedor Descripción

Proveedor de Entity Framework

Este proveedor utiliza ADO.NET Entity Framework para permitir el uso de datos relacionales con un servicio de datos definiendo un modelo de datos que se asigne a datos relacionales. Su origen de datos puede ser SQL Server o cualquier otro origen de datos con compatibilidad de otros proveedores para Entity Framework. Debería usar el proveedor de Entity Framework cuando tenga un origen de datos relacional, como una base de datos SQL Server.

Proveedor de reflexión

Este proveedor utiliza la reflexión para permitir la definición de un modelo de datos basado en las clases de datos existente que se pueden exponer como instancias de la interfaz IQueryable. Las actualizaciones se habilitan implementando la interfaz IUpdatable. Debe usar este proveedor cuando tenga clases de datos estáticas que se definen en tiempo de ejecución, como aquellas generadas por LINQ para SQL o las definidas por un conjunto de datos con tipo.

Proveedores de servicios de datos personalizados

WCF Data Services incluye un conjunto de proveedores que le permiten definir de forma dinámica un modelo de datos basado en tipos de datos enlazados en tiempo de ejecución. Debería implementar estas interfaces cuando no se conozcan los datos que se están exponiendo, cuando se esté diseñando la aplicación o cuando los proveedores de Entity Framework o de reflexión no sean suficientes.

Otros proveedores de servicios de datos WCF Data Services tiene el siguiente proveedor de servicios de datos adicional que mejora el rendimiento de un origen de datos definido mediante uno de los otros proveedores. Proveedor Descripción Proveedor de transmisiones por secuencias

Este proveedor permite exponer tipos de datos de objetos binarios grandes usando WCF Data Services . Si implementa la interfaz IDataServiceStreamProvider, se crea un proveedor de transmisiones por secuencias. Este proveedor se puede implementar junto con cualquier proveedor de orígenes de datos.

4.2.1. Proveedor de Entity Framework Al igual que ocurre con WCF Data Services , ADO.NET Entity Framework está basado en Entity Data Model, que es un tipo de modelo entidad-relación. Entity Framework traduce las operaciones efectuadas sobre su implementación de Entity Data Model, que se denomina el modelo conceptual, en operaciones equivalentes sobre un origen de datos. De esta forma, Entity Framework se convierte en un proveedor ideal para los servicios de datos basados en datos relacionales y se puede usar cualquier base de datos con un proveedor de datos que admita Entity Framework con WCF Data Services . En un modelo conceptual, el contenedor de entidades es la raíz del servicio. Debe definir un modelo conceptual en Entity Framework antes de que un servicio de datos pueda exponer los datos. WCF Data Services admite el modelo de simultaneidad optimista al permitirle definir un token de simultaneidad para una entidad. El servicio de datos utiliza este token de simultaneidad, que incluye una o más propiedades de la entidad, para determinar si se ha producido un cambio en los datos que se solicitan, que se están actualizando o eliminando. Cuando los valores de token obtenidos de la eTag de la solicitud difieren de los valores actuales de la entidad, el servicio de datos inicia una excepción. Para indicar que una propiedad forma parte del token de simultaneidad, debe aplicar el atributo ConcurrencyMode="Fixed" en el modelo de datos definido por el proveedor de Entity Framework . El token de simultaneidad no puede incluir ninguna propiedad clave ni de navegación.

4.2.1.1. Cómo: Crear un servicio de datos mediante un origen de datos de ADO.NET Entity Framework

MCT: Luis Dueñas

Pag 23 de 128

WCF Data Service WCF Data Services expone los datos de entidad como servicio de datos. ADO.NET Entity Framework proporciona estos datos de entidad cuando el origen de datos es una base de datos relacional. En este tema se muestra cómo crear un modelo de datos basado en Entity Framework en una aplicación web de Visual Studio que está basada en una base de datos existente y cómo usar este modelo de datos para crear un nuevo servicio de datos. Entity Framework también proporciona una herramienta de línea de comandos que puede generar un modelo de Entity Framework fuera de un proyecto de Visual Studio.

Para agregar un modelo de Entity Framework que está basado en una base de datos existente a una aplicación web existente 1. 2. 3.

En el menú Proyecto, haga clic en Agregar nuevo elemento. En el recuadro Plantillas, haga clic en la categoría Datos y, a continuación, seleccione ADO.NET Entity Data Model. Escriba el nombre del modelo y, a continuación, haga clic en Agregar. Se muestra la primera página del Asistente para Entity Data Model.

4. 5. 6.

En el cuadro de diálogo Elegir contenido del modelo, seleccione Generar desde la base de datos. Después, haga clic en Siguiente. Haga clic en el botón Nueva conexión. En el cuadro de diálogo Propiedades de la conexión, escriba el nombre del servidor, seleccione el método de autenticación, escriba el nombre de la base de datos y, a continuación, haga clic en Aceptar. El cuadro de diálogo Elegir la conexión de datos se actualiza con la configuración de la conexión de la base de datos.

7. 8.

Asegúrese de que está activada la casilla Guardar configuración de conexión de la entidad en App.Config como. Después, haga clic en Siguiente. En el cuadro de diálogo Elija los objetos de base de datos, seleccione todos los objetos de base de datos que piensa exponer en el servicio de datos. Nota: El servicio de datos no expone automáticamente los objetos incluidos en el modelo de datos. El propio servicio debe exponerlos explícitamente.

9.

Haga clic en Finalizar para completar el asistente. Con esto se crea un modelo de datos predeterminado basado en una base de datos específica. Entity Framework permite personalizar el modelo de datos.

Para crear el servicio de datos usando el nuevo modelo de datos 1. 2. 3. 4. 5.

En Visual Studio, abra el archivo .edmx que representa el modelo de datos. En el Explorador de modelos, haga clic con el botón secundario en el modelo, haga clic en Propiedades y, a continuación, anote el nombre del contenedor de entidades. En el Explorador de soluciones, haga clic con el botón secundario en el nombre del proyecto de ASP.NET y, a continuación, seleccione Agregar nuevo elemento. En el cuadro de diálogo Agregar nuevo elemento, seleccione Servicio de datos de ADO.NET. Proporcione un nombre para el servicio y, a continuación, haga clic en Aceptar. Visual Studio crea los archivos de código y marcado XML para el nuevo servicio. De forma predeterminada, se abre la ventana del editor de código.

MCT: Luis Dueñas

Pag 24 de 128

WCF Data Service 6.

En el código para el servicio de datos, reemplace el comentario /* TODO: put your data source

7.

class name here */ de la definición de la clase que define el servicio de datos por el tipo que hereda de la clase ObjectContext y que es el contenedor de entidades del modelo de datos, que anotó en el paso 2. En el código del servicio de datos, permita a los clientes autorizados tener acceso a los conjuntos de entidades que expone el servicio de datos. Para probar el servicio de datos Northwind.svc usando un explorador web, siga las instrucciones del tema Obtener acceso al servicio desde un explorador web (Tutorial rápido de WCF Data Services).

8.

4.2.2. Proveedor de reflexión Además de exponer los datos de un modelo de datos a través de Entity Framework, WCF Data Services puede exponer los datos que no estén definidos estrictamente en un modelo basado en entidad. El proveedor de reflexión expone los datos en clases que devuelven tipos que implementan la interfaz IQueryable. WCF Data Services usa la reflexión con el fin de deducir un modelo de datos para estas clases y puede convertir las consultas basadas en direcciones realizadas en recursos en consultas basadas en el lenguaje de consulta integrado (LINQ) realizadas en los tipos IQueryable expuestos. Nota: Puede utilizar el método AsQueryable para devolver una interfaz de IQueryable de cualquier clase que implemente la interfaz de IEnumerable. Esto permite usar tipos de colección más genéricos como origen de datos para un servicio de datos. El proveedor de reflexión admite las jerarquías de tipos.

Deducir el modelo de datos Al crear el servicio de datos, el proveedor deduce el modelo de datos por medio de la reflexión. En la lista siguiente se muestra cómo el proveedor de reflexión deduce el modelo de datos: Contenedor de entidades: clase que expone los datos como propiedades que devuelven una instancia de IQueryable. Al direccionar un modelo de datos basado en reflexión, el contenedor de entidades representa la raíz del servicio. Solo se admite una clase de contenedor de entidades para un espacio de nombres determinado. Conjuntos de entidades: las propiedades que devuelven instancias de IQueryable se tratan como conjuntos de entidades. Los conjuntos de entidades se direccionan directamente como recursos en la consulta. Solo una propiedad del contenedor de entidades puede devolver una instancia de IQueryable de un tipo determinado. Tipos de entidad: el tipo T del IQueryable que devuelve el tipo de entidad. El proveedor de reflexión convierte las clases que forman parte de una jerarquía de herencia en una jerarquía de tipos de entidad equivalente. Claves de entidad: cada clase de datos que es un tipo de entidad debe tener una propiedad clave. Esta propiedad recibe el atributo DataServiceKeyAttribute ([DataServiceKeyAttribute]). Nota: Solo debe aplicar el atributo DataServiceKeyAttribute a una propiedad que se pueda usar para identificar una instancia de forma única del tipo de entidad. Este atributo se ignora cuando se aplica a una propiedad de navegación. Propiedades de tipo de entidad: aparte de la clave de entidad, el proveedor de reflexión trata las propiedades accesibles no de indizador de una clase que no sea un tipo de entidad como se explica a continuación:

MCT: Luis Dueñas

Pag 25 de 128

WCF Data Service o

Si la propiedad devuelve un tipo primitivo, se supone que es una propiedad de un tipo de entidad.

o

Si la propiedad devuelve un tipo que también es un tipo de entidad, se supone que es una propiedad de navegación que representa el extremo "uno" de una relación varios a uno o uno a uno.

o

Si la propiedad devuelve un IEnumerable de un tipo de entidad, se supone que es una propiedad de navegación que representa el extremo "varios" de una relación uno a uno o varios a varios.

o

Si el tipo devuelto de la propiedad es un tipo de valor, la propiedad representa un tipo complejo.

Nota: A diferencia de un modelo de datos que está basado en el modelo de entidad-relación, los modelos que están basados en el proveedor de reflexión no entienden los datos relacionales. Debe usar Entity Framework para exponer los datos relacionales a través de WCF Data Services .

Asignación de tipos de datos Cuando un modelo de datos se deduce de las clases de .NET Framework, los tipos primitivos del modelo se asignan a los tipos de datos de .NET Framework de la siguiente forma: Tipo de datos de .NET Framework Tipo del modelo de datos Byte []

Edm.Binary

Boolean

Edm.Boolean

Byte

Edm.Byte

DateTime

Edm.DateTime

Decimal

Edm.Decimal

Double

Edm.Double

Guid

Edm.Guid

Int16

Edm.Int16

Int32

Edm.Int32

Int64

Edm.Int64

SByte

Edm.SByte

Single

Edm.Single

String

Edm.String

Nota: Los tipos de valor NULL de .NET Framework se asignan a los mismo tipos del modelo de datos que los tipos de valor correspondientes a los que no se puede asignar un valor NULL.

Habilitar las actualizaciones en el modelo de datos Para permitir las actualizaciones de los datos que se exponen a través de este tipo de modelo de datos, el proveedor de reflexión define una interfaz IUpdatable. Esta interfaz indica al servicio de datos cómo conservar las actualizaciones de los tipos expuestos. Para permitir las actualizaciones de los recursos que define el modelo de datos, la clase de contenedor de entidades debe implementar la interfaz de IUpdatable. La interfaz IUpdatable necesita la implementación de los siguientes miembros para que las actualizaciones puedan propagarse al origen de datos por medio del proveedor de reflexión: Miembro Descripción AddReferenceToCollection

Proporciona la funcionalidad para agregar un objeto a una colección de objetos relacionados a los que se tiene acceso desde una propiedad de navegación.

ClearChanges

Proporciona la funcionalidad que cancela los cambios pendientes de los

MCT: Luis Dueñas

Pag 26 de 128

WCF Data Service datos. CreateResource

Proporciona la funcionalidad para crear un recurso nuevo en el contenedor especificado.

DeleteResource

Proporciona la funcionalidad para eliminar un recurso.

GetResource

Proporciona la funcionalidad para recuperar un recurso que se identifica por una consulta y un nombre de tipo concretos.

GetValue

Proporciona la funcionalidad para devolver el valor de una propiedad de un recurso.

Proporciona la funcionalidad para quitar un objeto de una colección de RemoveReferenceFromCollection objetos relacionados a los que se tiene acceso desde una propiedad de navegación. ResetResource

Proporciona la funcionalidad para actualizar un recurso especificado.

ResolveResource

Proporciona la funcionalidad para devolver el recurso que se representa por una instancia de objeto concreta.

SaveChanges

Proporciona la funcionalidad para guardar todos los cambios pendientes.

SetReference

Proporciona la funcionalidad para establecer una referencia a objeto relacionada mediante una propiedad de navegación.

SetValue

Proporciona la funcionalidad para establecer el valor de la propiedad de un recurso.

Administrar la simultaneidad WCF Data Services admite un modelo de la simultaneidad optimista al permitirle definir un token de simultaneidad para una entidad. El servicio de datos utiliza este token de simultaneidad, que incluye una o más propiedades de la entidad, para determinar si se ha producido un cambio en los datos que se solicitan, que se están actualizando o eliminando. Cuando los valores de token obtenidos de la eTag de la solicitud difieren de los valores actuales de la entidad, el servicio de datos inicia una excepción. ETagAttribute se aplica a un tipo de entidad para definir un token de simultaneidad en el proveedor de reflexión. El token de simultaneidad no puede incluir ninguna propiedad clave ni de navegación.

Usar LINQ to SQL con el proveedor de reflexión Dado que Entity Framework se admite nativamente de forma predeterminada, es el proveedor de datos recomendado para usar datos relacionales con WCF Data Services . Sin embargo, puede usar el proveedor de reflexión para usar las clases LINQ to SQL con un servicio de datos. Los conjuntos de resultados Table que devuelven los métodos en el DataContext generado por el Object Relational Designer del LINQ to SQL implementan la interfaz IQueryable. Esto permite que el proveedor de reflexión tenga acceso a estos métodos y datos de entidades devueltos en SQL Server utilizando las clases LINQ to SQL generadas. Sin embargo, dado que LINQ to SQL no implementa la interfaz de IUpdatable, necesita agregar una clase parcial que extiende la clase parcial existente DataContext para agregar la implementación IUpdatable.

4.2.2.1. Cómo: Crear un servicio de datos mediante el proveedor de reflexión WCF Data Services le permite definir un modelo de datos que está basado en clases arbitrarias siempre que dichas clases se expongan como objetos que implementan la interfaz IQueryable.

Ejemplo En el ejemplo siguiente se define un modelo de datos que incluye Items y Orders. La clase del contenedor de entidades OrderItemData tiene dos métodos públicos que devuelven interfaces IQueryable. Estas interfaces son los conjuntos de entidades de los tipos de entidad Items y Orders. Cada Order puede incluir varios Items, por lo que el tipo de entidad Orders tiene una propiedad de navegación Items que devuelve una colección de objetos Items. La clase del contenedor de entidades OrderItemData es el tipo genérico de la clase DataService de la que se deriva el servicio de datos OrderItems.

MCT: Luis Dueñas

Pag 27 de 128

WCF Data Service Nota: Dado que este ejemplo muestra un proveedor de datos en memoria y los cambios no se conservan fuera de las instancias de objeto actuales, no hay ninguna ventaja derivada de implementar la interfaz IUpdatable. Imports System Imports System.Collections.Generic Imports System.Data.Services Imports System.Data.Services.Common Imports System.Linq Namespace CustomDataServiceClient _ Public Class Order Private _orderId As Integer Private _customer As String Private _items As IList(Of Item) Public Property OrderId() As Integer Get Return _orderId End Get Set(ByVal value As Integer) _orderId = value End Set End Property Public Property Customer() As String Get Return _customer End Get Set(ByVal value As String) _customer = value End Set End Property Public Property Items() As IList(Of Item) Get Return _items End Get Set(ByVal value As IList(Of Item)) _items = value End Set End Property End Class _ _ Public Class Item Private _product As String Private _quantity As Integer Public Property Product() As String Get Return _product End Get Set(ByVal value As String) _product = value End Set

MCT: Luis Dueñas

Pag 28 de 128

WCF Data Service End Property Public Property Quantity() As Integer Get Return _quantity End Get Set(ByVal value As Integer) _quantity = value End Set End Property End Class Partial Public Class OrderItemData #Region "Populate Service Data" Shared _orders As IList(Of Order) Shared _items As IList(Of Item) Sub New() _orders = New Order() { _ New Order() With {.OrderId = 0, .Customer = "Peter Franken", .Items = New List(Of Item)()}, _ New Order() With {.OrderId = 1, .Customer = "Ana Trujillo", .Items = New List(Of Item)()}} _items = New Item() { _ New Item() With {.Product = "Chai", .Quantity = 10}, _ New Item() With {.Product = "Chang", .Quantity = 25}, _ New Item() With {.Product = "Aniseed Syrup", .Quantity = 5}, _ New Item() With {.Product = "Chef Anton's Cajun Seasoning", .Quantity = 30}} _orders(0).Items.Add(_items(0)) _orders(0).Items.Add(_items(1)) _orders(1).Items.Add(_items(2)) _orders(1).Items.Add(_items(3)) End Sub #End Region Public ReadOnly Property Orders() As IQueryable(Of Order) Get Return _orders.AsQueryable() End Get End Property Public ReadOnly Property Items() As IQueryable(Of Item) Get Return _items.AsQueryable() End Get End Property End Class Public Class OrderItems Inherits DataService(Of OrderItemData) ' This method is called only once to initialize ' service-wide policies. Shared Sub InitializeService(ByVal config As DataServiceConfiguration) config.SetEntitySetAccessRule("Orders", EntitySetRights.All) config.SetEntitySetAccessRule("Items", EntitySetRights.All) config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2 End Sub End Class End Namespace

MCT: Luis Dueñas

Pag 29 de 128

WCF Data Service

4.2.2.2. Cómo: Crear un servicio de datos mediante un origen de datos LINQ to SQL WCF Data Services expone los datos de entidad como servicio de datos. El proveedor de reflexión le permite definir un modelo de datos que está basado en cualquier clase que exponga miembros que devuelvan una implementación de IQueryable. Para poder realizar actualizaciones en los datos del origen de datos, estas clases también deben implementar la interfaz IUpdatable. En este tema se muestra cómo crear clases LINQ to SQL que tienen acceso a la base de datos de ejemplo Northwind usando el proveedor de reflexión, así como el modo de crear el servicio de datos que está basado en estas clases de datos.

Para agregar clases LINQ to SQL a un proyecto 1. 2. 3. 4.

En una aplicación de Visual Basic o de C#, en el menú Proyecto, haga clic en Agregar nuevo elemento. Haga clic en la plantilla Clases de LINQ to SQL. Cambie el nombre a Northwind.dbml. Haga clic en Agregar. Se agrega al proyecto el archivo Northwind.dbml y se abre Object Relational Designer (O/R Designer).

5.

En Servidor/Explorador de bases de datos, bajo Northwind, expanda Tablas y arrastre la tabla Customers a Object Relational Designer (O/R Designer). Se crea una clase de entidad Customer que aparece en la superficie de diseño.

6. 7.

Repita el paso 6 para las tablas Orders, Order_Details y Products. Haga clic con el botón secundario en el nuevo archivo .dbml que representa las clases LINQ to SQL y haga clic en Ver código. Esto crea una nueva página de código subyacente denominada Northwind.cs que contiene una definición de clase parcial para la clase que hereda de la clase DataContext, que en este caso es NorthwindDataContext.

8.

Reemplace el contenido del archivo de código Northwind.cs por el código siguiente. Este código implementa el proveedor de reflexión extendiendo la clase DataContext y las clases de datos generadas por LINQ to SQL: Imports System.ComponentModel Imports System.Collections Imports System.Linq Imports System.Reflection Imports System.Data.Linq Imports System.Data.Linq.Mapping Imports System.Data.Services Imports System.Data.Services.Common ' Define the key properties for the LINQ to SQL data classes. _ Partial Public Class Customer End Class _ Partial Public Class Product End Class _ Partial Public Class Order End Class

MCT: Luis Dueñas

Pag 30 de 128

WCF Data Service _ Partial Public Class Order_Detail End Class #Region "IUpdatable implementation" ' Define the IUpdatable implementation for LINQ to SQL. Partial Public Class NorthwindDataContext Implements IUpdatable ' Creates an object in the container. Function CreateResource(ByVal containerName As String, ByVal fullTypeName As String) _ As Object Implements IUpdatable.CreateResource Dim t = Type.GetType(fullTypeName, True) Dim table = GetTable(t) Dim resource = Activator.CreateInstance(t) table.InsertOnSubmit(resource) Return resource End Function ' Gets the object referenced by the resource. Function GetResource(ByVal query As IQueryable, ByVal fullTypeName As String) As Object _ Implements IUpdatable.GetResource Dim resource = query.Cast(Of Object)().SingleOrDefault() ' fullTypeName can be null for deletes If fullTypeName IsNot Nothing AndAlso resource.GetType().FullName fullTypeName Then Throw New ApplicationException("Unexpected type for this resource.") End If Return resource End Function ' Resets the value of the object to its default value. Function ResetResource(ByVal resource As Object) As Object _ Implements IUpdatable.ResetResource Dim t = resource.GetType() Dim table = Mapping.GetTable(t) Dim dummyResource = Activator.CreateInstance(t) For Each member In table.RowType.DataMembers If Not member.IsPrimaryKey AndAlso Not member.IsDeferred AndAlso _ Not member.IsAssociation AndAlso Not member.IsDbGenerated Then Dim defaultValue = member.MemberAccessor.GetBoxedValue(dummyResource) member.MemberAccessor.SetBoxedValue(resource, defaultValue) End If Next Return resource End Function ' Sets the value of the given property on the object. Sub SetValue(ByVal targetResource As Object, ByVal propertyName As String, _ ByVal propertyValue As Object) Implements IUpdatable.SetValue Dim table = Mapping.GetTable(targetResource.GetType()) Dim member = table.RowType.DataMembers.Single(Function(x) x.Name = propertyName) member.MemberAccessor.SetBoxedValue(targetResource, propertyValue) End Sub ' Gets the value of a property on an object. Function GetValue(ByVal targetResource As Object, ByVal propertyName As String) _ As Object Implements IUpdatable.GetValue Dim table = Mapping.GetTable(targetResource.GetType())

MCT: Luis Dueñas

Pag 31 de 128

WCF Data Service Dim member = _ table.RowType.DataMembers.Single(Function(x) x.Name = propertyName) Return member.MemberAccessor.GetBoxedValue(targetResource) End Function ' Sets the related object for a reference. Sub SetReference(ByVal targetResource As Object, ByVal propertyName As String, _ ByVal propertyValue As Object) Implements IUpdatable.SetReference CType(Me, IUpdatable).SetValue(targetResource, propertyName, propertyValue) End Sub ' Adds the object to the related objects collection. Sub AddReferenceToCollection(ByVal targetResource As Object, ByVal propertyName As String, _ ByVal resourceToBeAdded As Object) _ Implements IUpdatable.AddReferenceToCollection Dim pi = targetResource.GetType().GetProperty(propertyName) If pi Is Nothing Then Throw New Exception("Can't find property") End If Dim collection = CType(pi.GetValue(targetResource, Nothing), IList) collection.Add(resourceToBeAdded) End Sub ' Removes the object from the related objects collection. Sub RemoveReferenceFromCollection(ByVal targetResource As Object, ByVal propertyName As String, _ ByVal resourceToBeRemoved As Object) _ Implements IUpdatable.RemoveReferenceFromCollection Dim pi = targetResource.GetType().GetProperty(propertyName) If pi Is Nothing Then Throw New Exception("Can't find property") End If Dim collection = CType(pi.GetValue(targetResource, Nothing), IList) collection.Remove(resourceToBeRemoved) End Sub ' Deletes the resource. Sub DeleteResource(ByVal targetResource As Object) _ Implements IUpdatable.DeleteResource Dim table = GetTable(targetResource.GetType()) table.DeleteOnSubmit(targetResource) End Sub ' Saves all the pending changes. Sub SaveChanges() Implements IUpdatable.SaveChanges SubmitChanges() End Sub ' Returns the actual instance of the resource represented ' by the resource object. Function ResolveResource(ByVal resource As Object) As Object Implements IUpdatable.ResolveResource Return resource End Function ' Reverts all the pending changes. Sub ClearChanges() Implements IUpdatable.ClearChanges ' Raise an exception as there is no real way to do this with LINQ to SQL. ' Comment out the following line if you'd prefer a silent failure Throw New NotSupportedException() End Sub

MCT: Luis Dueñas

Pag 32 de 128

WCF Data Service End Class #End Region

Para crear un servicio de datos usando un modelo de datos basado en LINQ to SQL 1. 2. 3.

En el Explorador de soluciones, haga clic con el botón secundario en el nombre del proyecto de ASP.NET y, a continuación, haga clic en Agregar nuevo elemento. En el cuadro de diálogo Agregar nuevo elemento, seleccione WCF Data Service. Proporcione un nombre para el servicio y, a continuación, haga clic en Aceptar. Visual Studio crea los archivos de código y marcado XML para el nuevo servicio. De forma predeterminada, se abre la ventana del editor de código.

4.

En el código para el servicio de datos, reemplace el comentario /* TODO: put your data source

5.

class name here */ de la definición de la clase que define el servicio de datos por el tipo que es el contenedor de entidades del modelo de datos, que en este caso es NorthwindDataContext. En el código del servicio de datos, reemplace el código de marcador de posición de la función InitializeService por el siguiente: config.SetEntitySetAccessRule("Customers", EntitySetRights.ReadMultiple) config.SetEntitySetAccessRule("Orders", EntitySetRights.AllRead _ Or EntitySetRights.WriteMerge) config.SetEntitySetAccessRule("Order_Details", EntitySetRights.AllRead _ Or EntitySetRights.AllWrite) config.SetEntitySetAccessRule("Products", EntitySetRights.ReadMultiple)

Esto permite a los clientes autorizados tener acceso a los recursos para los tres conjuntos de entidades especificados. 6.

Para probar el servicio de datos Northwind.svc usando un explorador web, siga las instrucciones del tema Obtener acceso al servicio desde un explorador web.

4.2.3. Proveedores de servicios de datos personalizados WCF Data Services incluye un conjunto de proveedores que permite definir un modelo de datos basado en tipos de datos enlazados en tiempo de ejecución. Proveedor

Descripción

Proveedor de metadatos

Este es el proveedor principal de servicios de datos personalizados que permite definir un modelo de datos personalizado en tiempo de ejecución implementando la interfaz IDataServiceMetadataProvider.

Proveedor de consultas

Este proveedor permite ejecutar las consultas sobre un modelo de datos personalizado que se define utilizando la interfaz IDataServiceMetadataProvider. El proveedor de consultas se crea implementando la interfaz IDataServiceQueryProvider.

Proveedor de actualizaciones

Este proveedor permite realizar actualizaciones a los tipos expuestos en un proveedor de servicio de datos personalizado y administrar la simultaneidad. El proveedor de actualizaciones se crea implementando la interfaz IDataServiceUpdateProvider.

Proveedor de paginaciones

Este proveedor se utiliza con el proveedor del servicio de datos personalizado para habilitar la compatibilidad con la paginación controlada por servidor. Un proveedor de paginaciones para un servicio de datos personalizado se crea implementando la interfaz IDataServicePagingProvider.

Proveedor de transmisiones por secuencias

Este proveedor permite exponer tipos de datos de objetos binarios grandes como una secuencia. Si implementa la interfaz IDataServiceStreamProvider, se crea un proveedor de transmisiones por secuencias. El proveedor de transmisiones por secuencias también

MCT: Luis Dueñas

Pag 33 de 128

WCF Data Service se puede utilizar con Entity Framework y proveedores de orígenes de datos de reflexión.

4.2.4. Proveedores de transmisión por secuencias Un servicio de datos puede exponer datos binarios de objetos grandes. Estos datos binarios pueden representar secuencias de vídeo y audio, imágenes, archivos de documento u otros tipos de medios binarios. Cuando una entidad del modelo de datos incluye una o más propiedades binarias, el servicio de datos devuelve estos datos binarios codificados en base 64 en la entrada de la fuente de respuesta. Dado que la carga y la serialización de datos binarios grandes de esta manera pueden afectar al rendimiento, Open Data Protocol (OData) define un mecanismo para recuperar datos binarios independientemente de la entidad a la que pertenezcan. Para ello, se separan los datos binarios de la entidad en uno o varios flujos de datos. Recurso multimedia: los datos binarios que pertenecen a una entidad, como vídeo, audio, imagen u otro tipo de flujo de recursos multimedia. Entrada de vínculo multimedia: entidad con una referencia a un flujo de recursos multimedia relacionado. Con WCF Data Services , defina un flujo de recursos binarios implementando un proveedor de transmisión de datos por secuencias. La implementación del proveedor de transmisión por secuencias proporciona al servicio de datos la secuencia de recursos multimedia asociada con una entidad concreta como objeto Stream. Esta implementación permite que el servicio de datos acepte y devuelva los recursos multimedia a través del protocolo HTTP como flujos de datos binarios de un tipo MIME especificado. La configuración de un servicio de datos para que admita la transmisión por secuencias de datos binarios requiere los siguientes pasos: 1.

Atribuya una o más entidades del modelo de datos como una entrada de vínculo multimedia. Estas entidades no deben incluir los datos binarios que se van a transmitir por secuencias. Las propiedades binarias de una entidad siempre se devuelven en la entrada como datos binarios codificados en base 64.

2.

Implemente la interfaz T:System.Data.Services.Providers.IDataServiceStreamProvider.

3.

Defina un servicio de datos que implemente la interfaz IServiceProvider. El servicio de datos usa la implementación del método GetService para acceder a la implementación del proveedor de transmisión de datos por secuencias. Este método devuelve la implementación del proveedor de transmisión por secuencias adecuado.

4.

Habilite flujos de mensajes grandes en la configuración de la aplicación web.

5.

Habilite el acceso a los recursos binarios en el servidor o en un origen de datos.

Los ejemplos de este tema se basan en un servicio de fotografías de transmisión por secuencias de ejemplo, que se describe con todo detalle en la entrada del blog relacionado con la parte 1 de la implementación de un proveedor de transmisión por secuencias de la serie de proveedores de transmisión por secuencias de servicios de datos.

Definir una entrada de vínculo multimedia en el modelo de datos El proveedor del origen de datos determina cómo se define una entidad como entrada de vínculo multimedia en el modelo de datos. Proveedor de Entity Framework Para indicar que una entidad es una entrada de vínculo multimedia, agregue el atributo HasStream a la definición del tipo de entidad en el modelo conceptual, como en el siguiente ejemplo:











También debe agregar el espacio de nombres

xmlns:m=http://schemas.microsoft.com/ado/2007/08/dataservices/metadata a la entidad o a la raíz del archivo .edmx o del archivo .csdl que definen el modelo de datos. Proveedor de reflexión Para indicar que una entidad es una entrada de vínculo multimedia, agregue el atributo HasStreamAttribute a la clase que define el tipo de entidad en el proveedor de reflexión. Proveedor de servicios de datos personalizados Cuando se usan proveedores de servicios personalizados, implemente la interfaz IDataServiceMetadataProvider para definir los metadatos del servicio de datos. Indique que un flujo de recurso binario pertenezca a la clase ResourceType estableciendo el valor de la propiedad IsMediaLinkEntry en true en la clase ResourceType que representa el tipo de entidad, que es una entrada de vínculo multimedia.

Implementar la interfaz IDataServiceStreamProvider Para crear un servicio de datos que admita flujos de datos binarios, debe implementar la interfaz IDataServiceStreamProvider. Esta implementación habilita el servicio de datos para devolver al cliente datos binarios como flujo y utilizar los datos binarios como flujo enviado desde el cliente. El servicio de datos crea una instancia de esta interfaz cuando sea necesario para acceder a los datos binarios como flujo. La interfaz IDataServiceStreamProvider especifica los siguientes miembros: Nombre del miembro Descripción

DeleteStream

El servicio de datos invoca este método para eliminar el recurso multimedia correspondiente cuando se elimina su entrada de vínculo multimedia. Cuando se implementa IDataServiceStreamProvider, este método contiene el código que elimina el recurso multimedia asociado con la entrada de vínculo multimedia proporcionada.

GetReadStream

El servicio de datos invoca este método para devolver un recurso multimedia como un flujo. Cuando se implementa IDataServiceStreamProvider, este método contiene el código que proporciona un flujo que el servicio de datos utiliza para devolver el recurso multimedia que está asociado a la entrada de vínculo multimedia proporcionada.

GetReadStreamUri

El servicio de datos invoca este método para devolver el URI que se utiliza para solicitar el recurso multimedia de la entrada de vínculo multimedia. Este valor se usa para crear el atributo src en el elemento de contenido de la entrada de vínculo multimedia que se utiliza para solicitar el flujo de datos. Cuando este método devuelve null, el servicio de datos automáticamente determina el URI. Use este

MCT: Luis Dueñas

Pag 35 de 128

WCF Data Service método cuando deba proporcionar clientes con acceso directo a datos binarios sin usar el proveedor de flujos. GetStreamContentType

El servicio de datos invoca este método para devolver el valor Content-Type del recurso multimedia asociado con la entrada de vínculo multimedia especificada.

GetStreamETag

El servicio de datos invoca este método para devolver el objeto eTag del flujo de datos que está asociado con la entidad especificada. Este método se utiliza cuando se administra la simultaneidad de los datos binarios. Cuando este método devuelve null, el servicio de datos no realiza el seguimiento de la simultaneidad.

GetWriteStream

El servicio de datos invoca este método para obtener el flujo que se utiliza cuando se recibe el flujo enviado desde el cliente. Cuando implemente la interfaz IDataServiceStreamProvider, debe devolver un flujo grabable en el que el servicio de datos pueda escribir los datos del flujo recibidos.

ResolveType

Devuelve un nombre de tipo calificado con el espacio de nombres que representa el tipo que el motor en tiempo de ejecución del servicio de datos debe crear para la entrada de vínculo multimedia asociada al flujo de datos del recurso multimedia que se está insertando.

Crear el servicio de transmisión de datos por secuencias Para proporcionar al motor de tiempo de ejecución de WCF Data Services acceso a la implementación de la interfaz IDataServiceStreamProvider, el servicio de datos que cree también debe implementar la interfaz IServiceProvider. En el siguiente ejemplo se muestra cómo implementar el método GetService para devolver una instancia de la clase

PhotoServiceStreamProvider que implemente la interfaz

IDataServiceStreamProvider. Partial Public Class PhotoData Inherits DataService(Of PhotoDataContainer) Implements IServiceProvider ' This method is called only once to initialize service-wide policies. Public Shared Sub InitializeService(ByVal config As DataServiceConfiguration) config.SetEntitySetAccessRule("PhotoInfo", _ EntitySetRights.ReadMultiple Or _ EntitySetRights.ReadSingle Or _ EntitySetRights.AllWrite) ' Named streams require version 3 of the OData protocol. config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3 End Sub #Region "IServiceProvider Members" Public Function GetService(ByVal serviceType As Type) As Object _ Implements IServiceProvider.GetService If serviceType Is GetType(IDataServiceStreamProvider) _ Or serviceType Is GetType(IDataServiceStreamProvider2) Then Return New PhotoServiceStreamProvider(Me.CurrentDataSource) End If Return Nothing End Function #End Region End Class

Habilitar flujos binarios grandes en el entorno de hospedaje Cuando cree un servicio de datos en una aplicación web de ASP.NET, se usa Windows Communication Foundation (WCF) para proporcionar la implementación del protocolo HTTP. De forma predeterminada, WCF limita el tamaño de los mensajes HTTP a solo 65K bytes. Para poder transmitir datos binarios grandes por secuencias al servicio de datos y desde él, debe configurar también la aplicación web para habilitar

MCT: Luis Dueñas

Pag 36 de 128

WCF Data Service archivos binarios grandes y utilizar secuencias para la transferencia. Para ello, en el elemento del archivo Web.config de la aplicación agregue lo siguiente:









Usar flujos de datos en una aplicación cliente La biblioteca cliente de WCF Data Services le permite recuperar y actualizar estos recursos expuestos como flujos binarios en el cliente.

Consideraciones para trabajar con un proveedor de transmisión por secuencias A continuación se enumeran algunas de las consideraciones que debe tener en cuenta al implementar un proveedor de transmisiones por secuencias y al tener acceso a los recursos multimedia de un servicio de datos. Los recursos multimedia no admiten solicitudes MERGE. Utilice una solicitud PUT para cambiar el recurso multimedia de una entidad existente. Una solicitud POST no se puede utilizar para crear una entrada de vínculo multimedia. En su lugar, debe emitir una solicitud POST para crear un nuevo recurso multimedia y el servicio de datos creará una entrada de vínculo multimedia con los valores predeterminados. Una solicitud MERGE o PUT posterior puede actualizar esta nueva entidad. También puede considerar la opción de almacenar en memoria caché la entidad y realizar actualizaciones en el contenedor de elementos eliminados, como establecer el valor de propiedad en el valor del encabezado Slug en la solicitud POST.

MCT: Luis Dueñas

Pag 37 de 128

WCF Data Service Cuando se recibe una solicitud POST, el servicio de datos llama a GetWriteStream para crear el recurso multimedia antes de llamar a SaveChanges para crear la entrada de vínculo multimedia. Una implementación de GetWriteStream no debe devolver un objeto MemoryStream. Cuando se utiliza este tipo de flujo, se producirán problemas de recursos de memoria cuando el servicio reciba flujos de datos muy grandes. A continuación se enumeran algunas de las consideraciones que debe tener en cuenta al almacenar recursos multimedia en una base de datos:

o

En el modelo de datos no debe incluirse una propiedad binaria que sea un recurso multimedia. Todas las propiedades expuestas en un modelo de datos se devuelven en la entrada de una fuente de respuesta.

o

Para mejorar el rendimiento con un flujo binario grande, recomendamos crear una clase de flujo personalizado para almacenar datos binarios en la base de datos. La implementación de GetWriteStream devuelve esta clase y envía los datos binarios a la base de datos en fragmentos. En el caso de una base de datos de SQL Server, se recomienda usar un objeto FILESTREAM para transmitir los datos por secuencias a la base de datos cuando los datos binarios ocupen más de 1 MB.

o

Asegúrese de que la base de datos esté diseñada para almacenar los flujos binarios grandes que vaya a recibir el servicio de datos.

o

Cuando un cliente envía a una solicitud POST para insertar una entrada de vínculo multimedia con un recurso multimedia en una solicitud única, se llama a GetWriteStream para obtener el flujo antes de que el servicio de datos inserte la nueva entidad en la base de datos. Una implementación del proveedor de transmisiones por secuencias debe ser capaz de controlar este comportamiento del servicio de datos. Considere la opción de usar una tabla de datos independiente para almacenar los datos binarios o almacenar el flujo de datos en un archivo hasta que la entidad se haya insertado en la base de datos.

Cuando implemente los métodos DeleteStream, GetWriteStream o GetReadStream, debe utilizar el objeto eTag y los valores Content-Type y que se proporcionan como parámetros de método. No establezca el objeto eTag ni los encabezados Content-Type en la implementación del proveedor IDataServiceStreamProvider. De forma predeterminada, el cliente envía secuencias binarias grandes mediante codificación de transferencia HTTP fragmentada. Dado que el servicio de desarrollo de ASP.NET no admite este tipo de codificación, no podrá usar este servidor web para hospedar los servicios de transmisión de datos por secuencias que deban aceptar flujos binarios grandes.

4.3. Operaciones de servicio WCF Data Services le permite definir operaciones de servicio en un servicio de datos para exponer métodos del servidor. Las operaciones de servicio se direccionan como los demás recursos del servicio de datos, mediante los URI. Las operaciones de servicio le permiten exponer la lógica de negocios de un servicio de datos; por ejemplo, implementar la lógica de validación, aplicar la seguridad basada en roles o exponer capacidades de consulta especializadas. Estas operaciones son métodos agregados a la clase del servicio de datos derivada de DataService. Como el resto de los recursos del servicio de datos, puede proporcionar parámetros al método de operación de servicio. Por ejemplo, el siguiente URI de operación de servicio (basado en el servicio de datos del tutorial rápido) pasa el valor London al parámetro city: http://localhost:12345/Northwind.svc/GetOrdersByCity?city='London'

La definición de esta operación de servicio es la siguiente: _ Public Function GetOrdersByCity(ByVal city As String) As IQueryable(Of Order)

MCT: Luis Dueñas

Pag 38 de 128

WCF Data Service Puede usar la propiedad CurrentDataSource de la clase DataService para tener acceso directo al origen de datos que está usando el servicio de datos.

Requisitos que deben cumplir las operaciones de servicio Al definir operaciones de servicio en el servicio de datos deben tenerse en cuenta los requisitos siguientes. Si un método no los cumple, no se expondrá como operación de servicio para el servicio de datos. La operación debe ser un método de instancia pública que sea miembro de la clase del servicio de datos. El método de la operación solo puede aceptar parámetros de entrada. Si se definen parámetros, el tipo de cada parámetro debe ser un tipo primitivo. El método debe devolver uno de los elementos siguientes:

o

void (Nothing en Visual Basic)

o

IEnumerable

o

IQueryable

o

Un tipo de entidad en el modelo de datos expuesto por el servicio de datos.

o

Una clase primitiva como entero o cadena.

Para admitir opciones de consulta como ordenaciones, paginaciones y filtrados, los métodos de las operaciones de servicio deben devolver IQueryable. Las solicitudes a operaciones de servicio que incluyen opciones de consulta se rechazan para las operaciones que solo devuelven IEnumerable. Para que se pueda tener acceso a las entidades relacionadas usando las propiedades de navegación, la operación de servicio debe devolver IQueryable. El método se debe anotar con el atributo [WebGet] o [WebInvoke].

o

[WebGet] permite invocar el método usando una solicitud GET.

o

[WebInvoke] permite invocar el método usando una solicitud POST.

Una operación de servicio se puede anotar con el elemento SingleResultAttribute que especifica que el valor devuelto desde el método es una sola entidad en vez de una colección de entidades. Esta distinción dicta la serialización resultante de la respuesta y la manera en que se representan en el URI los recorridos de las propiedades de navegación adicionales. Por ejemplo, cuando use la serialización AtomPub, una sola instancia de tipo de recurso se representa como elemento de entrada y un conjunto de instancias como elemento de fuente.

Direccionar operaciones de servicio Puede direccionar las operaciones de servicio colocando el nombre del método en el primer segmento de la ruta de acceso de un URI. Por ejemplo, el URI siguiente tiene acceso a una operación GetOrdersByCity que devuelve una colección IQueryable de objetos Orders, ordenada en orden descendente por RequiredDate, junto con los objetos Order_Details relacionados: http://localhost:12345/Northwind.svc/GetOrdersByCity?city='London'&$expand=Order_Details&$orderby=RequiredDate desc

Se pueden agregar segmentos de ruta de acceso u opciones de consulta adicionales al URI en función del tipo de valor devuelto de la operación de servicio.

MCT: Luis Dueñas

Pag 39 de 128

WCF Data Service Tipos de valor devueltos válidos

Reglas que deben cumplir los URI

void (Nothing en Visual Basic) – O bien – Tipos de entidad

El URI debe ser un solo segmento de ruta de acceso que sea el nombre de la operación de servicio. No se permiten las opciones de consulta.

– O bien – Tipos primitivos IEnumerable

El URI debe ser un solo segmento de ruta de acceso que sea el nombre de la operación de servicio. Puesto que el tipo de resultado no es un tipo IQueryable, no se permiten las opciones de consulta.

IQueryable

Se permiten segmentos de ruta de acceso de la consulta además de la ruta de acceso que es el nombre de la operación de servicio. También se permiten las opciones de consulta.

Control de acceso a las operaciones de servicio El método SetServiceOperationAccessRule de la clase IDataServiceConfiguration controla la visibilidad en el ámbito de todos los servicios de las operaciones de servicio, de la misma forma que el método SetEntitySetAccessRule controla la visibilidad del conjunto de entidades. Por ejemplo, la línea de código siguiente de la definición del servicio de datos permite el acceso a la operación de servicio

CustomersByCity. config.SetServiceOperationAccessRule("GetOrdersByCity", ServiceOperationRights.AllRead)

Nota: Si una operación de servicio tiene un tipo de valor devuelto que se oculta restringiendo el acceso en los conjuntos de entidades subyacentes, la operación de servicio no estará disponible para las aplicaciones cliente.

4.3.1. Cómo: Definir una operación de servicio WCF Data Services expone métodos que se definen en el servidor como operaciones de servicio. Las operaciones de servicio permiten que un servicio de datos proporcione acceso a través de un URI a un método que se define en el servidor. Para definir una operación de servicio, aplique el atributo WebGet] o [WebInvoke] al método. Para que admita operadores de consulta, la operación del servicio debe devolver una instancia de IQueryable. Las operaciones del servicio pueden tener acceso al origen de datos subyacente por medio de la propiedad CurrentDataSource en DataService. En el ejemplo de este tema se define una operación del servicio denominada GetOrdersByCity que devuelve una instancia de IQueryable filtrada de los objetos Order_Details y Orders relacionados. En el ejemplo se obtiene acceso a la instancia de ObjectContext que es el origen de datos del servicio de datos de ejemplo Northwind. Este servicio se crea cuando se completa el Tutorial rápido de WCF Data Services.

Para definir una operación del servicio en el servicio de datos Northwind 1.

En el proyecto del servicio de datos Northwind, abra el archivo Northwind.svc.

2.

En la clase Northwind, defina un método de operación de servicio denominado GetOrdersByCity como sigue: _ Public Function GetOrdersByCity(ByVal city As String) As IQueryable(Of Order)

MCT: Luis Dueñas

Pag 40 de 128

WCF Data Service En el método InitializeService de la clase Northwind, agregue el siguiente código para permitir el

3.

acceso a la operación del servicio: config.SetServiceOperationAccessRule("GetOrdersByCity", ServiceOperationRights.AllRead)

Para consultar la operación del servicio GetOrdersByCity En un explorador web, escriba uno de los siguientes URI para invocar la operación del servicio que se define en el siguiente ejemplo:

o

http://localhost:12345/Northwind.svc/GetOrdersByCity?city='London'

o

http://localhost:12345/Northwind.svc/GetOrdersByCity?city='London'&$top=2

o

http://localhost:12345/Northwind.svc/GetOrdersByCity?city='London'&$expand=Order_Detai ls&$orderby=RequiredDate desc

Ejemplo En el ejemplo siguiente se implementa una operación del servicio denominada GetOrderByCity en el servicio de datos Northwind. Esta operación utiliza ADO.NET Entity Framework para devolver un conjunto de objetos Order_Details y Orders relacionados como una instancia de IQueryable basada en el nombre de ciudad suministrado. Nota: Los operadores de consulta se admiten en este extremo de la operación del servicio porque el método devuelve una instancia de IQueryable. _ Public Function GetOrdersByCity(ByVal city As String) As IQueryable(Of Order) If String.IsNullOrEmpty(city) Then Throw New ArgumentNullException("city", _ "You must provide a value for the parameter'city'.") End If ' Get the ObjectContext that is the data source for the service. Dim context As NorthwindEntities = Me.CurrentDataSource Try Dim selectedOrders = From order In context.Orders.Include("Order_Details") _ Where order.Customer.City = city _ Select order Return selectedOrders Catch ex As Exception Throw New ApplicationException("An error occurred: {0}", ex) End Try End Function

4.4. Personalización de fuentes WCF Data Services utiliza Open Data Protocol (OData) para exponer los datos como fuente. OData admite los formatos Atom y JSON (JavaScript Object Notation) para las fuentes de distribución de datos. Cuando usa una fuente Atom, OData proporciona un método estándar para serializar los datos, como entidades y relaciones, en un formato XML que se puede incluir en el cuerpo de mensaje HTTP. OData define una asignación de entidad-propiedad predeterminada entre los datos contenidos en entidades y en elementos Atom. Quizá tenga un escenario de aplicación que requiera que los datos de la propiedad devueltos por el servicio de datos se serialicen de forma personalizada en vez de hacerlo con el formato de fuente estándar. Con OData , puede personalizar la serialización de una fuente de distribución de datos de forma que las

MCT: Luis Dueñas

Pag 41 de 128

WCF Data Service propiedades de una entidad se puedan asignar a elementos de fuente y atributos no usados de una entrada o a elementos de una entrada en la fuente.

MCT: Luis Dueñas

Pag 42 de 128

WCF Data Service

Nota: La personalización de fuentes solo se admite en las fuentes Atom. No se devuelven fuentes personalizadas cuando se solicita el formato JSON para la fuente devuelta. Con WCF Data Services , puede definir una asignación de entidad-propiedad alternativa para una carga Atom aplicando manualmente los atributos a los tipos de entidad del modelo de datos. El proveedor del origen de datos del servicio de datos determina cómo debería aplicar estos atributos. Nota: Cuando defina las fuentes personalizadas, debe asegurarse de que se incluyan todas las propiedades de entidad con asignaciones personalizadas en la proyección. Cuando no se incluya ninguna propiedad de entidad asignada en la proyección, se puede producir la pérdida de datos.

Personalizar fuentes con el proveedor de Entity Framework El modelo de datos usado con el proveedor de Entity Framework se representa como XML en el archivo .edmx. En este caso, los atributos que definen las fuentes personalizadas se agregan a los elementos EntityType y Property, que representan tipos de entidad y propiedades en el modelo de datos. Estos atributos de personalización de fuentes no se definen en el formato de archivo de definición de esquemas conceptuales [MC-CSDL], que es el formato que usa el proveedor de Entity Framework para definir el modelo de datos. Por consiguiente, debe declarar los atributos de personalización de fuentes en un espacio de nombres de esquema concreto, que se define como

m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata". El fragmento XML siguiente muestra los atributos de personalización de la fuente aplicados a los elementos Property del tipo de entidad Products que definen las propiedades ProductName, ReorderLevel y UnitsInStock.



Estos atributos generan la fuente de distribución de datos personalizada siguiente para el conjunto de entidades Products. En la fuente de distribución de datos personalizada, el valor de propiedad

ProductName se muestra tanto en el elemento author como en el elemento de propiedad ProductName y la propiedad UnitsInStock se muestra en un elemento personalizado que tiene su propio espacio de nombres único y con la propiedad ReorderLevel como atributo:

MCT: Luis Dueñas

Pag 43 de 128

WCF Data Service http://localhost:12345/Northwind.svc/Products(1)

2009-10-02T05:09:44Z

Chai



1 Chai 39 1 1 10 boxes x 20 bags 18.0000 0 false

39

Nota: Dado que Entity Designer no admite las extensiones del modelo de datos, debe modificar manualmente el archivo XML que contiene el modelo de datos.

Atributos de fuentes personalizadas En la tabla siguiente se muestran los atributos XML que personalizan fuentes y que puede agregar al lenguaje de definición de esquemas conceptuales (CSDL) que define el modelo de datos. Estos atributos son equivalentes a las propiedades de la clase EntityPropertyMappingAttribute usada con el proveedor de reflexión. Nombre de Descripción atributo Indica el tipo de contenido. Las palabras clave siguientes definen los tipos de contenido de distribución:

FC_ContentKind

Palabra clave Descripción textEl valor de propiedad se muestra en la fuente como texto. htmlEl valor de propiedad se muestra en la fuente como HTML. xhtmlEl valor de propiedad se muestra en la fuente como HTML con formato XML. Estas palabras clave son equivalentes a los valores de la enumeración SyndicationTextContent Kind usada con el proveedor de reflexión. No se admite este atributo cuando se usan los atributos FC_NsPrefix y FC_NsUri. Cuando especifique un valor de xhtml para el atributo FC_ContentKind, debe asegurarse de que el valor de propiedad contenga XML con el formato correcto. El servicio de datos devuelve el valor sin realizar transformaciones. Además, debe asegurarse de que cualquier prefijo de elemento XML del XML devuelto tenga un URI

MCT: Luis Dueñas

Pag 44 de 128

WCF Data Service de espacio de nombres definido en la fuente asignada. Indica que el valor de propiedad a la que se hace referencia debe incluirse tanto en la sección de contenido de la fuente como en la ubicación asignada. Los valores válidos FC_KeepInContent son true y false. Para hacer que la fuente resultante sea compatible con versiones anteriores de WCF Data Services , especifique el valor true para asegurarse de que el valor se incluye en la sección de contenido de la fuente. FC_NsPrefix

El prefijo del espacio de nombres del elemento XML de una asignación sin distribución. Este atributo debe usarse con el atributo FC_NsUri y no se puede usar con el atributo FC_ContentKind.

FC_NsUri

El URI del espacio de nombres del elemento XML de una asignación sin distribución. Este atributo debe usarse con el atributo FC_NsPrefix y no se puede usar con el atributo FC_ContentKind. La ruta de acceso de la propiedad de la entidad a la que se aplica esta regla de asignación de fuentes. Este atributo solo se admite cuando se usa en un elemento EntityType. La propiedad SourcePath no puede hacer referencia directamente a un tipo complejo. Para los tipos complejos, debe usar una expresión de ruta cuyos nombres de propiedad estén separados por un carácter de barra diagonal (/). Por ejemplo, se permiten los

FC_SourcePath

valores siguientes para un tipo de entidad Person con una propiedad entera Age y una propiedad compleja Address:

Age Address/Street La propiedad SourcePath no puede establecerse en un valor que contenga un espacio ni en ningún otro carácter que no sea válido para un nombre de propiedad. El nombre del elemento de destino de la fuente resultante que debe asignarse a la propiedad. Este elemento puede ser un elemento definido por la especificación Atom o un elemento personalizado. Las siguientes palabras clave son los valores predefinidos de la ruta de acceso de destino de distribución que señalan a una ubicación concreta de una fuente OData . Palabra clave Descripción SyndicationAuthorEmailEl elemento secundario atom:email del elemento

atom:author. SyndicationAuthorNameEl elemento secundario atom:name del elemento

atom:author. SyndicationAuthorUriEl elemento secundario atom:uri del elemento atom:author. SyndicationContributorEmailEl elemento secundario atom:email del elemento FC_TargetPath

atom:contributor. SyndicationContributorNameEl elemento secundario atom:name del elemento

atom:contributor. SyndicationContributorUriEl elemento secundario atom:uri del elemento

atom:contributor. SyndicationCustomProperty Elemento de propiedad personalizada.Cuando se realiza la asignación a un elemento personalizado, el destino debe ser una expresión de ruta de acceso cuyos elementos anidados estén separados mediante una barra diagonal (/) y cuyos atributos se especifiquen mediante una arroba (@). En el ejemplo siguiente, la cadena UnitsInStock/@ReorderLevel asigna un valor de propiedad a un atributo denominado ReorderLevel en un elemento secundario denominado UnitsInStock del elemento de entrada raíz.

MCT: Luis Dueñas

Pag 45 de 128

WCF Data Service

Cuando el destino es un nombre de elemento personalizado, también se deben especificar los atributos FC_NsPrefix y FC_NsUri. SyndicationPublishedEl elemento atom:published. SyndicationRightsEl elemento atom:rights. SyndicationSummaryEl elemento atom:summary. SyndicationTitleEl elemento atom:title. SyndicationUpdatedEl elemento atom:updated. Estas palabras clave son equivalentes a los valores de la enumeración SyndicationItemProperty usada con el proveedor de reflexión. Nota: Los nombres y los valores de los atributos distinguen mayúsculas de minúsculas. Los atributos se pueden aplicar al elemento EntityType o a uno o varios elementos Property, pero no a ambos.

Personalizar fuentes con el proveedor de reflexión Para personalizar fuentes para un modelo de datos que se implementó usando el proveedor de reflexión, agregue una o varias instancias del atributo EntityPropertyMappingAttribute a las clases que representan los tipos de entidad del modelo de datos. Las propiedades de la clase EntityPropertyMappingAttribute corresponden a los atributos de personalización de fuentes que se describen en la sección anterior. A continuación se muestra un ejemplo de la declaración del tipo Order, con asignación de la fuente personalizada definida para ambas propiedades. Nota: El modelo de datos para este ejemplo se define en el tema Cómo: Crear un servicio de datos mediante el proveedor de reflexión (WCF Data Services). _ _ _ Public Class Order

Estos atributos generan la fuente de distribución de datos personalizada siguiente para el conjunto de entidades Orders. En esta fuente personalizada, el valor de propiedad OrderId solo se muestra en el elemento title de entry y el valor de propiedad Customer se muestra en el elemento author y como elemento de la propiedad Customer:

http://localhost:12345/OrderItems.svc/Orders(0) 0 2009-07-25T21:11:11Z

MCT: Luis Dueñas

Pag 46 de 128

WCF Data Service

Peter Franken



Peter Franken



Personalizar fuentes con un proveedor de servicios de datos personalizado La personalización de fuentes para un modelo de datos definido utilizando un proveedor de servicios de datos personalizado se define para un tipo de recurso llamando al método AddEntityPropertyMappingAttribute en el objeto ResourceType que representa un tipo de entidad en el modelo de datos.

Usar fuentes personalizadas Cuando una aplicación usa directamente una fuente de OData , debe ser capaz de procesar cualquiera de los elementos y atributos personalizados existentes en la fuente devuelta. Cuando se han implementado fuentes personalizadas en un modelo de datos, sea cual sea el proveedor del servicio de datos, el extremo $metadata devuelve la información de la fuente personalizada como atributos de fuente personalizados en el CSDL devuelto por el servicio de datos. Si se usa el cuadro de diálogo Agregar referencia de servicio o la herramienta datasvcutil.exe para generar las clases del servicio de datos del cliente, se usan los atributos de fuente personalizados para garantizar que las solicitudes y las respuestas del servicio de datos se controlen correctamente.

Consideraciones sobre personalización de fuentes Debe considerar lo siguiente cuando defina asignaciones de fuentes personalizadas. El cliente de WCF Data Services trata los elementos asignados en una fuente como vacíos cuando contengan solo espacio en blanco. Es por ello que los elementos asignados que contengan solo espacio en blanco no se materializan en el cliente con el mismo espacio en blanco. Para conservar este espacio en blanco en el cliente, debe establecer el valor de KeepInContext en true en el atributo de asignación de fuente.

Requisitos de control de versiones La personalización de fuentes tiene los siguientes requisitos de control de versiones de OData : La personalización de fuente requiere que tanto el cliente como el servicio de datos admitan la versión 2.0 del protocolo de OData y versiones posteriores.

4.4.1. Cómo: Personalizar fuentes con el proveedor de Entity Framework WCF Data Services le permite personalizar la serialización Atom en una respuesta del servicio de datos para que las propiedades de una entidad se puedan asignar a los elementos no usados que se definen en el protocolo AtomPub. En este tema se explica cómo definir los atributos de asignación para los tipos de entidad en un modelo de datos definido en un archivo .edmx utilizando el proveedor de Entity Framework.

MCT: Luis Dueñas

Pag 47 de 128

WCF Data Service En este tema modificará manualmente el archivo .edmx generado por la herramienta que contiene el modelo de datos. Dado que Entity Designer no admite las extensiones al modelo de datos, debe modificar manualmente el archivo. En el ejemplo de este tema se usan el servicio de datos de ejemplo Northwind y las clases del servicio de datos del cliente generadas automáticamente. Se crean este servicio y las clases de datos del cliente al completar el tutorial rápido de WCF Data Services.

Para modificar manualmente el archivo Northwind.edmx para agregar los atributos de personalización de fuente 1. 2. 3.

En el Explorador de soluciones, haga clic con el botón secundario del mouse en el archivo Northwind.edmx y, a continuación, elija Abrir con. En el cuadro de diálogo Abrir con - Northwind.edmx, seleccione Editor XML y, a continuación, haga clic en Aceptar. Busque el elemento ConceptualModels y reemplace el tipo de entidad Customers existente con el siguiente elemento que contiene los atributos de asignación de personalización de la fuente:











MCT: Luis Dueñas

Pag 48 de 128

WCF Data Service

4. 5.

Guarde los cambios y cierre el archivo Northwind.edmx. (Opcional) Haga clic con el botón secundario en el archivo Northwind.edmx y, a continuación, haga clic en Ejecutar herramienta personalizada. Esto regenera el archivo de capa de objeto, que puede ser necesario.

6.

Compile de nuevo el proyecto.

Ejemplo En el ejemplo anterior se devuelve el resultado siguiente para el URI

http://myservice/Northwind.svc/Customers('ALFKI').

http://localhost:12345/Northwind.svc/Customers('ALFKI') ALFKI 2009-07-27T07:59:43Z

Peter Franken



Peter Franken Alfreds Futterkiste Marketing Manager Obere Str. 57 Berlin

12209 Germany 089-0877310 089-0877554

Alfreds Futterkiste

4.4.2. Cómo: Personalizar fuentes con el proveedor de reflexión WCF Data Services le permite personalizar la serialización Atom en una respuesta del servicio de datos para que las propiedades de una entidad se puedan asignar a los elementos no usados que se definen en el

MCT: Luis Dueñas

Pag 49 de 128

WCF Data Service protocolo AtomPub. En este tema se muestra cómo definir los atributos de asignación para los tipos de entidad de un modelo de datos que se define usando el proveedor de reflexión.

Ejemplo En el ejemplo siguiente, las dos propiedades del tipo Order se asignan a elementos Atom existentes. La propiedad Product del tipo Item se asigna a un atributo de fuente personalizado en un espacio de nombres independiente. Imports System Imports System.Collections.Generic Imports System.Data.Services Imports System.Data.Services.Common Imports System.Linq Namespace CustomDataService _ _ _ Public Class Order Private _orderId As Integer Private _customer As String Private _items As IList(Of Item) Public Property OrderId() As Integer Get Return _orderId End Get Set(ByVal value As Integer) _orderId = value End Set End Property Public Property Customer() As String Get Return _customer End Get Set(ByVal value As String) _customer = value End Set End Property Public Property Items() As IList(Of Item) Get Return _items End Get Set(ByVal value As IList(Of Item)) _items = value End Set End Property End Class _

_ Public Class Item Private _product As String Private _quantity As Integer Private _url As String Public Property Product() As String Get Return _product End Get Set(ByVal value As String) _product = value End Set End Property Public Property Quantity() As Integer Get Return _quantity End Get Set(ByVal value As Integer) _quantity = value End Set End Property Public Property Url As String Get Return _url End Get Set(ByVal value As String) _url = value End Set End Property End Class Partial Public Class OrderItemData #Region "Populate Service Data" Shared _orders As IList(Of Order) Shared _items As IList(Of Item) Sub New() _orders = New Order() { _ New Order() With {.OrderId = 0, .Customer = "Peter Franken", .Items = New List(Of Item)()}, _ New Order() With {.OrderId = 1, .Customer = "Ana Trujillo", .Items = New List(Of Item)()}} _items = New Item() { _ New Item() With {.Product = "Chai", .Quantity = 10, _ .Url = "http://northwindtraders.com/Teas"}, _ New Item() With {.Product = "Chang", .Quantity = 25, _ .Url = "http://northwindtraders.com/Specialty"}, _ New Item() With {.Product = "Aniseed Syrup", .Quantity = 5, _ .Url = "http://northwindtraders.com/Specialty"}, _ New Item() With {.Product = "Chef Anton's Cajun Seasoning", _

MCT: Luis Dueñas

Pag 51 de 128

WCF Data Service .Quantity = 30, .Url = "http://northwindtraders.com/Spices"}} _orders(0).Items.Add(_items(0)) _orders(0).Items.Add(_items(1)) _orders(1).Items.Add(_items(2)) _orders(1).Items.Add(_items(3)) End Sub #End Region Public ReadOnly Property Orders() As IQueryable(Of Order) Get Return _orders.AsQueryable() End Get End Property Public ReadOnly Property Items() As IQueryable(Of Item) Get Return _items.AsQueryable() End Get End Property End Class Public Class OrderItems Inherits DataService(Of OrderItemData) ' This method is called only once to initialize ' service-wide policies. Shared Sub InitializeService(ByVal config As DataServiceConfiguration) config.SetEntitySetAccessRule("Orders", _ EntitySetRights.AllRead _ Or EntitySetRights.AllWrite) config.SetEntitySetAccessRule("Items", _ EntitySetRights.AllRead _ Or EntitySetRights.AllWrite) config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3 End Sub End Class End Namespace

En el ejemplo anterior se devuelve el resultado siguiente para el identificador URI

http://myservice/OrderItems.svc/Orders(0)?$expand=Items .

http://localhost:12345/OrderItems.svc/Orders(0) 0 2009-07-25T21:12:30Z

Peter Franken

MCT: Luis Dueñas

Pag 52 de 128

WCF Data Service

Items http://localhost:12345/OrderItems.svc/Orders(0)/Items 2009-07-25T21:12:30Z

http://localhost:12345/OrderItems.svc/Items('Chai')

2009-07-25T21:12:30Z





Chai 10

Chai

http://localhost:12345/OrderItems.svc/Items('Chang')

2009-07-25T21:12:30Z





Chang 25

MCT: Luis Dueñas

Pag 53 de 128

WCF Data Service

Chang





Peter Franken



4.5. Interceptores WCF Data Services permite que una aplicación intercepte los mensajes de solicitud para permitir agregar una lógica personalizada a una operación. Puede utilizar esta lógica personalizada para validar los datos de los mensajes entrantes. También puede utilizarla como restricción adicional del ámbito de una solicitud de consulta, por ejemplo, para insertar una directiva de autorización personalizada por solicitud. Métodos con atributos especiales realizan la interceptación en el servicio de datos. WCF Data Services llama a estos métodos en el punto adecuado del procesamiento de mensajes. Los interceptores se definen por entidad. Los métodos de interceptor no pueden aceptar los parámetros de la solicitud, como sí pueden las operaciones del servicio. Los métodos de interceptor de consulta, a los que se llama al procesar una solicitud GET de HTTP, deben devolver una expresión lambda que determina si los resultados de la consulta deben devolver una instancia del conjunto de entidades del interceptor. El servicio de datos utiliza esta expresión para perfeccionar más la operación solicitada. El ejemplo siguiente muestra una definición de un interceptor de consulta. ' Define a query interceptor for the Orders entity set. _ Public Function OnQueryOrders() As Expression(Of Func(Of Order, Boolean))

Los interceptores de cambio, a los que se llama al procesar las operaciones que no son de consulta, deben devolver void (Nothing en Visual Basic). Los métodos de interceptor de cambio deben aceptar los dos siguientes parámetros: 1.

Un parámetro de un tipo que es compatible con el tipo de entidad del conjunto de entidades. Cuando el servicio de datos llama al interceptor de cambio, el valor de este parámetro refleja la información de la entidad que envía la solicitud.

2.

Un parámetro de tipo UpdateOperations. Cuando el servicio de datos llama al interceptor de cambio, el valor de este parámetro refleja la operación que la solicitud intenta realizar.

El ejemplo siguiente muestra una definición de un interceptor de cambio. ' Define a change interceptor for the Products entity set. _ Public Sub OnChangeProducts(ByVal product As Product, ByVal operations As UpdateOperations)

La interceptación admite los siguientes atributos.

MCT: Luis Dueñas

Pag 54 de 128

WCF Data Service [QueryInterceptor( EnitySetName )] Se llama a los métodos que tienen aplicado el atributo QueryInterceptorAttribute cuando se recibe una solicitud GET de HTTP para el recurso de conjunto de entidades concreto. Estos métodos siempre deben devolver una expresión lambda en el formulario de Expression. [ChangeInterceptor( EnitySetName )] Se llama a los métodos que tienen aplicado el atributo ChangeInterceptorAttribute cuando se recibe una solicitud HTTP distinta de GET para el recurso de conjunto de entidades concreto. Estos métodos siempre deben devolver void (Nothing en Visual Basic).

4.5.1. Cómo: Interceptar mensajes del servicio de datos Con WCF Data Services , puede interceptar mensajes de solicitud, lo que le permitirá agregar lógica personalizada a una operación. Para interceptar un mensaje, se usan métodos con atributos especiales con el servicio de datos.

Para definir un interceptor de consultas para el conjunto de entidades Orders 1.

En el proyecto del servicio de datos de Northwind, abra el archivo Northwind.svc.

2.

En la página de código de la clase Northwind, agregue la instrucción using siguiente (Imports en Visual Basic). Imports System.Linq.Expressions

3.

En la clase Northwind, defina un método de operación de servicio denominado OnQueryOrders como sigue: ' Define a query interceptor for the Orders entity set. _ Public Function OnQueryOrders() As Expression(Of Func(Of Order, Boolean))

Para definir un interceptor de cambios para el conjunto de entidades Products 1.

En el proyecto del servicio de datos de Northwind, abra el archivo Northwind.svc.

2.

En la clase Northwind, defina un método de operación de servicio denominado

OnChangeProducts como sigue: ' Define a change interceptor for the Products entity set. _ Public Sub OnChangeProducts(ByVal product As Product, ByVal operations As UpdateOperations)

Ejemplo En este ejemplo se define un método de interceptor de consultas para el conjunto de entidades Orders que devuelve una expresión lambda. Esta expresión contiene un delegado que filtra los pedidos ( Orders) solicitados basándose en los clientes (Customers) relacionados que tienen un nombre de contacto concreto. El nombre se determina a su vez en función del usuario que realiza la solicitud. En este ejemplo se supone que el servicio de datos se hospeda dentro de una aplicación web ASP.NET que usa WCF, y que la autenticación está habilitada. La clase HttpContext se usa para recuperar el principio de la solicitud actual. ' Define a query interceptor for the Orders entity set. _ Public Function OnQueryOrders() As Expression(Of Func(Of Order, Boolean)) ' Filter the returned orders to only orders ' that belong to a customer that is the current user. Return Function(o) o.Customer.ContactName = _ HttpContext.Current.User.Identity.Name End Function

MCT: Luis Dueñas

Pag 55 de 128

WCF Data Service En este ejemplo se define un método interceptor de cambios para el conjunto de entidades Products. Este método valida la entrada al servicio para una operación Add o Change y produce una excepción si se hace un cambio en un producto que ya no se fabrica. También bloquea la eliminación de productos como una operación no admitida.

' Define a change interceptor for the Products entity set. _ Public Sub OnChangeProducts(ByVal product As Product, ByVal operations As UpdateOperations) If operations = UpdateOperations.Change Then Dim entry As System.Data.Objects.ObjectStateEntry If Me.CurrentDataSource.ObjectStateManager _ .TryGetObjectStateEntry(product, entry) Then ' Reject changes to a discontinued Product. ' Because the update is already made to the entity by the time the ' change interceptor in invoked, check the original value of the Discontinued ' property in the state entry and reject the change if 'true'. If CType(entry.OriginalValues("Discontinued"), Boolean) Then Throw New DataServiceException(400, String.Format( "A discontinued {0} cannot be modified.", product.ToString())) Else Throw New DataServiceException(String.Format( _ "The requested {0} could not be found in the data source.", product.ToString())) End If ElseIf (operations = UpdateOperations.Delete) Then ' Block the delete and instead set the Discontinued flag. Throw New DataServiceException(400, _ "Products cannot be deleted; instead set the Discontinued flag to 'true'") End If End If End

4.6. Desarrollar e implementar WCF Data Services En este tema se proporciona información sobre el desarrollo y la implementación de WCF Data Services.

Desarrollar el servicio de datos de WCF Cuando use WCF Data Services para crear un servicio de datos que admita Open Data Protocol (OData) , debe realizar las siguientes tareas básicas durante el desarrollo: 1.

Definir el modelo de datos WCF Data Services admite distintos proveedores de servicios de datos que le permiten definir un modelo de datos basado en los datos de diferentes orígenes de datos, desde bases de datos relacionales hasta tipos de datos enlazados en tiempo de ejecución.

2.

Crear el servicio de datos El servicio de datos más básico expone una clase que hereda de la clase DataService, con un tipo T que es el nombre completo del espacio de nombres del contenedor de la entidad.

3.

Configurar el servicio de datos De forma predeterminada, WCF Data Services deshabilita el acceso a los recursos que expone un contenedor de entidades. La clase DataServiceConfiguration le permite configurar el acceso a los

MCT: Luis Dueñas

Pag 56 de 128

WCF Data Service recursos y a las operaciones del servicio, especificar la versión admitida de OData , así como definir otros comportamientos de todo el servicio, como los comportamientos de los procesamientos por lotes o el número máximo de entidades que pueden devolverse en una única fuente de respuesta. En este tema se describe principalmente el desarrollo y la implementación de los servicios de datos mediante el uso de Visual Studio.

Elegir un servidor web de desarrollo Cuando desarrolle un servicio de datos de WCF como aplicación de ASP.NET o un sitio web de ASP.NET mediante el uso de Visual Studio, dispone de una selección de servidores web en los que ejecutar el servicio de datos durante el desarrollo. Los siguientes servidores web se integran con Visual Studio para facilitar la prueba y depuración de los servicios de datos en el equipo local. 1.

Servidor IIS local Cuando cree un servicio de datos que sea una aplicación de ASP.NET o un sitio web de ASP.NET que se ejecute en Internet Information Services (IIS), se recomienda que desarrolle y pruebe el servicio de datos con IIS en el equipo local. Si se ejecuta el servicio de datos en IIS, se facilita el seguimiento de las solicitudes HTTP durante el proceso de depuración. De esta forma, puede predeterminar los derechos necesarios que requiere IIS para obtener acceso a los archivos, a las bases de datos y al resto de los recursos que necesita el servicio de datos. Para ejecutar el servicio de datos en IIS, debe asegurarse de que tanto IIS como Windows Communication Foundation (WCF) estén instalados y configurados correctamente, así como de conceder acceso a las cuentas de IIS en las bases de datos y en el sistema de archivos. Nota: Debe ejecutar Visual Studio con derechos de administrador para habilitar el entorno de desarrollo con el fin de configurar el servidor IIS local.

2.

Servidor de desarrollo de Visual Studio Visual Studio incluye un servidor web integrado, el servidor de desarrollo de Visual Studio, que es el servidor web predeterminado de los proyectos de ASP.NET. Este servidor web está diseñado para ejecutar proyectos de ASP.NET en el equipo local durante el desarrollo. El Tutorial rápido del servicio de datos de WCF muestra cómo crear un servicio de datos que se ejecute en el servidor de desarrollo de Visual Studio. Debe tener en cuenta las siguientes limitaciones cuando use el servidor de desarrollo de Visual Studio para desarrollar el servicio de datos:

o

A este servidor solo se puede acceder desde el equipo local.

o

Este servidor escucha en localhost y en un puerto específico, no en el puerto 80, que es el puerto predeterminado para los mensajes HTTP.

o

Este servidor ejecuta el servicio de datos en el contexto de su cuenta de usuario actual. Por ejemplo, si está ejecutando como usuario de nivel de administrador, un servicio de datos que se ejecute en el servidor de desarrollo de Visual Studio tendrá privilegios de nivel de administrador. De esta forma, el servicio de datos puede acceder a los recursos para los que no tiene derechos de acceso cuando se implementa en un servidor IIS.

o

Este servidor no incluye las características adicionales de IIS, como la autenticación.

o

Este servidor no puede administrar los flujos HTTP fragmentados, que envía de forma predeterminada el cliente de WCF Data Services cuando se accede a los datos binarios grandes desde el servicio de datos.

MCT: Luis Dueñas

Pag 57 de 128

WCF Data Service o

Este servidor experimenta problemas al procesar el carácter de punto (.) en una dirección URL, aunque este carácter lo admita WCF Data Services en valores clave.

Sugerencia: Aunque puede usar el servidor de desarrollo de Visual Studio para probar los servicios de datos durante el desarrollo, debe probarlos de nuevo después de implementarlos en un servidor web que ejecute IIS. 3.

Entorno de desarrollo de Windows Azure Windows Azure Tools para Visual Studio incluye un conjunto integrado de herramientas para desarrollar los servicios de Windows Azure en Visual Studio. Con estas herramientas, puede desarrollar un servicio de datos que se puede implementar en Windows Azure y que puede probar en el equipo local antes de su implementación. Use estas herramientas cuando use Visual Studio para desarrollar un servicio de datos que se ejecute en la plataforma Windows Azure.

Sugerencias de desarrollo Debe plantearse lo siguiente cuando desarrolle un servicio de datos: Determine los requisitos de seguridad del servicio de datos, si planea autenticar a los usuarios o restringir el acceso de usuarios específicos. Un programa de inspección HTTP puede ser muy útil cuando se depure un servicio de datos, ya que permite inspeccionar el contenido de los mensajes de solicitud y respuesta. Cualquier analizador de paquetes de red que pueda mostrar paquetes sin procesar se puede usar para inspeccionar las solicitudes HTTP al servicio de datos y las respuestas. Cuando depure un servicio de datos, quizá desee obtener más información sobre un error desde el servicio de datos que durante el funcionamiento normal. Puede obtener información adicional sobre el error del servicio de datos si establece la propiedad UseVerboseErrors de la clase DataServiceConfiguration en true y la propiedad IncludeExceptionDetailInFaults del atributo ServiceDebugBehavior de la clase de servicio de datos en true. También puede habilitar la traza en WCF para ver las excepciones producidas en la capa de mensajería HTTP. Se suele desarrollar un servicio de datos como proyecto de aplicación de ASP.NET, pero también puede crear el servicio de datos como proyecto de sitio web de ASP.NET en Visual Studio. Cuando cree un servicio de datos mediante el uso del cuadro de diálogo Agregar nuevo elemento de Visual Studio, ASP.NET hospeda el servicio de datos en IIS. Mientras que ASP.NET e IIS sean el host predeterminado para un servicio de datos, se admiten opciones de hospedaje adicionales.

Implementar WCF Data Services El servicio de datos de WCF proporciona flexibilidad al elegir el proceso que hospede el servicio de datos. Puede usar Visual Studio para implementar un servicio de datos en las siguientes plataformas: Servidor web hospedado en IIS Cuando se desarrolla un servicio de datos como proyecto de ASP.NET, se puede implementar en un servidor web de IIS mediante los procesos de implementación estándar de ASP.NET. Visual Studio proporciona las siguientes tecnologías de implementación para ASP.NET, en función del tipo de proyecto de ASP.NET que hospede el servicio de datos que esté implementando.

o

o

Tecnologías de implementación para aplicaciones web ASP.NET



Paquete de implementación web



Publicación con un solo clic

Tecnologías de implementación para sitios web ASP.NET

MCT: Luis Dueñas

Pag 58 de 128

WCF Data Service 

Herramienta Copiar sitio web



Herramienta Publicar sitio web



XCopy

Sugerencia: Antes de intentar implementar el servicio de datos en IIS, asegúrese de haber probado la implementación en un servidor web que esté ejecutando IIS. Windows Azure Puede implementar un servicio de datos en Windows Azure con Windows Azure Tools para Visual Studio. Puede descargar Windows Azure Tools para Visual Studio desde el Centro de descarga de Microsoft.

Consideraciones de implementación Debe plantearse lo siguiente cuando implemente un servicio de datos: Cuando implemente un servicio de datos que use el proveedor de Entity Framework para acceder a una base de datos de SQL Server, quizá también deba propagar las estructuras de datos, los datos o ambos con la implementación del servicio de datos. Visual Studio puede crear automáticamente scripts (archivos .sql) para ello en la base de datos de destino. Estos scripts se pueden incluir en el paquete de implementación web de una aplicación de ASP.NET. En un sitio web de ASP.NET, puede hacer esto con el Asistente para publicar bases de datos en Visual Studio. Puesto que WCF Data Services incluye una implementación básica de WCF, puede usar Windows Server AppFabric para supervisar un servicio de datos implementado en IIS que se ejecute en Windows Server.

4.7. Proteger WCF Data Services En este tema se describen consideraciones de seguridad específicas del desarrollo. de la implementación y la ejecución de WCF Data Services y de aplicaciones que acceden a servicios compatibles con Open Data Protocol (OData) . También debe seguir las recomendaciones para crear aplicaciones de .NET Framework seguras. Cuando planee cómo proteger un servicio de OData basado en WCF Data Services , debe tratar tanto la autenticación, el proceso para detectar y comprobar la identidad de una entidad de seguridad, como la autorización, el proceso para determinar si una entidad de seguridad autenticada puede acceder a los recursos solicitados. También debe plantearse si va a cifrar el mensaje mediante SSL.

Autenticar las solicitudes de clientes WCF Data Services no implementa ningún tipo de autenticación proprio, sino que confía en las medidas de autenticación del host de servicio de datos. Es decir, el servicio asume que cualquier solicitud recibida ya la ha autenticado el host de red y que este ha identificado correctamente la entidad de seguridad para la solicitud adecuadamente a través de las interfaces proporcionadas por WCF Data Services . Estas opciones de autenticación y los distintos enfoques se describen en el Programa sobre OData y autenticación.

Opciones de autenticación para un servicio de datos de WCF En la siguiente tabla se enumeran algunos de los mecanismos de autenticación disponibles para ayudar al usuario a autenticar solicitudes en el servicio de datos de WCF. Opciones de Descripción autenticación Autenticación anónima

Cuando está habilitada la autenticación HTTP anónima, cualquier entidad de seguridad puede conectarse al servicio de datos. No se necesitan credenciales para el acceso

MCT: Luis Dueñas

Pag 59 de 128

WCF Data Service anónimo. Use esta opción solo cuando desee permitir a alguien el acceso al servicio de datos. Se necesitan credenciales formadas por un nombre de usuario y una contraseña para la autenticación. Admite autenticación de clientes que no sean de Windows.

Autenticación básica e implícita

Nota: de seguridad Se envían credenciales de autenticación básica (nombre de usuario y contraseña) sin cifrado y se pueden interceptar. La autenticación implícita envía un hash basado en las credenciales proporcionadas. De esta forma, la protección es mayor que la de la autenticación básica. Ambas son susceptibles de sufrir ataques de tipo " Man in the middle". Cuando se usan estos métodos de autenticación, debe plantearse la comunicación cifrada entre el cliente y el servicio de datos mediante el uso de la Capa de sockets seguros (SSL). Microsoft Internet Information Services (IIS) proporciona una implementación de la autenticación tanto básica como implícita para solicitudes HTTP en una aplicación ASP.NET. Esta implementación del proveedor de autenticación de Windows permite que una aplicación cliente de .NET Framework proporcione credenciales en el encabezado HTTP de la solicitud al servicio de datos para negociar sin ningún problema la autenticación de un usuario de Windows. Cuando desee que el servicio de datos use la autenticación básica basada en algún servicio de autenticación personalizado y no en las credenciales de Windows, debe implementar un módulo HTTP de ASP.NET personalizado para autenticación.

Autenticación de Windows

Autenticación de formularios de ASP.NET

Las credenciales basadas en Windows se intercambian mediante el uso de NTLM o Kerberos. Este mecanismo es más seguro que la autenticación básica o implícita, pero requiere que el cliente sea una aplicación basada en Windows. IIS también proporciona una implementación de autenticación de Windows para solicitudes HTTP en una aplicación de ASP.NET. La autenticación de formularios permite autenticar a los usuarios mediante su propio código y, a continuación, conservar un token de autenticación en una cookie o en la dirección URL de la página. Autentique el nombre de usuario y la contraseña de los usuarios que usen un formulario de inicio de sesión que cree. Las solicitudes no autenticadas se redirigen a una página de inicio de sesión, en la que el usuario proporciona las credenciales y envía el formulario. Si la aplicación autentica la solicitud, el sistema emite un vale que contiene una clave con el fin de restablecer la identidad para posteriores solicitudes. Nota: de seguridad De forma predeterminada, la cookie que contiene el vale de autenticación de formularios no está protegido cuando use la autenticación de formularios en una aplicación web de ASP.NET. Debe plantearse exigir SSL para proteger tanto el vale de autenticación como las credenciales de inicio de sesión iniciales. En la autenticación basada en notificaciones, el servicio de datos confía en un servicio de proveedor de identidad de “terceros” de confianza para autenticar al usuario. El

Autenticación basada en notificaciones

proveedor de identidad autentica positivamente al usuario que solicita acceso a los recursos del servicio de datos y emite un token que concede acceso a los recursos solicitados. A continuación, este token se presenta en el servicio de datos, que concede acceso al usuario basándose en la relación de confianza con el servicio de identidad que emitió el token de acceso. La ventaja de usar un proveedor de autenticación basado en notificaciones es que se pueden usar para autenticar distintos tipos de clientes en dominios de confianza. Si se usan tales proveedores de terceros, un servicio de datos puede descargar los requisitos de mantenimiento y autenticación de usuarios. OAuth 2.0 es un protocolo de

MCT: Luis Dueñas

Pag 60 de 128

WCF Data Service autenticación basado en notificaciones compatible con el Control de acceso de AppFabric de Windows Azure para autorización federada como servicio. Este protocolo admite servicios basados en REST.

Autenticación de la biblioteca cliente De forma predeterminada, la biblioteca de cliente de WCF Data Services no proporciona credenciales cuando se realiza una solicitud a un servicio de OData . Cuando el servicio de datos necesita credenciales de inicio de sesión para autenticar a un usuario, estas credenciales se puedan proporcionar en una clase NetworkCredential a la que se accede desde la propiedad Credentials de la clase DataServiceContext, como en el ejemplo siguiente: ' Set the client authentication credentials. context.Credentials = New NetworkCredential(userName, password, domain)

Cuando el servicio de datos necesite credenciales de inicio de sesión que no se puedan especificar mediante el uso de un objeto NetworkCredential, como un token basado en notificaciones o una cookie, debe establecer manualmente los encabezados en la solicitud HTTP, que suelen ser los encabezados Authorization y Cookie.

Suplantación Por lo general, el servicio de datos accede a los recursos necesarios, como los archivos del servidor o de la base de datos, mediante el uso de las credenciales del proceso de trabajo que hospeda el servicio de datos. Al usar la suplantación, las aplicaciones ASP.NET se pueden ejecutar con la identidad de Windows (cuenta de usuario) del usuario que realiza la solicitud. La suplantación se suele usar en aplicaciones que confían en IIS para autenticar al usuario y las credenciales de esta entidad de seguridad se usan para obtener acceso a los recursos necesarios.

Configurar la autorización del servicio de datos La autorización es la concesión de acceso a los recursos de la aplicación a una entidad de seguridad o a un proceso que se identifique basándose en una autenticación correcta previa. Como regla general, solo debe conceder los derechos estrictamente necesarios a los usuarios del servicio de datos para realizar las operaciones que necesiten las aplicaciones cliente.

Restringir el acceso a los recursos del servicio de datos De forma predeterminada, WCF Data Services permite conceder acceso común de lectura y escritura a los recursos del servicio de datos (conjunto de entidades y operaciones de servicio) a cualquier usuario que pueda acceder al servicio de datos. Las reglas que definen el acceso de lectura y escritura se pueden definir por separado para cada conjunto de entidades expuesto por el servicio de datos, así como a las operaciones de servicio. Se recomienda limitar el acceso tanto de lectura como de escritura a solo los recursos que necesite la aplicación cliente.

Implementar interceptores basados en roles Los interceptores permiten interceptar solicitudes en los recursos del servicio de datos antes de que actúe en ellos dicho servicio. Los interceptores permiten tomar decisiones de autorización basadas en el usuario autenticado que realiza la solicitud.

Restringir el acceso al almacén de datos persistentes y a los recursos locales A las cuentas que se usan para acceder al almacén persistente se les deben conceder solo los derechos estrictamente necesarios en una base de datos o en el sistema de archivos para admitir los requisitos del servicio de datos. Cuando se usa la autenticación anónima, se trata de la cuenta usada para ejecutar la aplicación de hospedaje. Cuando se usa la suplantación, a los usuarios autenticados se les debe conceder acceso a estos recursos, normalmente como parte de un grupo de Windows.

Otras consideraciones de seguridad Proteger los datos de la carga OData se basa en el protocolo HTTP. En un mensaje HTTP, el encabezado puede contener valiosas credenciales de usuario, en función de la autenticación implementada por el servicio de datos. El cuerpo del

MCT: Luis Dueñas

Pag 61 de 128

WCF Data Service mensaje también puede contener datos valiosos de clientes que se deben proteger. En ambos casos, se recomienda que use SSL para proteger esta información a través de la conexión.

Cookies y encabezados del mensaje ignorados Los encabezados de solicitudes HTTP, que no sean los que declaran los tipos de contenido y las ubicaciones de los recursos, se ignoran y nunca los establece el servicio de datos. Las cookies se pueden usar como parte de un esquema de autenticación, por ejemplo, con la autenticación de los formularios de ASP.NET. WCF Data Services ignora, sin embargo, las cookies HTTP establecidas en una solicitud entrante. El host de un servicio de datos puede procesar la cookie, pero el tiempo de ejecución de WCF Data Services nunca analiza ni devuelve cookies. La biblioteca cliente de WCF Data Services tampoco procesa las cookies enviadas en la respuesta.

Requisitos personalizados de hospedaje De forma predeterminada, WCF Data Services se crea como aplicación de ASP.NET hospedada en IIS. De esta forma, el servicio de datos puede sacar provecho de los comportamientos seguros de esta plataforma. Puede definir los WCF Data Services hospedados por un host personalizado. Los componentes y la plataforma que hospedan un servicio de datos deben asegurar los siguientes comportamientos de seguridad para impedir ataques contra el servicio de datos: Limitar la longitud del URI aceptada en una solicitud del servicio de datos para todas las operaciones posibles. Limitar el tamaño de los mensajes HTTP tanto entrantes como salientes. Limitar el número total de solicitudes pendientes en cualquier momento dado. Limitar la longitud de los encabezados HTTP y sus valores, así como proporcionar acceso a WCF Data Services a los datos del encabezado. Detectar y contraatacar los ataques conocidos, como ataques SYN de TCP y de reproducción de mensajes.

Los valores ya no se codifican Los valores de propiedad enviados al servicio de datos ya no los codifica el tiempo de ejecución de WCF Data Services . Por ejemplo, cuando una propiedad de cadena de una entidad contiene contenido HTML con formato, el servicio de datos no codifica las etiquetas en HTML. Además, el servicio de datos ya no codifica los valores de propiedad de la respuesta. Asimismo, la biblioteca cliente ya no efectúa ningún tipo de codificación adicional.

Consideraciones sobre aplicaciones cliente Las consideraciones de seguridad siguientes se aplican a las aplicaciones que usan el cliente de WCF Data Services para acceder a los servicios de OData : La biblioteca cliente asume que los protocolos usados para acceder al servicio de datos proporcionan un nivel de seguridad adecuado. La biblioteca cliente usa todos los valores predeterminados para las opciones de tiempos de espera y de análisis de las pilas de transporte proporcionadas por la plataforma subyacente. La biblioteca cliente no lee la configuración a partir de los archivos de configuración de la aplicación. La biblioteca cliente no implementa los mecanismos de acceso entre dominios. En su lugar, confía en los mecanismos proporcionados por la pila HTTP subyacente. La biblioteca cliente no dispone de elementos de interfaz de usuario y nunca intenta mostrar o presentar los datos que recibe o envía. Se recomienda que las aplicaciones cliente siempre validen la entrada del usuario, así como los datos aceptados de servicios que no sean de confianza.

4.8. Hospedar el servicio de datos MCT: Luis Dueñas

Pag 62 de 128

WCF Data Service Mediante WCF Data Services , puede crear un servicio que exponga datos como una fuente de Open Data Protocol (OData) . Este servicio de datos se define como una clase que hereda de DataService. Esta clase proporciona la funcionalidad necesaria para procesar mensajes de solicitud, realizar actualizaciones en el origen de datos y generar mensajes de respuesta, como requiere OData . Sin embargo, un servicio de datos no puede enlazar solicitudes HTTP de entrada ni escucharlas en un socket de red. Para esta funcionalidad necesaria, el servicio de datos se basa en un componente de hospedaje. El host del servicio de datos realiza las siguientes tareas en nombre del servicio de datos: Realiza escuchas de las solicitudes y las enruta al servicio de datos. Crea una instancia del servicio de datos para cada solicitud. Solicita al servicio de datos que procese la solicitud entrante. Envía la respuesta en nombre del servicio de datos. Para simplificar el hospedaje de un servicio de datos, WCF Data Services está diseñado para integrarse con Windows Communication Foundation (WCF). El servicio de datos proporciona una implementación WCF predeterminada que actúa como host del servicio de datos en una aplicación ASP.NET. Por consiguiente, un servicio de datos se puede hospedar de una de las siguientes maneras: En una aplicación ASP.NET. En una aplicación administrada que admita servicios WCF autohospedados. En algún otro host del servicio de datos personalizado.

Hospedar un servicio de datos en una aplicación ASP.NET Cuando se usa el cuadro de diálogo Agregar nuevo elemento en Visual Studio para definir un servicio de datos en una aplicación ASP.NET, la herramienta genera dos nuevos archivos en el proyecto. El primer archivo tiene una extensión .svc e indica al tiempo de ejecución de WCF cómo crear instancias del servicio de datos. A continuación, se muestra un ejemplo de este archivo para el servicio de datos de ejemplo Northwind que se creó al completar el tutorial rápido:

Esta directiva indica a la aplicación que debe crear el host del servicio para la clase del servicio de datos con nombre utilizando la clase DataServiceHostFactory. La página de código subyacente del archivo .svc contiene la clase que es la implementación del propio servicio de datos, el cual se define para el servicio de datos de ejemplo Northwind del siguiente modo: Public Class Northwind Inherits DataService(Of NorthwindEntities)

Dado que un servicio de datos se comporta como un servicio WCF, el servicio de datos se integra con ASP.NET y sigue el modelo de programación web de WCF.

Servicios WCF autohospedados Dado que incorpora una implementación WCF, WCF Data Services admite el servicio de datos autohospedado como un servicio WCF. Un servicio puede hospedarse a sí mismo en cualquier aplicación .NET Framework, como una aplicación de consola. La clase DataServiceHost, que hereda de WebServiceHost, se utiliza para crear instancias del servicio de datos en una dirección concreta. El autohospedaje se puede utilizar para el desarrollo y las pruebas porque facilita el despliegue y la solución de problemas del servicio. Sin embargo, este tipo de hospedaje no ofrece las características de administración y hospedaje avanzadas que proporciona ASP.NET o Internet Information Services (IIS).

Definir un host de servicio de datos personalizado

MCT: Luis Dueñas

Pag 63 de 128

WCF Data Service Para los casos en los que la implementación de un host de WCF sea demasiado restrictiva, se puede definir también un host personalizado para un servicio de datos. Cualquier clase que implemente la interfaz IDataServiceHost se puede utilizar como el host de red de un servicio de datos. Un host personalizado debe implementar la interfaz IDataServiceHost y ser capaz de controlar las siguientes responsabilidades básicas del host del servicio de datos: Proporcionar la ruta de acceso raíz del servicio al servicio de datos. Procesar la información de encabezados de respuesta y solicitud en la implementación de miembro de IDataServiceHost adecuada. Controlar las excepciones iniciadas por el servicio de datos. Validar parámetros en la cadena de consulta.

4.9. Trabajar con varias versiones de WCF Data Services Open Data Protocol (OData) le permite tener acceso a un origen de datos de forma remota mediante protocolos de Internet estándar sobre HTTP. A medida que se lanzan nuevas versiones de OData , quizá las aplicaciones cliente no usen la misma versión de OData que admite el servicio de datos. Puede que una aplicación cliente antigua tenga acceso a un servicio de datos utilizando una versión más reciente de OData o quizá una aplicación cliente use una versión más reciente de la biblioteca de cliente de WCF Data Services que admite una versión más reciente de OData que la de la fuente de OData a la que se está obteniendo acceso. WCF Data Services aumenta la compatibilidad que proporciona OData para administrar tales escenarios de versión. También existe soporte técnico para generar y usar metadatos del modelo de datos con el fin de crear las clases de servicio de datos de cliente cuando el cliente usa una versión distinta de OData de la que usa el servicio de datos.

Versiones del protocolo El servicio de datos se puede configurar para definir la versión más alta del protocolo OData que usará el servicio, independientemente de la versión solicitada por el cliente. Para ello, especifique un valor de la enumeración DataServiceProtocolVersion para la propiedad MaxProtocolVersion de la clase DataServiceBehavior que usa el servicio de datos. Cuando una aplicación usa las bibliotecas de cliente de WCF Data Services para tener acceso a un servicio de datos, las bibliotecas establecen automáticamente estos encabezados en los valores correctos, en función de la versión de OData y de las características que se usan en la aplicación. De forma predeterminada, WCF Data Services utiliza la versión de protocolo más baja que admita la operación solicitada. En la siguiente tabla se detallan las versiones de .NET Framework y Silverlight que incluyen compatibilidad con WCF Data Services para versiones específicas del protocolo OData . Versión del protocolo OData Compatibilidad introducida en… Versión 1

.NET Framework versión 3.5 Service Pack 1 (SP1) Versión 3 de Silverlight. .NET Framework versión 4

Versión 2

Actualización a .NET Framework versión 3.5 SP1. Versión 4 de Silverlight.

Versiones de metadatos De forma predeterminada, WCF Data Services usa la versión 1.1 de CSDL para representar un modelo de datos. Este siempre es el caso para los modelos de datos basados en un proveedor de reflexión o en un proveedor del servicio de datos personalizados. No obstante, cuando se define un modelo de datos

MCT: Luis Dueñas

Pag 64 de 128

WCF Data Service mediante Entity Framework , la versión devuelta de CSDL es la misma que la que usa Entity Framework . La versión de CSDL la determina el espacio de nombres del elemento Schema. El elemento DataServices de los metadatos devueltos también contiene un atributo DataServiceVersion, que tiene el mismo valor que el encabezado DataServiceVersion del mensaje de respuesta. Las aplicaciones cliente, como el cuadro de diálogo Agregar referencia de servicio de Visual Studio, usan esta información para generar las clases del servicio de datos de cliente que funcionen correctamente con la versión de WCF Data Services que hospede el servicio de datos.

5. Biblioteca de cliente de WCF Data Services Cualquier aplicación puede interactuar con un servicio de datos basado en Open Data Protocol (OData) si puede enviar una solicitud HTTP y procesar la fuente de OData que devuelve un servicio de datos. Esta interoperabilidad permite tener acceso a los servicios basados en OData de una amplia gama de aplicaciones basadas en web. WCF Data Services incluye bibliotecas de cliente que proporcionan una experiencia de programación más enriquecida cuando se usan fuentes OData desde aplicaciones basadas en .NET Framework o Silverlight. Las dos clases principales de la biblioteca cliente son la clase DataServiceContext y la clase DataServiceQuery. La clase DataServiceContext encapsula las operaciones admitidas en un servicio de datos determinado. Los servicios de OData carecen de estado, pero no ocurre lo mismo con el contexto. Por consiguiente, puede usar la clase DataServiceContext para mantener el estado en el cliente entre las interacciones con el servicio de datos para admitir características como la administración de cambios. Esta clase también administra las identidades y realiza el seguimiento de los cambios. La clase DataServiceQuery representa una consulta en un conjunto de entidades concreto. En esta sección se describe cómo usar las bibliotecas de cliente para obtener acceso a los datos de una aplicación cliente de .NET Framework y para cambiarlos. Existen otras bibliotecas de cliente disponibles que permiten usar una fuente de OData en otros tipos de aplicaciones.

5.1. Generar la biblioteca de cliente del servicio de datos Un servicio de datos que implementa Open Data Protocol (OData) puede devolver un documento de metadatos del servicio que describe el modelo de datos expuesto por la fuente de OData . Puede usar el cuadro de diálogo Agregar referencia de servicio de Visual Studio para agregar una referencia a un servicio basado en OData . Cuando se usa esta herramienta para agregar una referencia a los metadatos que se devuelven en una fuente de OData en un proyecto de cliente, realiza las acciones siguientes: Solicita el documento de metadatos del servicio al servicio de datos e interpreta los metadatos devueltos. Nota: Los metadatos devueltos se almacenan en el proyecto de cliente como un archivo .edmx. Este archivo .edmx no se puede abrir utilizando el diseñador de Entity Data Model ya que no tiene el mismo formato que un archivo .edmx utilizado por Entity Framework. Puede ver este archivo de metadatos utilizando el editor XML o cualquier editor de texto. Genera una representación del servicio como una clase de contenedor de entidades que hereda de DataServiceContext. Esta clase de contenedor de entidades generada es similar al contenedor de entidades generado por las herramientas de Entity Data Model. Genera clases de datos para los tipos de modelo de entidad que detecta en los metadatos del servicio. Agrega al proyecto una referencia al ensamblado System.Data.Services.Client. Las clases del servicio de datos del cliente también se pueden generar usando la herramienta DataSvcUtil.exe en el símbolo del sistema.

MCT: Luis Dueñas

Pag 65 de 128

WCF Data Service

Asignación de tipos de datos del cliente Cuando se usa el cuadro de diálogo Agregar referencia de servicio en Visual Studio o la herramienta DataSvcUtil.exe para generar clases de datos del cliente que se basan en una fuente de OData , los tipos de datos de .NET Framework se asignan a los tipos primitivos del modelo de datos de la siguiente forma: Tipo del modelo de datos Tipo de datos de .NET Framework Edm.Binary

Byte []

Edm.Boolean

Boolean

Edm.Byte

Byte

Edm.DateTime

DateTime

Edm.Decimal

Decimal

Edm.Double

Double

Edm.Guid

Guid

Edm.Int16

Int16

Edm.Int32

Int32

Edm.Int64

Int64

Edm.SByte

SByte

Edm.Single

Single

Edm.String

String

5.1.1. Utilidad de cliente de WCF Data Service (DataSvcUtil.exe) DataSvcUtil.exe es una herramienta de línea de comandos proporcionada por WCF Data Services que utiliza una fuente de Open Data Protocol (OData) y genera las clases de servicio de datos de cliente necesarias para tener acceso a un servicio de datos desde una aplicación cliente de .NET Framework. Esta utilidad puede generar las clases de datos utilizando los siguientes orígenes de metadatos: El URI de la raíz de un servicio de datos. La utilidad solicita el documento de metadatos de servicio, que describe el modelo de datos expuesto por el servicio de datos. Un archivo de modelo de datos (.csdl) definido mediante el lenguaje de definición de esquemas conceptuales (CSDL), tal y como se define en [MC-CSDL]: Formato de archivos de definición de esquemas conceptuales. Un archivo .edmx creado mediante las herramientas de Entity Data Model que se proporcionan con Entity Framework. La herramienta DataSvcUtil.exe se instala en el directorio de .NET Framework. En muchos casos, se encuentra en C:\Windows\Microsoft.NET\Framework\v4.0. Para los sistemas de 64 bits, se encuentra en C:\Windows\Microsoft.NET\Framework64\v4.0. También puede tener acceso a la herramienta DataSvcUtil.exe desde el símbolo del sistema de Visual Studio (haga clic en Inicio, seleccione Todos los programas, Microsoft Visual Studio 2010, Visual Studio Tools y haga clic en Símbolo del sistema de Visual Studio 2010).

datasvcutil /out:file [/in:file | /uri:serviceuri] [/dataservicecollection] [/language:devlang] [/nologo] [/version:ver] [/help] Parámetros Opción /dataservicecollection /help

MCT: Luis Dueñas

Descripción Especifica que también se genera el código necesario para enlazar los objetos a los controles. Muestra la sintaxis de comandos y opciones para la herramienta.

Pag 66 de 128

WCF Data Service – O bien – /? /in:

Especifica el archivo .csdl o .edmx o un directorio donde se encuentra el archivo.

/language:[VB|CSharp]

Especifica el lenguaje de los archivos de código fuente generados. El lenguaje predeterminado es C#.

/nologo

Evita que se muestre el mensaje de copyright.

/out:

Especifica el nombre del archivo de código fuente que contiene las clases de servicio de datos de cliente generadas.

/uri:

El URI de la fuente de OData .

/version:[1.0|2.0]

Especifica la versión superior aceptada de OData . La versión se determina basándose en el atributo DataServiceVersion del elemento DataService en los metadatos devueltos del servicio de datos. Al especificar el parámetro /dataservicecollection, también debe especificar /version:2.0 para habilitar el enlace de datos.

5.1.2. Cómo: Agregar una referencia a un servicio de datos Puede usar el cuadro de diálogo Agregar referencia de servicio de Visual Studio para agregar una referencia a WCF Data Services . Esto le permite tener acceso más fácilmente a un servicio de datos en una aplicación cliente desarrollada en Visual Studio. Cuando complete este procedimiento, se generarán clases de datos basadas en los metadatos que se obtienen del servicio de datos.

Para agregar una referencia a un servicio de datos 1. 2. 3.

(Opcional) Si el servicio de datos no forma parte de la solución y no se está ejecutando, inícielo y anote el URI del mismo. Haga clic con el botón secundario en el proyecto de cliente y, a continuación, seleccione Agregar referencia de servicio. Si el servicio de datos forma parte de la solución actual, haga clic en Detectar. – O bien – En el cuadro de texto Dirección, escriba la dirección URL base del servicio de datos, como por ejemplo, http://localhost:1234/Northwind.svc y, a continuación, haga clic en Ir.

4.

Haga clic en Aceptar. De este modo se agrega un nuevo archivo de código que contiene las clases de datos que se usan para obtener acceso e interactuar con los recursos del servicio de datos como objetos.

5.1.3. Cómo: Generar manualmente clases del servicio de datos del cliente WCF Data Services se integra con Visual Studio para permitir la generación automática de las clases del servicio de datos del cliente cuando se usa el cuadro de diálogo Agregar referencia de servicio para agregar una referencia a un servicio de datos en un proyecto de Visual Studio. También puede generar manualmente las mismas clases del servicio de datos del cliente mediante la herramienta de generación de código DataSvcUtil.exe. Esta herramienta, que se incluye con WCF Data Services , genera las clases de .NET Framework a partir de la definición del servicio de datos. También se puede usar para generar clases del servicio de datos a partir del archivo de modelo conceptual (.csdl) y del archivo .edmx que representa un modelo de Entity Framework en un proyecto Visual Studio. El ejemplo usado en este tema crea clases del servicio de datos del cliente basadas en el servicio de datos de ejemplo de Northwind. Este servicio se crea cuando se completa el Tutorial rápido de WCF Data Services. Algunos ejemplos de este tema requieren el archivo de modelo conceptual para el modelo de Northwind. Algunos ejemplos de este tema requieren el archivo de modelo conceptual .edmx para el modelo de Northwind.

MCT: Luis Dueñas

Pag 67 de 128

WCF Data Service

Para generar clases de C# que admitan el enlace de datos En el símbolo del sistema, ejecute el siguiente comando sin los saltos de línea: "%windir%\Microsoft.NET\Framework\v3.5\DataSvcUtil.exe" /dataservicecollection /version:2.0 /language:CSharp /out:Northwind.cs /uri:http://localhost:12345/Northwind.svc

Nota: Debe reemplazar el valor proporcionado para el parámetro /uri: con el URI de la instancia del servicio de datos de ejemplo de Northwind.

Para generar clases Visual Basic que admitan el enlace de datos En el símbolo del sistema, ejecute el siguiente comando sin los saltos de línea: "%windir%\Microsoft.NET\Framework\v3.5\DataSvcUtil.exe" /dataservicecollection /version:2.0 /language:VB /out:Northwind.vb /uri:http://localhost:12345/Northwind.svc

Para generar clases de C# basadas en el URI de servicio En el símbolo del sistema, ejecute el siguiente comando sin los saltos de línea: "%windir%\Microsoft.NET\Framework\v3.5\DataSvcUtil.exe" /language:CSharp /out:northwind.cs /uri:http://localhost:12345/Northwind.svc

Para generar clases de Visual Basic basadas en el URI de servicio En el símbolo del sistema, ejecute el siguiente comando sin los saltos de línea: "%windir%\Microsoft.NET\Framework\v3.5\datasvcutil.exe" /language:VB /out:Northwind.vb /uri:http://localhost:12345/Northwind.svc

Para generar clases de C# basadas en el archivo de modelo conceptual (CSDL) En el símbolo del sistema, ejecute el siguiente comando sin los saltos de línea: "%windir%\Microsoft.NET\Framework\v3.5\datasvcutil.exe" /language:CSharp /in:Northwind.csdl /out:Northwind.cs

Para generar clases de Visual Basic basadas en el archivo de modelo conceptual (CSDL) En el símbolo del sistema, ejecute el siguiente comando sin los saltos de línea: "%windir%\Microsoft.NET\Framework\v3.5\datasvcutil.exe" /language:VB /in:Northwind.csdl /out:Northwind.vb

Para generar clases de C# basadas en el archivo .edmx En el símbolo del sistema, ejecute el siguiente comando sin los saltos de línea: "%windir%\Microsoft.NET\Framework\v3.5\datasvcutil.exe" /language:CSharp /in:Northwind.edmx /out:c:\northwind.cs

Para generar clases de Visual Basic basadas en el archivo .edmx En el símbolo del sistema, ejecute el siguiente comando sin los saltos de línea: "%windir%\Microsoft.NET\Framework\v3.5\datasvcutil.exe" /language:VB /in:Northwind.edmx /out:c:\northwind.vb

5.2. Consultar el servicio de datos La biblioteca de cliente de WCF Data Services le permite ejecutar consultas en un servicio de datos mediante los conocidos modelos de programación de .NET Framework, incluido el uso de Language Integrated Query (LINQ). La biblioteca de cliente traduce una consulta, que se define en el cliente como instancia de la clase DataServiceQuery, en un mensaje de solicitud HTTP GET. La biblioteca recibe el mensaje de respuesta y lo traduce en instancias de las clases del servicio de datos de cliente. El seguimiento de estas clases lo realiza la clase DataServiceContext a la que pertenece la clase DataServiceQuery.

Consultas del servicio de datos

MCT: Luis Dueñas

Pag 68 de 128

WCF Data Service La clase DataServiceQuery genérica representa una consulta que devuelve una colección de cero o más instancias de tipo de entidad. Una consulta del servicio de datos siempre pertenece al contexto de un servicio de datos existente. Este contexto mantiene la información necesaria del URI del servicio y la de los metadatos para crear y ejecutar la consulta. Cuando se usa el cuadro de diálogo Agregar referencia de servicio para agregar un servicio de datos a una aplicación cliente basada en .NET Framework, se crea una clase de contendor de entidades que hereda de la clase DataServiceContext. Esta clase incluye propiedades que devuelven instancias de DataServiceQuery con tipo. Existe una propiedad para cada conjunto de entidades que expone el servicio de datos. Estas propiedades facilitan la creación de una instancia de DataServiceQuery con tipo. Las consultas se ejecutan en los escenarios siguientes: Cuando se enumeran los resultados implícitamente, por ejemplo:

o

Cuando se enumera una propiedad de la clase DataServiceContext que representa un conjunto de entidades, como ocurre en un bucle foreach (C#) o For Each (Visual Basic).

o

Cuando se asigna la consulta a una colección List.

Cuando se llama explícitamente a los métodos Execute o BeginExecute. Cuando se llama a un operador de ejecución de consultas LINQ, como First o Single. Cuando se ejecuta la siguiente consulta, devuelve todas las entidades Customers del servicio de datos de Northwind: ' Define a new query for Customers. Dim query As DataServiceQuery(Of Customer) = context.Customers

El cliente de WCF Data Services admite consultas para objetos enlazados en tiempo de ejecución, como cuando usa el tipo dynamic en C#. Sin embargo, por razones de rendimiento siempre debe redactar consultas fuertemente tipadas en el servicio de datos. El cliente no admite los objetos de tipo y dinámicos de la clase Tuple.

Consultas LINQ Puesto que la clase DataServiceQuery implementa la interfaz IQueryable definida por LINQ, la biblioteca de cliente de WCF Data Services puede transformar consultas LINQ en datos del conjunto de entidades en un URI que representa una expresión de consulta evaluada en un recurso del servicio de datos. En el siguiente ejemplo hay una consulta LINQ que es equivalente a la clase DataServiceQuery anterior que devuelve

Orders con un costo de flete de más de 30 dólares y ordena los resultados por el costo del flete: Dim selectedOrders = From o In context.Orders _ Where (o.Freight > 30) _ Order By 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:

http://localhost:12345/Northwind.svc/Orders?Orderby=ShippedDate&?filter=Freight gt 30 Nota: 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 REST (Representational State Transfer) usada por 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.

Agregar opciones de consulta Las consultas del servicio de datos admiten todas las opciones de consulta que proporciona WCF Data Services . Llame al método AddQueryOption para anexar las opciones de consulta a una instancia de la clase DataServiceQuery. El método AddQueryOption devuelve una nueva instancia de la clase DataServiceQuery que es equivalente a la consulta original pero con un conjunto de opciones de consulta

MCT: Luis Dueñas

Pag 69 de 128

WCF Data Service nuevo. Cuando se ejecuta la siguiente consulta, devuelve el valor Orders filtrado por el valor Freight y ordenado por OrderID, en orden descendente: ' Define a query for orders with a Freight value greater than 30 ' and that is ordered by the ship date, descending. Dim selectedOrders As DataServiceQuery(Of Order) = context.Orders _ .AddQueryOption("$filter", "Freight gt 30") _ .AddQueryOption("$orderby", "OrderID desc")

Puede usar la opción de consulta $orderby tanto para ordenar como para filtrar una consulta basada en una sola propiedad, como en el siguiente ejemplo que filtra y ordena los objetos Orders devueltos basados en el valor de la propiedad Freight: ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) ' Define a query for orders with a Freight value greater than 30 ' that also orders the result by the Freight value, descending. Dim selectedOrders As DataServiceQuery(Of Order) = _ context.Orders.AddQueryOption("$orderby", "Freight gt 30 desc") Try ' Enumerate over the results of the query. For Each order As Order In selectedOrders Console.WriteLine("Order ID: {0} - Freight: {1}", _ order.OrderID, order.Freight) Next Catch ex As DataServiceQueryException Throw New ApplicationException( _ "An error occurred during query execution.", ex) End Try

Puede llamar al método AddQueryOption consecutivamente para construir expresiones de consulta complejas. Las opciones de consulta le proporcionan otra forma de expresar los componentes estáticos de una consulta LINQ. 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. Se recomienda usar el método Select de LINQ para que el cliente genere la opción de consulta $select en el URI de solicitud.

Ejecución de cliente frente a servidor El cliente ejecuta una consulta en dos partes. Siempre que sea posible, las expresiones 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. Considere la siguiente consulta LINQ: Dim basePrice As Integer = 100 Dim discount As Decimal = Convert.ToDecimal(0.1) ' Define a query that returns products based on a ' calculation that is determined on the client. Dim productsQuery = From p In context.Products Where p.UnitPrice > (basePrice - (basePrice * discount)) AndAlso p.ProductName.Contains("bike") Select p

MCT: Luis Dueñas

Pag 70 de 128

WCF Data Service En este ejemplo, la expresión (basePrice – (basePrice * discount)) se evalúa en el cliente. Por ello, el URI de la consulta real http://localhost:12345/northwind.svc/Products()?$filter=(UnitPrice gt 90.00M) and

substringof('bike',ProductName) que se envía al servicio de datos contiene el valor decimal ya calculado de 90 en la cláusula de filtro. El resto de las partes de la expresión de filtrado, incluida la expresión de subcadena, las evalúa el servicio de datos. Las expresiones que se evalúan en el cliente siguen la semántica de Common Language Runtime (CLR), mientras que las expresiones enviadas al servicio de datos confían en la implementación del servicio de datos del protocolo OData . Debe tener en cuenta los escenarios en los que esta evaluación independiente pueda causar resultados inesperados, como cuando tanto el cliente como el servidor realicen evaluaciones basadas en la hora en distintas zonas horarias.

Respuestas de consulta Cuando se ejecuta, el objeto DataServiceQuery devuelve una interfaz IEnumerable del tipo de entidad solicitado. Este resultado de consulta se puede convertir en un objeto QueryOperationResponse, como en el siguiente ejemplo: ' Execute the query for all customers and get the response object. Dim response As QueryOperationResponse(Of Customer) = _ CType(query.Execute(), QueryOperationResponse(Of Customer))

Las instancias de tipo de entidad que representan entidades del servicio de datos se crean en el cliente mediante un proceso denominado materialización de objetos. El objeto QueryOperationResponse implementa IEnumerable para proporcionar acceso a los resultados de la consulta. QueryOperationResponse también tiene los siguientes miembros que le permiten tener acceso a información adicional sobre el resultado de una consulta: La propiedad Error: obtiene un error provocado por la operación, si se ha producido alguna. La propiedad Headers: contiene una colección de encabezados de respuesta HTTP asociados con la respuesta de la consulta. La propiedad Query: obtiene QueryOperationResponse.

el

objeto

DataServiceQuery

original

generado

por

La propiedad StatusCode: obtiene el código de respuesta HTTP para la respuesta de la consulta. La propiedad TotalCount: obtiene el número total de entidades establecidas cuando se llamó al método IncludeTotalCount en DataServiceQuery. La propiedad GetContinuation: devuelve un objeto DataServiceQueryContinuation que contiene el URI de la siguiente página de resultados. De forma predeterminada, WCF Data Services solo devuelve los datos seleccionados explícitamente por el URI de la consulta. De esta forma, puede cargar explícitamente datos desde el servicio de datos cuando sea necesario. Se envía una solicitud al servicio de datos cada vez que carga datos explícitamente desde el servicio de datos. Los datos que se pueden cargar explícitamente incluyen entidades relacionadas, datos de respuesta paginados y flujos de datos binarios. Nota: Puesto que un servicio de datos puede devolver una respuesta paginada, se recomienda que la aplicación use el modelo de programación para controlar la respuesta paginada de un servicio de datos. La cantidad de datos devueltos por una consulta también se puede reducir especificando que solo se devuelvan algunas propiedades de una entidad en la respuesta.

Obtener un recuento del número total de entidades del conjunto En algunos escenarios, es útil saber el número total de entidades de una entidad establecida y no solamente el número devuelto por la consulta. Llame al método IncludeTotalCount de DataServiceQuery para solicitar que se incluya este recuento total de entidades con los resultados de la consulta. En este caso, la propiedad TotalCount del objeto QueryOperationResponse devuelto devuelve el número total de entidades del conjunto.

MCT: Luis Dueñas

Pag 71 de 128

WCF Data Service Además, puede obtener el recuento total de entidades del conjunto como valor de las estructuras Int32 o Int64 llamando a los métodos Count o LongCount, respectivamente. Cuando se llama a estos métodos, no se devuelve ningún objeto QueryOperationResponse; solo se devuelve el valor de recuento.

5.2.1. Proyecciones de consultas La proyección proporciona un mecanismo en Open Data Protocol (OData) para reducir la cantidad de datos de la fuente devueltos por una consulta mediante la especificación de que solo se devuelven algunas propiedades de una entidad en la respuesta. En este tema se describen la definición de la proyección de una consulta, cuáles son los requisitos para los tipos con entidad y sin ella, la realización de actualizaciones en los resultados proyectados, la creación de tipos proyectados y la enumeración de algunas consideraciones de proyecciones.

Definir una proyección de consultas Puede agregar una cláusula de proyección a una consulta mediante la opción de consulta $select en un URI o mediante la cláusula select (Select en Visual Basic) en una consulta LINQ. Los datos de la entidad devueltos se pueden proyectar en tipos de entidad o tipos sin entidad en el cliente. Los ejemplos de este tema muestran cómo usar la cláusula select en una consulta LINQ. Nota: Se puede producir una pérdida de datos en el servicio de datos cuando se guardan actualizaciones efectuadas en tipos proyectados.

Requisitos para tipos con entidad y sin ella Los tipos de entidad deben tener una o varias propiedades de entidad que constituyen la clave de la entidad. Los tipos de entidad se definen en clientes de una de las siguientes formas: Aplicando las clases DataServiceKeyAttribute o DataServiceEntityAttribute al tipo. Cuando el tipo tiene una propiedad denominada ID. Cuando el tipo tiene una propiedad denominada typeID, donde type es el nombre del tipo. De forma predeterminada, cuando proyecte los resultados de la consulta en un tipo definido en el cliente, las propiedades solicitadas en la proyección deben existir en el tipo de cliente. Sin embargo, cuando especifica un valor de true para la propiedad IgnoreMissingProperties de la clase DataServiceContext, es necesario que se produzcan las propiedades especificadas en la proyección en el tipo de cliente.

Realizar actualizaciones en los resultados proyectados Cuando proyecte los resultados de la consulta en tipos de entidad en el cliente, la clase DataServiceContext puede realizar el seguimiento de esos objetos con las actualizaciones que se van a devolver al servicio de datos cuando se llame al método SaveChanges. No obstante, las actualizaciones efectuadas en datos proyectados en entidades sin tipo en el cliente no se pueden devolver al servicio de datos. Esto se debe a que el servicio de datos no puede actualizar la entidad correcta en el origen de datos sin ninguna clave que identifique la instancia de la entidad. Los tipos sin identidad no se adjunta a la clase DataServiceContext. Cuando una o varias propiedades de un tipo de entidad definido en el servicio de datos no se producen en el tipo de cliente en el que se proyecta la entidad, las inserciones de nuevas entidades no contendrán estas propiedades que faltan. En este caso, las actualizaciones realizadas en entidades existentes tampoco incluirán estas propiedades que faltan. Cuando un valor existe para tal propiedad, la actualización la restablece en el valor predeterminado de la propiedad, de la forma definida en el origen de datos.

Crear tipos proyectados En el siguiente ejemplo se usa una consulta LINQ anónima que proyecta propiedades de relacionadas con la dirección del tipo Customers en un nuevo tipo CustomerAddress, que se define en el cliente y al que se le puede dar el atributo de tipo de entidad: ' Define an anonymous LINQ query that projects the Customers type into ' a CustomerAddress type that contains only address properties.

MCT: Luis Dueñas

Pag 72 de 128

WCF Data Service Dim query = From c In context.Customers _ Where c.Country = "Germany" _ Select New CustomerAddress With { _ .CustomerID = c.CustomerID, _ .Address = c.Address, _ .City = c.City, _ .Region = c.Region, _ .PostalCode = c.PostalCode, _ .Country = c.Country}

En este ejemplo, se usa el modelo de inicializador de objeto para crear una nueva instancia del tipo CustmerAddress en vez de llamar a un constructor. No se admiten constructores cuando se realizan proyecciones en tipos de entidad, pero se pueden usar al realizar proyecciones en tipos sin entidad y anónimos. Puesto que CustomerAddress es un tipo de entidad, los cambios se pueden efectuar en el servicio de datos y devolvérselos a él. Asimismo, los datos de tipo Customer se proyectan en una instancia del tipo de entidad CustomerAddress en vez de en un tipo anónimo. Se admiten las proyecciones en tipos anónimos, pero los datos son de solo lectura porque los tipos anónimos se tratan como tipos sin entidad. La configuración de la enumeración MergeOption de la clase DataServiceContext se usa para identificar la resolución durante la proyección de la consulta. Es decir, si una instancia del tipo Customer ya existe en la clase DataServiceContext, una instancia de CustomerAddress con la misma identidad seguirá las reglas de resolución de entidades establecidas por la enumeración MergeOption. En la siguiente tabla se describen los comportamientos cuando se proyectan resultados en tipos con entidad y sin ella: Tipo sin Acción Tipo con entidad entidad Crear una nueva instancia proyectada mediante inicializadores, como en el ejemplo siguiente: Dim query = From c In context.Customers _ Where c.Country = "Germany" _ Select New CustomerAddress With { _ .CustomerID = c.CustomerID, _ .Address = c.Address, _

Admitida

Admitida

Se inicia una clase NotSupportedException.

Admitida

Se inicia una clase NotSupportedException.

Admitida

.City = c.City, _ .Region = c.Region, _ .PostalCode = c.PostalCode, _ .Country = c.Country}

Crear una nueva instancia proyectada con constructores, como en el ejemplo siguiente: Dim query = From c In context.Customers _ Where c.Country = "Germany" _ Select New CustomerAddress( _ c.CustomerID, _ c.Address, _ c.City, _ c.Region, _ c.PostalCode, _ c.Country)

Usar una proyección para transformar un valor de propiedad, como en el ejemplo siguiente: Dim query = From c In context.Customers _ Where c.Country = "Germany" _

MCT: Luis Dueñas

Pag 73 de 128

WCF Data Service Select New CustomerAddress With _ {.CustomerID = c.CustomerID, _ .Address = "Full address: " & c.Address & ", " & _ c.City & "," & c.Region & " " & c.PostalCode, _ .City = c.City, _ .Region = c.Region, _ .PostalCode = c.PostalCode, _ .Country = c.Country}

Esta transformación no la admiten los tipos de entidad porque puede confundir y posiblemente sobrescribir los datos en el origen de datos que pertenece a otra entidad.

Consideraciones sobre proyecciones Se aplican las siguiente consideraciones adicionales cuando se define una proyección de consultas. Cuando defina las fuentes personalizadas para el formato Atom, debe asegurarse de que se incluyan todas las propiedades de entidad con asignaciones personalizadas en la proyección. Cuando no se incluya ninguna propiedad de entidad asignada en la proyección, se puede producir la pérdida de datos. Cuando se realicen inserciones en un tipo proyectado que no contenga todas las propiedades de la entidad en el modelo de datos del servicio de datos, las propiedades no incluidas en la proyección en el cliente se establecen en sus valores predeterminados. Cuando se realicen actualizaciones en un tipo proyectado que no contenga todas las propiedades de la entidad en el modelo de datos del servicio de datos, los valores existentes no incluidos en la proyección en el cliente se sobrescribirán con valores predeterminados sin inicializar. Cuando una proyección incluya una propiedad compleja, se debe devolver todo el objeto complejo. Cuando una proyección incluya una propiedad de navegación, los objetos relacionados se cargan implícitamente sin tener que llamar al método Expand. No se admite el método Expand para su uso en una consulta proyectada. Las consultas de proyecciones de consultas en el cliente se traducen para usar la opción de consulta $select en el URI de solicitud. Cuando se ejecute una consulta con proyección en una versión previa de WCF Data Services que no admita la opción de consulta $select, se devuelve un error. Esto también puede ocurrir cuando se establece la propiedad MaxProtocolVersion de la clase DataServiceBehavior para el servicio de datos en el valor de la enumeración V1.

5.2.1.1. Cómo: Proyectar los resultados de una consulta La proyección proporciona un mecanismo para reducir la cantidad de datos devueltos por una consulta mediante la especificación de que solo se devuelven algunas propiedades de una entidad en la respuesta. Puede realizar proyecciones en los resultados de una consulta de WCF Data Services mediante la opción de consulta $select o mediante la cláusula select (Select en Visual Basic) en una consulta LINQ. En el ejemplo de este tema se usa el servicio de datos de ejemplo Northwind y las clases del servicio de datos del cliente generadas automáticamente. Se crean este servicio y las clases de datos del cliente al completar el tutorial rápido del servicio de datos de WCF.

Ejemplo En el siguiente ejemplo se muestra una consulta LINQ que proyecta entidades Customers en un nuevo tipo CustomerAddress, que contiene solo las propiedades específicas de la dirección más la propiedad de identidad. Esta clase CustomerAddress se define en el cliente y recibe el atributo para que la biblioteca de cliente pueda reconocerla como tipo de entidad. ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri)

MCT: Luis Dueñas

Pag 74 de 128

WCF Data Service ' Define an anonymous LINQ query that projects the Customers type into ' a CustomerAddress type that contains only address properties. Dim query = From c In context.Customers _ Where c.Country = "Germany" _ Select New CustomerAddress With { _ .CustomerID = c.CustomerID, _ .Address = c.Address, _ .City = c.City, _ .Region = c.Region, _ .PostalCode = c.PostalCode, _ .Country = c.Country} Try ' Enumerate over the query result, which is executed implicitly. For Each item In query ' Modify the address and mark the object as updated. item.Address += " #101" context.UpdateObject(item) ' Write out the current values. Console.WriteLine("Customer ID: {0} \r\nStreet: {1} " _ & "\r\nCity: {2} \r\nState: {3} \r\nZip Code: {4} \r\nCountry: {5}", _ item.CustomerID, item.Address, item.City, item.Region, _ item.PostalCode, item.Country) Next ' Save changes to the data service. context.SaveChanges() Catch ex As DataServiceQueryException Throw New ApplicationException( _ "An error occurred during query execution.", ex) End Try

En el siguiente ejemplo se muestra una consulta LINQ que proyecta las entidades Customers devueltas en un nuevo tipo CustomerAddressNonEntity, que contiene solo las propiedades específicas de la dirección y no tiene propiedad de identidad. Esta clase CustomerAddressNonEntity se define en el cliente y no recibe el atributo como tipo de entidad. ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) ' Define an anonymous LINQ query that projects the Customers type into ' a CustomerAddress type that contains only address properties. Dim query = From c In context.Customers _ Where c.Country = "Germany" _ Select New CustomerAddressNonEntity With _ {.CompanyName = c.CompanyName, _ .Address = c.Address, _ .City = c.City, _ .Region = c.Region, _ .PostalCode = c.PostalCode, _ .Country = c.Country} Try ' Enumerate over the query result, which is executed implicitly. For Each item In query item.Address += "Street"

MCT: Luis Dueñas

Pag 75 de 128

WCF Data Service Console.WriteLine("Company name: {0} \nStreet: {1} " _ & "\nCity: {2} \nState: {3} \nZip Code: {4} \nCountry: {5}", _ item.CompanyName, item.Address, item.City, item.Region, _ item.PostalCode, item.Country) Next Catch ex As DataServiceQueryException Throw New ApplicationException( _ "An error occurred during query execution.", ex) End Try

En el siguiente ejemplo se muestran las definiciones de los tipos CustomerAddressCustomerAddressNonEntity usados en los ejemplos anteriores. _ Partial Public Class CustomerAddress Private _customerID As String Private _address As String Private _city As String Private _region As String Private _postalCode As String Private _country As String Public Property CustomerID() As String Get Return Me._customerID End Get Set(ByVal value As String) Me._customerID = Value End Set End Property Public Property Address() As String Get Return Me._address End Get Set(ByVal value As String) Me._address = Value End Set End Property Public Property City() As String Get Return Me._city End Get Set(ByVal value As String) Me._city = Value End Set End Property Public Property Region() As String Get Return Me._region End Get Set(ByVal value As String) Me._region = Value End Set End Property

MCT: Luis Dueñas

Pag 76 de 128

WCF Data Service Public Property PostalCode() As String Get Return Me._postalCode End Get Set(ByVal value As String) Me._postalCode = Value End Set End Property Public Property Country() As String Get Return Me._country End Get Set(ByVal value As String) Me._country = value End Set End Property End Class Public Class CustomerAddressNonEntity Private _companyName As String Private _address As String Private _city As String Private _region As String Private _postalCode As String Private _country As String Public Property CompanyName() As String Get Return Me._companyName End Get Set(ByVal value As String) Me._companyName = value End Set End Property Public Property Address() As String Get Return Me._address End Get Set(ByVal value As String) Me._address = Value End Set End Property Public Property City() As String Get Return Me._city End Get Set(ByVal value As String) Me._city = Value End Set End Property Public Property Region() As String Get Return Me._region End Get

MCT: Luis Dueñas

Pag 77 de 128

WCF Data Service Set(ByVal value As String) Me._region = Value End Set End Property Public Property PostalCode() As String Get Return Me._postalCode End Get Set(ByVal value As String) Me._postalCode = Value End Set End Property Public Property Country() As String Get Return Me._country End Get Set(ByVal value As String) Me._country = value End Set End Property End Class

5.2.2. Consideraciones sobre LINQ En este tema se proporciona información sobre la forma en la que se redactan y se ejecutan las consultas LINQ cuando se usa el cliente de WCF Data Services y las limitaciones de uso de LINQ para consultar un servicio de datos que implemente Open Data Protocol (OData) .

Redactar consultas LINQ LINQ permite redactar consultas en una colección de objetos que implemente la interfaz IEnumerable. 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. Puesto que la clase DataServiceQuery implementa la interfaz IQueryable definida por LINQ, WCF Data Services puede redactar una consulta LINQ en las fuentes expuestas por el servicio de datos, que traduce la biblioteca cliente en un URI de solicitud de consulta que se envía al servicio de datos en ejecución. Nota: 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. Se produce una excepción NotSupportedException cuando la consulta no puede asignarse a un URI del servicio de datos de destino. 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

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:

http://localhost:12345/Northwind.svc/Orders?Orderby=ShippedDate&?filter=Freight gt 30 MCT: Luis Dueñas

Pag 78 de 128

WCF Data Service 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)

El cliente de WCF Data Services 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, 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 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 o Single, 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. 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. 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.

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

Usar métodos de consulta LINQ: Dim filteredOrders = context.Orders.Where(Function(o) o.Freight.Value > 0)

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")

Todos los ejemplos anteriores se traducen en el URI de la consulta:

http://localhost:12345/northwind.svc/Orders()?$filter=Freight gt 30M .

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

MCT: Luis Dueñas

Pag 79 de 128

WCF Data Service Select c

Usar métodos de consulta LINQ: Dim sortedCustomers = context.Customers.OrderBy(Function(c) c.CompanyName) _ .ThenByDescending(Function(c) c.PostalCode)

Opción $orderby de la cadena de consulta del URI): Dim sortedCustomers = context.Customers.AddQueryOption("$orderby", "CompanyName, PostalCode desc")

Todos los ejemplos anteriores se traducen en el URI de la consulta:

http://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 }

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 })

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. Se recomienda usar el método Select 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:

"http://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) _

MCT: Luis Dueñas

Pag 80 de 128

WCF Data Service .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) _

Los dos ejemplos anteriores se traducen en el URI de la consulta:

http://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 en la clase DataServiceQuery 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. En los siguientes ejemplos se muestran las formas equivalentes de usar el método Expand 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

Con los métodos de consulta LINQ: Dim ordersQuery = context.Orders.Expand("Order_Details") _ .Where(Function(o) o.CustomerID = "ALFKI")

Los dos ejemplos anteriores se traducen en el URI de la consulta:

http://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 No se admite ningún operador de conjuntos en una clase DataServiceQuery, que incluya lo siguiente: All Any Concat

Operadores de conjuntos

DefaultIfEmpty Distinct Except Intersect Union Zip

Operaciones de ordenación

No se admiten los siguientes operadores de ordenación que requieran la interfaz IComparer en una clase DataServiceQuery: OrderBy OrderByDescending

MCT: Luis Dueñas

Pag 81 de 128

WCF Data Service ThenBy ThenByDescending No se admiten ninguno de los siguientes operadores de proyección y filtrado que acepten un argumento posicional en una clase DataServiceQuery: Join Select Métodos de proyección y filtrado

SelectMany SelectMany SelectMany SelectMany Where No se admite ningún operador de agrupación en una clase DataServiceQuery, que incluya lo siguiente:

Operadores de agrupación

GroupBy GroupJoin Los operadores de agrupación se deben ejecutar en el cliente. No se admite ninguna operación de agregado en una clase DataServiceQuery, que incluya lo siguiente: Aggregate Average Count

Operadores de agregación

LongCount Max Min Sum Las operaciones de agregado se deben ejecutar en el cliente o las debe encapsular una operación de servicio. No se admiten los siguientes operadores de paginación en una clase DataServiceQuery: ElementAt Last LastOrDefault

Operadores de paginación

SkipWhile TakeWhile Nota: Los operadores de paginación que se ejecuten en una secuencia vacía devuelven NULL.

Operadores

MCT: Luis Dueñas

No se admiten los siguientes operadores adicionales en una clase DataServiceQuery:

Pag 82 de 128

WCF Data Service adicionales

1.

Empty

2.

Range

3.

Repeat

4.

ToDictionary

5.

ToLookup

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 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)

Miembro de la estructura DateTime1 Función de OData admitida 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) 1 Las 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 la clase Math Función de OData admitida 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)

Miembro de la clase Expression Función de OData admitida TypeIs 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.

5.2.3. Materialización de objetos Cuando use el cuadro de diálogo Agregar referencia de servicio para utilizar una fuente Open Data Protocol (OData) en una aplicación cliente basada en .NET Framework, se generarán clases equivalentes para cada tipo de entidad en el modelo de datos expuesto por la fuente. Los datos de entidad devueltos

MCT: Luis Dueñas

Pag 83 de 128

WCF Data Service por una consulta se materializan en una instancia de una de estas clases de servicio de datos de cliente generadas. WCF Data Services también le permite definir sus propias clases de servicio de datos de cliente en lugar de usar las clases de datos generadas por herramientas. De esta forma, puede usar sus propias clases de datos, que también se conocen como clases de datos "tipos de objetos CLR antiguos sin formato" (POCO). Al usar estos tipos de clases de datos personalizadas, debe asignar un atributo a la clase de datos con la clase DataServiceKeyAttribute o la clase DataServiceEntityAttribute y asegurarse de que los nombres de tipos en el cliente coincidan con los del modelo de datos del servicio de datos. Después de que la biblioteca recibe el mensaje de respuesta de la consulta, materializa los datos devueltos desde la fuente OData en instancias de las clases del servicio de datos de cliente que tienen el tipo de la consulta. El proceso general para materializar estos objetos es el siguiente: 1.

La biblioteca de cliente lee el tipo serializado desde el elemento entry en la fuente del mensaje de respuesta e intenta crear una nueva instancia del tipo correcto de una de estas formas:

o

Cuando el tipo declarado en la fuente tenga el mismo nombre que el tipo de la clase DataServiceQuery, se crea una nueva instancia de este tipo mediante el constructor vacío.

o

Cuando el tipo declarado en la fuente tenga el mismo tipo que se deriva del tipo de la clase DataServiceQuery, se crea una nueva instancia de este tipo derivado mediante el constructor vacío.

o

Cuando el tipo declarado en la fuente no se pueda hacer coincidir con el tipo de la clase DataServiceQuery ni con ningún tipo derivado, se crea una nueva instancia del tipo consultado mediante el constructor vacío.

o

Cuando se establezca la propiedad ResolveType, se llama al delegado proporcionado para invalidar la asignación de tipos basada en nombre predeterminada y, en su lugar, se crea una nueva instancia del tipo devuelto por el delgado Func. Si este delegado devuelve un valor NULL, en su lugar se crea una nueva instancia del tipo consultado. Quizá sea necesario invalidar la asignación de nombre basada en tipo predeterminada para admitir escenarios de herencia.

2.

La biblioteca de cliente lee el valor de URI desde el elemento id de entry, que es el valor de identidad de la entidad. A menos que se use un valor de la propiedad MergeOption de la enumeración NoTracking, se usa el valor de identidad para realizar el seguimiento del objeto en la clase DataServiceContext. El valor de identidad también se usa para garantizar que solo se cree una única instancia de identidad, aunque se devuelva varias veces una entidad en la respuesta de la consulta.

3.

La biblioteca de cliente lee propiedades desde la entrada de la fuente y establece las propiedades correspondientes en el objeto recién creado. Cuando un objeto con el mismo valor de identidad se produce en la clase DataServiceContext, las propiedades se establecen basándose en la configuración de MergeOption de la clase DataServiceContext. Puede que la respuesta contenga valores de propiedad para los que no se produzca ninguna propiedad correspondiente en el tipo de cliente. En este caso, la acción depende del valor de la propiedad IgnoreMissingProperties de la clase DataServiceContext. Cuando esta propiedad se establece en true, se ignora la propiedad que falta. En caso contrario, se produce un error. Las propiedades se establecen de la siguiente forma:

o

Las propiedades escalares se establecen en el valor correspondiente de la entrada del mensaje de respuesta.

o

Las propiedades complejas se establecen en una nueva instancia de tipo complejo, que se establecen con las propiedades del tipo complejo de la respuesta.

MCT: Luis Dueñas

Pag 84 de 128

WCF Data Service o

Las propiedades de navegación que devuelven una colección de entidades relacionadas se establecen en una instancia nueva o en una instancia existente de la interfaz ICollection, donde T es el tipo de entidad relacionada. Esta colección está vacía a menos que se hayan cargado los objetos relacionados en la clase DataServiceContext. Nota: Cuando las clases de datos del cliente generadas admitan el enlace de datos, las propiedades de navegación devuelven instancias de la clase DataServiceCollection en su lugar.

4.

Se genera el evento ReadingEntity.

5.

La biblioteca de clientes adjunta el objeto a la clase DataServiceContext. El objeto no se adjunta cuando la clase MergeOption es la enumeración NoTracking.

5.2.4. Cómo: Ejecutar consultas en el servicio de datos WCF Data Services le permite consultar un servicio datos desde una aplicación cliente basada en .NET Framework usando las clases generadas del servicio de datos del cliente. Puede ejecutar las consultas usando uno de estos métodos: Ejecutar una consulta LINQ en la instancia con nombre de DataServiceQuery que se obtiene desde la instancia de DataServiceContext generada por la herramienta Add Data Service Reference. Implícitamente, enumerando en la instancia con nombre de DataServiceQuery que se obtiene desde la instancia de DataServiceContext generada por la herramienta Add Data Service Reference. Explícitamente, llamando al método Execute de DataServiceQuery o al método BeginExecute para la ejecución asincrónica. En el ejemplo de este tema se usa el servicio de datos de ejemplo Northwind y las clases del servicio de datos del cliente generadas automáticamente. Se crean este servicio y las clases de datos del cliente al completar el tutorial rápido de WCF Data Services.

Ejemplo En el ejemplo siguiente se muestra cómo definir y ejecutar en el servicio de datos de Northwind una consulta LINQ que devuelve todas las entidades Customers. ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) Try ' Define a LINQ query that returns all customers. Dim allCustomers = From cust In context.Customers Select cust ' Enumerate over the query obtained from the context. For Each customer As Customer In allCustomers Console.WriteLine("Customer Name: {0}", customer.CompanyName) Next Catch ex As DataServiceQueryException Throw New ApplicationException("An error occurred during query execution.", ex) End Try

En el siguiente ejemplo se muestra cómo usar el contexto que genera la herramienta Add Data Service Reference para ejecutar implícitamente en el servicio de datos de Northwind una consulta que devuelve

MCT: Luis Dueñas

Pag 85 de 128

WCF Data Service todas las entidades Customers. El contexto determina automáticamente el URI del conjunto de entidades

Customers solicitado. La consulta se ejecuta implícitamente cuando se produce la enumeración. ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) ' Define a new query for Customers. Dim query As DataServiceQuery(Of Customer) = context.Customers Try ' Enumerate over the query result, which is executed implicitly. For Each customer As Customer In query Console.WriteLine("Customer Name: {0}", customer.CompanyName) Next Catch ex As DataServiceQueryException Throw New ApplicationException("An error occurred during query execution.", ex) End Try

En el ejemplo siguiente se muestra cómo usar DataServiceContext para ejecutar explícitamente en el servicio de datos de Northwind una consulta que devuelve todas las entidades Customers. ' Define a request URI that returns Customers. Dim customersUri = New Uri(svcUri, "Northwind.svc/Customers") ' Create the DataServiceContext using the service URI. Dim context = New DataServiceContext(svcUri) Try ' Enumerate over the query result. For Each customer As Customer In context.Execute(Of Customer)(customersUri) Console.WriteLine("Customer Name: {0}", customer.CompanyName) Next Catch ex As DataServiceQueryException Throw New ApplicationException("An error occurred during query execution.", ex) End Try

5.2.5. Cómo: Agregar opciones de consulta a una consulta de servicio de datos WCF Data Services le permite consultar un servicio datos desde una aplicación cliente basada en .NET Framework usando las clases generadas del servicio de datos del cliente. La forma más fácil de hacerlo es crear una expresión de consulta Language Integrated Query (LINQ) que incluya las opciones de consulta deseadas. Además, puede llamar a una serie de métodos de consulta LINQ para crear una consulta equivalente. Por último, puede usar el método AddQueryOption para agregar las opciones de consulta a una consulta. En cada uno de estos casos, el URI que genera el cliente incluye el conjunto de entidades solicitado con las opciones de consulta seleccionadas aplicadas.

Ejemplo En el ejemplo siguiente se muestra cómo crear una expresión de consulta LINQ que devuelve solo los pedidos con un coste de flete superior a 30 dólares y que ordena los resultados por la fecha de envío en orden descendente. ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) ' Define a query for orders with a Freight value greater than 30 ' and that is ordered by the ship date, descending. Dim selectedOrders = From o In context.Orders Where (o.Freight > 30) Order By o.ShippedDate Descending Select o Try ' Enumerate over the results of the query.

MCT: Luis Dueñas

Pag 86 de 128

WCF Data Service For Each order As Order In selectedOrders Console.WriteLine("Order ID: {0} - Ship Date: {1} - Freight: {2}", order.OrderID, order.ShippedDate, order.Freight) Next Catch ex As DataServiceQueryException Throw New ApplicationException("An error occurred during query execution.", ex) End Try

En el ejemplo siguiente se muestra cómo crear un consulta LINQ, mediante los métodos de consulta LINQ, que es equivalente a la consulta anterior. ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) ' Define a query for orders with a Freight value greater than 30 ' and that is ordered by the ship date, descending. Dim selectedOrders = context.Orders.Where(Function(o) o.Freight.Value > 30).OrderByDescending(Function(o) o.ShippedDate) Try ' Enumerate over the results of the query. For Each order As Order In selectedOrders Console.WriteLine("Order ID: {0} - Ship Date: {1} - Freight: {2}", order.OrderID, order.ShippedDate, order.Freight) Next Catch ex As DataServiceQueryException Throw New ApplicationException("An error occurred during query execution.", ex) End Try

En el siguiente ejemplo se muestra cómo usar el método AddQueryOption para crear una clase DataServiceQuery que es equivalente a los ejemplos anteriores. ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) ' Define a query for orders with a Freight value greater than 30 ' and that is ordered by the ship date, descending. Dim selectedOrders As DataServiceQuery(Of Order) = context.Orders _ .AddQueryOption("$filter", "Freight gt 30") _ .AddQueryOption("$orderby", "OrderID desc") Try ' Enumerate over the results of the query. For Each order As Order In selectedOrders Console.WriteLine("Order ID: {0} - Ship Date: {1} - Freight: {2}", order.OrderID, order.ShippedDate, order.Freight) Next Catch ex As DataServiceQueryException Throw New ApplicationException("An error occurred during query execution.", ex) End Try

En el ejemplo siguiente se muestra cómo usar la opción de consulta $orderby para filtrar y ordenar los objetos Orders devueltos por la propiedad Freight. ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) ' Define a query for orders with a Freight value greater than 30 ' that also orders the result by the Freight value, descending. Dim selectedOrders As DataServiceQuery(Of Order) = context.Orders.AddQueryOption("$orderby", "Freight gt 30 desc") Try ' Enumerate over the results of the query. For Each order As Order In selectedOrders Console.WriteLine("Order ID: {0} - Freight: {1}", order.OrderID, order.Freight) Next

MCT: Luis Dueñas

Pag 87 de 128

WCF Data Service Catch ex As DataServiceQueryException Throw New ApplicationException("An error occurred during query execution.", ex) End Try

5.2.6. Cómo: Determinar el número de entidades devueltas por una consulta Con WCF Data Services , puede determinar el número de entidades que existen en el conjunto de entidades especificado por un URI de consulta. Este número se puede incluir junto con el resultado de la búsqueda o como valor entero.

Ejemplo En este ejemplo se ejecuta una consulta después de llamar al método IncludeTotalCount. La propiedad TotalCount devuelve el número de entidades del conjunto de entidades Customers. ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) ' Define a new query for Customers that includes the total count. Dim query As DataServiceQuery(Of Customer) = context.Customers.IncludeTotalCount() Try ' Execute the query for all customers and get the response object. Dim response As QueryOperationResponse(Of Customer) = _ CType(query.Execute(), QueryOperationResponse(Of Customer)) ' Retrieve the total count from the response. Console.WriteLine("There are a total of {0} customers.", response.TotalCount) ' Enumerate the customers in the response. For Each customer As Customer In response Console.WriteLine("\tCustomer Name: {0}", customer.CompanyName) Next Catch ex As DataServiceQueryException Throw New ApplicationException("An error occurred during query execution.", ex) End Try

En este ejemplo se llama al método Count para devolver únicamente un valor entero que es el número de entidades del conjunto de entidades Customers. ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) ' Define a new query for Customers. Dim query As DataServiceQuery(Of Customer) = context.Customers Try ' Execute the query to just return the value of all customers in the set. Dim totalCount = query.Count() ' Retrieve the total count from the response. Console.WriteLine("There are {0} customers in total.", totalCount) Catch ex As DataServiceQueryException Throw New ApplicationException("An error occurred during query execution.", ex) End Try

5.2.7. Cómo: especificar las credenciales de cliente para una solicitud de servicio de datos De forma predeterminada, la biblioteca cliente no proporciona credenciales cuando se envía una solicitud a un servicio OData . Sin embargo, puede especificar que las credenciales se envíen para autenticar solicitudes

MCT: Luis Dueñas

Pag 88 de 128

WCF Data Service al servicio de datos proporcionando una clase NetworkCredential para la propiedad Credentials de la clase DataServiceContext. En el ejemplo de este tema se muestra cómo proporcionar explícitamente credenciales que el cliente de WCF Data Services use cuando solicite datos del servicio de datos.

Ejemplo El siguiente ejemplo proviene una página de código subyacente de un archivo de lenguaje XAML que es la página principal de la aplicación de Windows Presentation Framework. En este ejemplo se muestra una instancia de LoginWindow para recopilar las credenciales de autenticación del usuario y, a continuación, las usa cuando realiza una solicitud al servicio de datos. Imports NorthwindClient.Northwind Imports System.Data.Services.Client Imports System.Windows.Data Imports System.Net Imports System.Windows Imports System.Security Partial Public Class ClientCredentials Inherits Window ' Create the binding collections and the data service context. Private binding As DataServiceCollection(Of Customer) Private context As NorthwindEntities Private customerAddressViewSource As CollectionViewSource ' Instantiate the service URI and credentials. Dim serviceUri As Uri = New Uri("http://localhost:54321/Northwind.svc/") Private credentials As NetworkCredential = New NetworkCredential() Public Sub Main() InitializeComponent() End Sub Private Sub ClientCredentials_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs) Dim userName = String.Empty Dim domain = String.Empty Dim password = New SecureString() ' Get credentials for authentication. Dim login As New LoginWindow() login.ShowDialog() If login.DialogResult = True _ AndAlso Not login.userNameBox.Text Is String.Empty _ AndAlso login.passwordBox.SecurePassword.Length 0 Then ' Instantiate the context. context = New NorthwindEntities(serviceUri) ' Get the user name and domain from the login. Dim qualifiedUserName As String() = login.userNameBox.Text.Split(New [Char]() {"\"c}) If qualifiedUserName.Length = 2 Then domain = qualifiedUserName(0) userName = qualifiedUserName(1) Else userName = login.userNameBox.Text End If password = login.passwordBox.SecurePassword ' Set the client authentication credentials. context.Credentials = _ New NetworkCredential(userName, password, domain) ' Define an anonymous LINQ query that returns a collection of Customer types.

MCT: Luis Dueñas

Pag 89 de 128

WCF Data Service Dim query = From c In context.Customers Where c.Country = "Germany" Select c Try ' Instantiate the binding collection, which executes the query. binding = New DataServiceCollection(Of Customer)(query) ' Load result pages into the binding collection. While Not binding.Continuation Is Nothing ' Continue to execute the query until all pages are loaded. binding.Load(context.Execute(Of Customer)(binding.Continuation.NextLinkUri)) End While ' Assign the binding collection to the CollectionViewSource. customerAddressViewSource = CType(Me.Resources("customerViewSource"), CollectionViewSource) customerAddressViewSource.Source = binding Catch ex As Exception MessageBox.Show(ex.Message) End Try ElseIf login.DialogResult = False Then MessageBox.Show("Login cancelled.") End If End Sub End Class

El código XAML siguiente define la página principal de la aplicación WPF.











MCT: Luis Dueñas

Pag 90 de 128

WCF Data Service







El siguiente ejemplo proviene de la página de código subyacente de la ventana que se usa para recopilar las credenciales de autenticación del usuario antes de realizar una solicitud al servicio de datos. Imports System.ComponentModel Imports System.Windows Imports System.Security Partial Public Class LoginWindow Inherits Window Public Sub New() InitializeComponent() End Sub Private Sub OKButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles OKButton.Click Me.DialogResult = True e.Handled = True End Sub Private Sub CancelButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles CancelButton.Click Me.DialogResult = False e.Handled = True End Sub Private Sub LoginWindow_Closing(ByVal sender As System.Object, ByVal e As CancelEventArgs) If Me.DialogResult = True AndAlso _ (Me.userNameBox.Text = String.Empty OrElse Me.passwordBox.SecurePassword.Length = 0) Then e.Cancel = True MessageBox.Show("Please enter name and password or click Cancel.") End If End Sub End Class

El código XAML siguiente define el inicio de sesión de la aplicación WPF.



MCT: Luis Dueñas

Pag 91 de 128

WCF Data Service









Seguridad Las siguientes consideraciones de seguridad se aplican al ejemplo de este tema: Para comprobar que funcionan las credenciales proporcionadas en este ejemplo, el servicio de datos de Northwind debe usar un esquema de autenticación que no sea de acceso anónimo. De lo contrario, el sitio web que hospede el servicio de datos no solicitará las credenciales. Las credenciales de usuario solo se deben solicitar durante la ejecución y no se deben almacenar en la memoria caché. Las credenciales se deben almacenar siempre de forma segura. Los datos enviados con la autenticación básica e implícita no se cifran. Por tanto, los datos los puede ver un adversario. Además, se envían credenciales de autenticación básica (nombre de usuario y contraseña) en texto no cifrado y se pueden interceptar.

5.2.8. Cómo: establecer los encabezados en la solicitud de cliente Cuando use la biblioteca cliente de WCF Data Services para acceder a un servicio de datos compatible con Open Data Protocol (OData) , la biblioteca establece automáticamente los encabezados HTTP necesarios en los mensajes de solicitud enviados al servicio de datos. Sin embargo, la biblioteca cliente no sabe establecer los encabezados de mensaje necesarios en ciertos casos, como cuando el servicio de datos necesita cookies o autenticación basada en notificaciones. En estos casos, debe establecer manualmente los encabezados de mensaje del mensaje de solicitud antes de enviarlo. En el ejemplo de este tema se muestra cómo controla el evento SendingRequest para agregar un nuevo encabezado al mensaje de solicitud antes de que se envíe al servicio de datos. En el ejemplo de este tema se usa el servicio de datos de ejemplo Northwind y las clases del servicio de datos de cliente generadas automáticamente. Se crean este servicio y las clases de datos del cliente al completar el tutorial rápido del servicio de datos de WCF. Además puede usar el servicio de datos de ejemplo Northwind que se publica en el sitio web de OData . Este servicio de datos de ejemplo es de solo lectura y si se intentan guardar los cambios, devuelve un error. Los servicios de datos de ejemplo del sitio web de OData permiten la autenticación anónima.

Ejemplo En el siguiente ejemplo se registra un controlador del evento SendingRequest y, a continuación, se ejecuta una consulta en el servicio de datos. Nota: Cuando un servicio de datos necesita que se establezca manualmente el encabezado del mensaje de todas

MCT: Luis Dueñas

Pag 92 de 128

WCF Data Service las solicitudes, plantéese registrar el controlador del evento SendingRequest mediante la invalidación del método parcial OnContextCreated del contenedor de entidades que represente el servicio de datos, que en este caso es NorthwindEntities. ' Create the DataServiceContext using the service URI. Dim context As NorthwindEntities = New NorthwindEntities(svcUri) ' Register to handle the SendingRequest event. ' Note: If this must be done for every request to the data service, consider ' registering for this event by overriding the OnContextCreated partial method in ' the entity container, in this case NorthwindEntities. AddHandler context.SendingRequest, AddressOf OnSendingRequest ' Define a query for orders with a Freight value greater than 30. Dim query = From cust In context.Customers Where cust.Country = "Germany" Select cust Try ' Enumerate to execute the query. For Each cust As Customer In query Console.WriteLine("Name: {0}\nAddress:\n{1}\n{2}, {3}", _ cust.CompanyName, cust.Address, cust.City, cust.Region) Next Catch ex As DataServiceQueryException Throw New ApplicationException( _ "An error occurred during query execution.", ex) End Try

El siguiente método controla el evento SendingRequest y agrega un encabezado de autenticación a la solicitud. Private Shared Sub OnSendingRequest(ByVal sender As Object, ByVal e As SendingRequestEventArgs) ' Add an Authorization header that contains an OAuth WRAP access token to the request. e.RequestHeaders.Add("Authorization", "WRAP access_token=""123456789""") End Sub

5.3. Cargar contenido aplazado De forma predeterminada, WCF Data Services limita el número de datos que devuelve una consulta. Sin embargo, es posible cargar explícitamente datos adicionales, incluidos los datos de la respuesta paginados, las entidades relacionadas y los flujos de datos binarios, del servicio de datos cuando sea necesario. En este tema se describe cómo cargar dicho contenido aplazado en una aplicación.

Entidades relacionadas Cuando se ejecuta una consulta, solo se devuelven las entidades pertenecientes al conjunto de entidades direccionado. Por ejemplo, cuando una consulta al servicio de datos de Northwind devuelve entidades

Customers, no se devuelven de forma predeterminada las entidades relacionadas Orders, aunque haya una relación entre Customers y Orders. Además, cuando está habilitada la paginación en el servicio de datos, debe cargar explícitamente las páginas de datos subsiguientes del servicio. Hay dos maneras de cargar las entidades relacionadas: Carga diligente: puede usar la opción de consulta $expand para solicitar que la consulta devuelva entidades que están relacionadas mediante una asociación con el conjunto de entidades solicitado por la consulta. Utilice el método Expand en DataServiceQuery para agregar la opción $expand a la consulta que se envía al servicio de datos. Puede solicitar varios conjuntos de entidades relacionados separándolos mediante comas, como muestra el ejemplo siguiente. Todas las

MCT: Luis Dueñas

Pag 93 de 128

WCF Data Service entidades solicitadas por la consulta se devuelven en una única respuesta. En el ejemplo siguiente se devuelve Order_Details y Customers junto con el conjunto de entidades Orders: ' Define a query for orders that also returns items and customers. Dim query As DataServiceQuery(Of Order) = context.Orders.Expand("Order_Details,Customer")

WCF Data Services limita a 12 el número de conjuntos de entidades que se pueden incluir en una única consulta utilizando la opción de consulta $expand. Carga explícita: puede llamar al método LoadProperty de la instancia de DataServiceContext para cargar explícitamente las entidades relacionadas. Cada llamada al método LoadProperty crea una solicitud independiente al servicio de datos. En el ejemplo siguiente se carga explícitamente

Order_Details para una entidad Orders: ' Explicitly load the order details for each order. context.LoadProperty(order, "Order_Details")

Al considerar qué opción se debe usar, observe que hay una correlación entre el número de solicitudes al servicio de datos y la cantidad de datos devueltos en una sola respuesta. Use la carga diligente cuando su aplicación requiera objetos asociados y desee evitar la latencia agregada de las solicitudes adicionales para recuperarlos explícitamente. Sin embargo, si hay casos en que la aplicación solo necesita los datos para determinadas instancias de entidades relacionadas, debería plantearse cargar explícitamente esas entidades llamando al método LoadProperty.

Contenido paginado Si la paginación está habilitada en el servicio de datos, la configuración del servicio de datos limita el número de entradas de la fuente que devuelve el servicio de datos. Los límites de página pueden establecerse por separado para cada conjunto de entidades. Si está habilitada la paginación, la última entrada de la fuente contiene un vínculo a la página de datos siguiente. Este vínculo está contenido en un objeto DataServiceQueryContinuation. El URI a la página de datos siguiente se obtiene llamando al método GetContinuation del objeto QueryOperationResponse que se obtiene cuando se ejecuta DataServiceQuery. A continuación, el objeto DataServiceQueryContinuation devuelto se usa para cargar la página de resultados siguiente. Debe enumerar los resultados de la consulta antes de llamar al método GetContinuation. Considere la posibilidad de usar un bucle do…while para enumerar el resultado de la consulta primero y, a continuación, comprobar el valor de un vínculo siguiente non-null. Cuando el método GetContinuation devuelve null (Nothing en Visual Basic), no hay ninguna página de resultados adicional para la consulta original. En el ejemplo siguiente se muestra un bucle do…while que carga los datos del cliente paginados del servicio de datos de ejemplo Northwind. ' With a paged response from the service, use a do...while loop ' to enumerate the results before getting the next link. Do ' Write the page number. Console.WriteLine("Page {0}:", pageCount + 1) ' If nextLink is not null, then there is a new page to load. If token IsNot Nothing Then ' Load the new page from the next link URI. response = CType(context.Execute(Of Customer)(token), QueryOperationResponse(Of Customer)) End If ' Enumerate the customers in the response. For Each customer As Customer In response Console.WriteLine("\tCustomer Name: {0}", customer.CompanyName) Next ' Get the next link, and continue while there is a next link. token = response.GetContinuation() Loop While token IsNot Nothing

MCT: Luis Dueñas

Pag 94 de 128

WCF Data Service Cuando una consulta solicita que se devuelvan las entidades relacionadas en una respuesta única junto con el conjunto de entidades solicitado, los límites de paginación pueden afectar a las fuentes anidadas que están incluidas alineadas con la respuesta. Por ejemplo, cuando se establece un límite de paginación en el servicio de datos de ejemplo Northwind para el conjunto de entidades Customers, también puede establecerse un límite de paginación independiente para el conjunto de entidades Orders relacionado, como en el ejemplo siguiente del archivo Northwind.svc.cs que define el servicio de datos de ejemplo Northwind. ' Set page size defaults for the data service. config.SetEntitySetPageSize("Orders", 20) config.SetEntitySetPageSize("Order_Details", 50) config.SetEntitySetPageSize("Products", 50) ' Paging requires v2 of the OData protocol. config.DataServiceBehavior.MaxProtocolVersion = System.Data.Services.Common.DataServiceProtocolVersion.V2

En este caso, debe implementar la paginación para las dos fuentes de entidades, la de nivel superior,

Customers, y las anidadas, Orders. En el ejemplo siguiente se muestra el bucle while que se usa para cargar las páginas de las entidades Orders relacionadas con una entidad Customers seleccionada. While nextOrdersLink IsNot Nothing For Each o As Order In c.Orders ' Print out the orders. Console.WriteLine("\t\tOrderID: {0} - Freight: ${1}", o.OrderID, o.Freight) Next ' Load the next page of Orders. Dim ordersResponse = context.LoadProperty(c, "Orders", nextOrdersLink) nextOrdersLink = ordersResponse.GetContinuation() End While

Flujos de datos binarios WCF Data Services le permite tener acceso a datos de objetos binarios grandes (BLOB) en forma de flujo de datos. La transmisión por secuencias aplaza la carga de datos binarios hasta que se necesita, y el cliente puede procesar estos datos más eficazmente. Para aprovecharse de esta funcionalidad, el servicio de datos debe implementar el proveedor IDataServiceStreamProvider. Cuando está habilitada la transmisión por secuencias, los tipos de entidad se devuelven sin los datos binarios relacionados. En este caso, debe usar el método GetReadStream de la clase DataServiceContext para tener acceso al flujo de datos para los datos binarios del servicio. De igual forma, use el método SetSaveStream para agregar o cambiar los datos binarios de una entidad como un flujo.

5.3.1. Cómo: Cargar entidades relacionadas Cuando necesite cargar entidades asociadas en WCF Data Services , puede usar el método LoadProperty de la clase DataServiceContext. También puede usar el método Expand de DataServiceQuery para requerir que las entidades relacionadas se carguen rápidamente en la misma respuesta a la consulta.

Ejemplo En el ejemplo siguiente se muestra cómo cargar explícitamente la entidad Customer relacionada con cada instancia de Orders devuelta. ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) Try ' Enumerate over the top 10 orders obtained from the context. For Each order As Order In context.Orders.Take(10) ' Explicitly load the customer for each order.

MCT: Luis Dueñas

Pag 95 de 128

WCF Data Service context.LoadProperty(order, "Customer") ' Write out customer and order information. Console.WriteLine("Customer: {0} - Order ID: {1}", order.Customer.CompanyName, order.OrderID) Next Catch ex As DataServiceQueryException Throw New ApplicationException("An error occurred during query execution.", ex) End Try

En el ejemplo siguiente se muestra cómo usar el método Expand para devolver las entidades Order Details pertenecientes a las entidades Orders devueltas por la consulta. ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) ' Define a query for orders that also returns items and customers. Dim query As DataServiceQuery(Of Order) = context.Orders.Expand("Order_Details,Customer") Try ' Enumerate over the first 10 results of the query. For Each order As Order In query.Take(10) Console.WriteLine("Customer: {0}", order.Customer.CompanyName) Console.WriteLine("Order ID: {0}", order.OrderID) For Each item As Order_Detail In order.Order_Details Console.WriteLine("\tProduct: {0} - Quantity: {1}", item.ProductID, item.Quantity) Next Next Catch ex As DataServiceQueryException Throw New ApplicationException("An error occurred during query execution.", ex) End Try

5.3.2. Cómo: Cargar resultados paginados WCF Data Services permite que el servicio de datos limite el número de entidades que se devuelven en una fuente de respuesta única. Cuando esto sucede, la última entrada de la fuente contiene un vínculo a la página siguiente de datos. El URI a la página de datos siguiente se obtiene llamando al método GetContinuation del objeto QueryOperationResponse, que se obtiene cuando se ejecuta DataServiceQuery. A continuación, el URI representado por este objeto se utiliza para cargar la página siguiente de resultados.

Ejemplo En este ejemplo se usa un bucle do…while para cargar las entidades Customers desde los resultados paginados del servicio de datos. ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) Dim token As DataServiceQueryContinuation(Of Customer) = Nothing Dim pageCount = 0 Try ' Execute the query for all customers and get the response object. Dim response As QueryOperationResponse(Of Customer) = _ CType(context.Customers.Execute(), QueryOperationResponse(Of Customer)) ' With a paged response from the service, use a do...while loop ' to enumerate the results before getting the next link. Do ' Write the page number. Console.WriteLine("Page {0}:", pageCount + 1) ' If nextLink is not null, then there is a new page to load.

MCT: Luis Dueñas

Pag 96 de 128

WCF Data Service If token IsNot Nothing Then ' Load the new page from the next link URI. response = CType(context.Execute(Of Customer)(token), QueryOperationResponse(Of Customer)) End If ' Enumerate the customers in the response. For Each customer As Customer In response Console.WriteLine("\tCustomer Name: {0}", customer.CompanyName) Next ' Get the next link, and continue while there is a next link. token = response.GetContinuation() Loop While token IsNot Nothing Catch ex As DataServiceQueryException Throw New ApplicationException("An error occurred during query execution.", ex) End Try

En este ejemplo se devuelve las entidades Orders relacionadas con cada entidad Customers y se utiliza un bucle do…while para cargar páginas de entidades Customers y un bucle while anidado para cargar páginas de entidades Orders relacionadas del servicio de datos. ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) Dim nextLink As DataServiceQueryContinuation(Of Customer) = Nothing Dim pageCount = 0 Dim innerPageCount = 0 Try ' Execute the query for all customers and related orders, ' and get the response object. Dim response = _ CType(context.Customers.AddQueryOption("$expand", "Orders") _ .Execute(), QueryOperationResponse(Of Customer)) ' With a paged response from the service, use a do...while loop ' to enumerate the results before getting the next link. Do ' Write the page number. Console.WriteLine("Customers Page {0}:", ++pageCount) ' If nextLink is not null, then there is a new page to load. If nextLink IsNot Nothing Then ' Load the new page from the next link URI. response = CType(context.Execute(Of Customer)(nextLink), QueryOperationResponse(Of Customer)) End If ' Enumerate the customers in the response. For Each c As Customer In response Console.WriteLine("\tCustomer Name: {0}", c.CompanyName) Console.WriteLine("\tOrders Page {0}:", innerPageCount + 1) ' Get the next link for the collection of related Orders. Dim nextOrdersLink As DataServiceQueryContinuation(Of Order) = _ response.GetContinuation(c.Orders) While nextOrdersLink IsNot Nothing For Each o As Order In c.Orders ' Print out the orders. Console.WriteLine("\t\tOrderID: {0} - Freight: ${1}", o.OrderID, o.Freight) Next

MCT: Luis Dueñas

Pag 97 de 128

WCF Data Service ' Load the next page of Orders. Dim ordersResponse = context.LoadProperty(c, "Orders", nextOrdersLink) nextOrdersLink = ordersResponse.GetContinuation() End While Next ' Get the next link, and continue while there is a next link. nextLink = response.GetContinuation() Loop While nextLink IsNot Nothing Catch ex As DataServiceQueryException Throw New ApplicationException("An error occurred during query execution.", ex) End Try

5.4. Actualizar el servicio de datos Cuando se usa la biblioteca de cliente de WCF Data Services para usar una fuente de Open Data Protocol (OData) , la biblioteca traduce las entradas de la fuente en instancias de clases del servicio de datos del cliente. Se realiza el seguimiento de estas clases del servicio de datos mediante el uso de la clase DataServiceContext a la que pertenece la clase DataServiceQuery. El cliente realiza el seguimiento de los cambios en las entidades que se notifican utilizando métodos de la clase DataServiceContext. Estos métodos permiten al cliente realizar el seguimiento de las entidades agregadas y eliminadas y también los cambios que se realizan en los valores de propiedad o en las relaciones entre instancias de entidad. Estos cambios se devuelven al servicio de datos como operaciones basadas en REST al llamar al método SaveChanges. Nota: Al utilizar una instancia de DataServiceCollection para enlazar los datos a los controles, los cambios realizados en los datos del control enlazado se notifican automáticamente a DataServiceContext.

Agregar, modificar y cambiar entidades Cuando se usa el cuadro de diálogo Agregar referencia de servicio de Visual Studio para agregar una referencia a una fuente de OData , cada una de las clases del servicio de datos de cliente resultante tiene un método estático Create que toma un parámetro para cada propiedad de entidad que no admite valores NULL. Puede usar este método para crear instancias de clases de tipos de entidad, como en el ejemplo siguiente: ' Create the new product. Dim newProduct = Product.CreateProduct(0, "White Tea - loose", False)

Para agregar una instancia de entidad, llame al método AddTo adecuado en la clase DataServiceContext generada por el cuadro de diálogo Agregar referencia de servicio, como se muestra en el ejemplo siguiente: ' Add the new product to the Products entity set. context.AddToProducts(newProduct)

Esto agrega el objeto al contexto y al conjunto de entidades correcto. También puede llamar al método AddObject, pero, en ese caso, debe proporcionar el nombre del conjunto de entidades. Si la entidad agregada tiene una o varias relaciones con otras entidades, puede usar el método AddRelatedObject o bien uno de los métodos anteriores y también definir esos vínculos explícitamente. Estas operaciones se explican más adelante en este tema. Para modificar una instancia de una entidad existente, primero ejecute una consulta para esa entidad, efectúe los cambios deseados en sus propiedades y, a continuación, llame al método UpdateObject de la instancia de DataServiceContext para indicar a la biblioteca cliente que debe enviar una actualización para ese objeto, como se muestra en el ejemplo siguiente: ' Mark the customer as updated. context.UpdateObject(customerToChange)

MCT: Luis Dueñas

Pag 98 de 128

WCF Data Service Para eliminar una instancia de una entidad, llame al método DeleteObject de la instancia de la clase DataServiceContext, como se muestra en el ejemplo siguiente: ' Mark the product for deletion. context.DeleteObject(deletedProduct)

Adjuntar entidades La biblioteca de clientes le permite guardar las actualizaciones que realizó en una entidad sin ejecutar primero ninguna consulta para cargar la entidad en la instancia de la clase DataServiceContext. Use el método AttachTo para adjuntar un objeto existente a un conjunto de entidades concreto de la instancia de DataServiceContext. Después puede modificar el objeto y guardar los cambios en el servicio de datos. En el ejemplo siguiente, se adjunta al contexto un objeto de cliente que se ha cambiado y, a continuación, se llama a UpdateObject para marcar el objeto adjunto como Modified antes de llamar a SaveChanges: ' Attach the existing customer to the context and mark it as updated. context.AttachTo("Customers", customer) context.UpdateObject(customer) ' Send updates to the data service. context.SaveChanges()

Al adjuntar objetos deben tenerse en cuenta las consideraciones siguientes: Un objeto se adjunta con el estado Unchanged. Cuando se adjunta un objeto, no se adjuntan los objetos relacionados con el objeto adjunto. No se puede adjuntar un objeto si el contexto ya está realizando el seguimiento de la entidad. Se usa la sobrecarga del método AttachTo que toma un parámetro etag cuando se adjunta un objeto entidad que se recibió junto con un valor de eTag. Este valor de eTag se usa después para comprobar la simultaneidad cuando se guardan los cambios realizados en el objeto adjunto.

Crear y modificar vínculos de relación Cuando se agrega una nueva entidad usando el método AddObject o el método AddTo adecuado de la clase DataServiceContext generada por el cuadro de diálogo Agregar referencia de servicio, no se define automáticamente ninguna relación entre la nueva entidad y las entidades relacionadas. Puede crear y cambiar las relaciones entre las instancias de las entidades y hacer que la biblioteca de cliente refleje esos cambios en el servicio de datos. Las relaciones entre las entidades se definen como asociaciones en el modelo y la clase DataServiceContext realiza el seguimiento de cada relación como objeto de vínculo en el contexto. WCF Data Services proporciona los siguientes métodos en la clase DataServiceContext para crear, modificar y eliminar estos vínculos: Método Descripción Crea un nuevo vínculo entre dos objetos entidad relacionados. Llamar a este método es AddRelatedObject equivalente a llamar a los métodos AddObject y AddLink tanto para crear el nuevo objeto como para definir la relación en un objeto existente. AddLink

Crea un nuevo vínculo entre dos objetos entidad relacionados.

SetLink

Actualiza un vínculo existente entre dos objetos entidad relacionados. El método SetLink también se usa para eliminar vínculos con una cardinalidad de cero o uno a uno (0..1:1) y de uno a uno (1:1). Para ello, puede establecer el objeto relacionado en null.

DeleteLink

Marca para su eliminación un vínculo cuyo seguimiento lo está realizando el contexto cuando se llame al método SaveChanges. Use este método cuando elimine un objeto relacionado o cuando cambie una relación eliminando primero el vínculo a un objeto existente y luego agregando un vínculo al nuevo objeto relacionado.

AttachLink

Notifica al contexto la existencia de un vínculo entre dos objetos entidad. El contexto supone que esta relación ya existe en el servicio de datos y no intenta crear el vínculo cuando se llama al método SaveChanges. Use este método cuando adjunte objetos a un contexto y también necesite adjuntar el vínculo entre los dos. Si está definiendo una

MCT: Luis Dueñas

Pag 99 de 128

WCF Data Service nueva relación, en su lugar debe usar el método AddLink. Deja de realizar el seguimiento del vínculo especificado en el contexto. Este método se usa para eliminar relaciones uno a varios (*:*). En vínculos de relación con una cardinalidad de uno, debe usar el método SetLink en su lugar. En el ejemplo siguiente se muestra la forma de usar el método AddRelatedObject para agregar un nuevo DetachLink

Order_Detail que se relacione con una entidad Orders existente. Dado que el seguimiento del nuevo objeto Order_Details lo realiza la clase DataServiceContext, la relación del objeto Order_Details agregado a la entidad Products existente se define llamando al método AddLink: ' Add the new item with a link to the related order. context.AddRelatedObject(order, "Order_Details", newItem) ' Since the item is now tracked by the context, ' set just the link to the related product. context.AddLink(selectedProduct, "Order_Details", newItem)

Mientras que el método AddLink definen los vínculos que se deben crear en el servicio de datos, para que estos vínculos se reflejen en los objetos que están en el contexto, también debe establecer las propiedades de navegación en los propios objetos. En el ejemplo anterior, debe establecer las propiedades de navegación de la forma siguiente: ' Add the new order detail to the collection, and ' set the reference to the product. order.Order_Details.Add(newItem) newItem.Order = order newItem.Product = selectedProduct

Guardar los cambios El seguimiento de los cambios se realiza en la instancia de la clase DataServiceContext, pero los cambios no se envían al servidor inmediatamente. Una vez que haya efectuado los cambios necesarios de una actividad determinada, llame al método SaveChanges para enviar todos los cambios al servicio de datos. También puede guardar cambios de forma asincrónica utilizando los métodos BeginSaveChanges y EndSaveChanges.

5.4.1. Cómo: Agregar, modificar y eliminar entidades Con las bibliotecas de cliente de WCF Data Services , puede crear, actualizar y eliminar los datos de entidad de un servicio de datos realizando las acciones equivalentes en los objetos en la clase DataServiceContext.

Ejemplo En el siguiente ejemplo se crea una nueva instancia de objeto y, a continuación, se llama al método AddObject de la clase DataServiceContext para crear el elemento en el contexto. Cuando se llama al método SaveChanges se envía un mensaje HTTP POST al servicio de datos. ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) ' Create the new product. Dim newProduct = Product.CreateProduct(0, "White Tea - loose", False) ' Set property values. newProduct.QuantityPerUnit = "120gm bags" newProduct.ReorderLevel = 5 newProduct.UnitPrice = 5.2D Try ' Add the new product to the Products entity set. context.AddToProducts(newProduct) ' Send the insert to the data service. context.SaveChanges()

MCT: Luis Dueñas

Pag 100 de 128

WCF Data Service Console.WriteLine("New product added with ID {0}.", newProduct.ProductID) Catch ex As DataServiceRequestException Throw New ApplicationException("An error occurred when saving changes.", ex)

En el siguiente ejemplo se recupera y modifica un objeto existente y, a continuación, se llama al método UpdateObject en DataServiceContext para marcar el elemento en el contexto cuando se actualice. Cuando se llama al método SaveChanges se envía un mensaje HTTP MERGE al servicio de datos. Dim customerId = "ALFKI" ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) ' Get a customer to modify using the supplied ID. Dim customerToChange = (From customer In context.Customers _ Where customer.CustomerID = customerId Select customer).Single() ' Change some property values. customerToChange.CompanyName = "Alfreds Futterkiste" customerToChange.ContactName = "Maria Anders" customerToChange.ContactTitle = "Sales Representative" Try ' Mark the customer as updated. context.UpdateObject(customerToChange) ' Send the update to the data service. context.SaveChanges() Catch ex As DataServiceRequestException Throw New ApplicationException("An error occurred when saving changes.", ex) End Try

En el siguiente ejemplo se llama al método DeleteObject en DataServiceContext para marcar el elemento en el contexto cuando se elimine. Cuando se llama al método SaveChanges se envía un mensaje HTTP DELETE al servicio de datos. ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) Try ' Get the product to delete, by product ID. Dim deletedProduct = (From product In context.Products _ Where product.ProductID = productID _ Select product).Single() ' Mark the product for deletion. context.DeleteObject(deletedProduct) ' Send the delete to the data service. context.SaveChanges() ' Handle the error that occurs when the delete operation fails, ' which can happen when there are entities with existing ' relationships to the product being deleted. Catch ex As DataServiceRequestException Throw New ApplicationException("An error occurred when saving changes.", ex) End Try

En el ejemplo siguiente se crea una instancia de objeto nueva y, a continuación, se llama al método AddRelatedObject en DataServiceContext para crear el elemento en el contexto junto con el vínculo al pedido relacionado. Cuando se llama al método SaveChanges se envía un mensaje HTTP POST al servicio de datos. Dim productId = 25 Dim customerId = "ALFKI"

MCT: Luis Dueñas

Pag 101 de 128

WCF Data Service Dim newItem As Order_Detail = Nothing ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) Try ' Get the specific product. Dim selectedProduct = (From product In context.Products Where product.ProductID = productId Select product).Single() ' Get the specific customer. Dim cust = (From customer In context.Customers.Expand("Orders") _ Where customer.CustomerID = customerId Select customer).Single() ' Get the first order. Dim order = cust.Orders.FirstOrDefault() ' Create a new order detail for the specific product. newItem = Order_Detail.CreateOrder_Detail(order.OrderID, selectedProduct.ProductID, 10, 5, 0) ' Add the new item with a link to the related order. context.AddRelatedObject(order, "Order_Details", newItem) ' Since the item is now tracked by the context, set just the link to the related product. context.AddLink(selectedProduct, "Order_Details", newItem) ' Add the new order detail to the collection, and set the reference to the product. order.Order_Details.Add(newItem) newItem.Order = order newItem.Product = selectedProduct ' Send the inserts to the data service. context.SaveChanges() Catch ex As DataServiceQueryException Throw New ApplicationException("An error occurred when saving changes.", ex) ' Handle any errors that may occur during insert, such as a constraint violation. Catch ex As DataServiceRequestException Throw New ApplicationException("An error occurred when saving changes.", ex)

5.4.2. Cómo: Definir las relaciones de entidad Al agregar una nueva entidad en WCF Data Services , las relaciones entre la nueva entidad y las entidades relacionadas no se definen automáticamente. Puede crear y cambiar las relaciones entre las instancias de las entidades y hacer que la biblioteca de cliente refleje esos cambios en el servicio de datos.

Ejemplo En el ejemplo siguiente se crea una instancia de objeto nueva y, a continuación, se llama al método AddRelatedObject de la clase DataServiceContext para crear el elemento en el contexto junto con el vínculo al pedido relacionado. Se envía un mensaje POST de HTTP al servicio de datos cuando se llama al método SaveChanges. Dim productId = 25 Dim customerId = "ALFKI" Dim newItem As Order_Detail = Nothing ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) Try ' Get the specific product. Dim selectedProduct = (From product In context.Products _ Where product.ProductID = productId _ Select product).Single() ' Get the specific customer. Dim cust = (From customer In context.Customers.Expand("Orders") _

MCT: Luis Dueñas

Pag 102 de 128

WCF Data Service Where customer.CustomerID = customerId Select customer).Single() ' Get the first order. Dim order = cust.Orders.FirstOrDefault() ' Create a new order detail for the specific product. newItem = Order_Detail.CreateOrder_Detail( _ order.OrderID, selectedProduct.ProductID, 10, 5, 0) ' Add the new item with a link to the related order. context.AddRelatedObject(order, "Order_Details", newItem) ' Since the item is now tracked by the context, set just the link to the related product. context.AddLink(selectedProduct, "Order_Details", newItem) ' Add the new order detail to the collection, and set the reference to the product. order.Order_Details.Add(newItem) newItem.Order = order newItem.Product = selectedProduct ' Send the inserts to the data service. context.SaveChanges() Catch ex As DataServiceQueryException Throw New ApplicationException("An error occurred when saving changes.", ex) ' Handle any errors that may occur during insert, such as a constraint violation. Catch ex As DataServiceRequestException Throw New ApplicationException("An error occurred when saving changes.", ex)

En el ejemplo siguiente se muestra cómo utilizar el método AddObject para agregar un objeto

Order_Details a un objeto Orders relacionado con una referencia a un objeto Products concreto. Los métodos AddLink y SetLink definen las relaciones. En este ejemplo, las propiedades de navegación del objeto Order_Details también se establecen explícitamente. Dim productId = 25 Dim customerId = "ALFKI" Dim newItem As Order_Detail = Nothing ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) Try ' Get the specific product. Dim selectedProduct = (From product In context.Products Where product.ProductID = productId Select product).Single() ' Get the specific customer. Dim cust = (From customer In context.Customers.Expand("Orders") _ Where customer.CustomerID = customerId Select customer).Single() ' Get the first order. Dim order = cust.Orders.FirstOrDefault() ' Create a new order detail for the specific product. newItem = Order_Detail.CreateOrder_Detail(order.OrderID, selectedProduct.ProductID, 10, 5, 0) ' Add the new order detail to the context. context.AddToOrder_Details(newItem) ' Add links for the one-to-many relationships. context.AddLink(order, "Order_Details", newItem) context.AddLink(selectedProduct, "Order_Details", newItem) ' Add the new order detail to the collection, and set the reference to the product. order.Order_Details.Add(newItem) newItem.Product = selectedProduct ' Send the inserts to the data service. context.SaveChanges() Catch ex As DataServiceQueryException

MCT: Luis Dueñas

Pag 103 de 128

WCF Data Service Throw New ApplicationException("An error occurred when saving changes.", ex) ' Handle any errors that may occur during insert, such as a constraint violation. Catch ex As DataServiceRequestException Throw New ApplicationException("An error occurred when saving changes.", ex)

5.4.3. Cómo: Adjuntar una entidad existente a DataServiceContext Cuando una entidad ya existe en un servicio de datos, la biblioteca de cliente de WCF Data Services le permite adjuntar un objeto que representa directamente la entidad en la clase DataServiceContext sin ejecutar una consulta primero.

Ejemplo En el ejemplo siguiente se muestra cómo crear un objeto Customer existente que contiene cambios que se van a guardar en el servicio de datos. Se adjunta el objeto al contexto y se llama al método UpdateObject para marcar el objeto adjunto como Modified antes de llamar al método SaveChanges. ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) ' Define an existing customer to attach, including the key. Dim customer As Customer = customer.CreateCustomer("ALFKI", "Alfreds Futterkiste") ' Set current property values. customer.Address = "Obere Str. 57" customer.City = "Berlin" customer.PostalCode = "12209" customer.Country = "Germany" ' Set property values to update. customer.ContactName = "Peter Franken" customer.ContactTitle = "Marketing Manager" customer.Phone = "089-0877310" customer.Fax = "089-0877451" Try ' Attach the existing customer to the context and mark it as updated. context.AttachTo("Customers", customer) context.UpdateObject(customer) ' Send updates to the data service. context.SaveChanges() Catch ex As DataServiceClientException Throw New ApplicationException("An error occurred when saving changes.", ex) End Try

MCT: Luis Dueñas

Pag 104 de 128

WCF Data Service

5.5. Operaciones asincrónicas Las aplicaciones web deben tolerar una latencia superior entre cliente y servidor que las aplicaciones que se ejecutan en redes internas. Para optimizar el rendimiento y la experiencia del usuario de la aplicación, se recomienda usar los métodos asincrónicos de las clases DataServiceContext y DataServiceQuery cuando se tenga acceso a los servidores de WCF Data Services en la web. Aunque los servidores de WCF Data Services procesen solicitudes HTTP de forma sincrónica, algunos métodos de las bibliotecas de cliente de WCF Data Services son sincrónicas y esperan hasta que se complete el intercambio completo de solicitudes y respuestas antes de continuar con la ejecución. Los métodos asincrónicos de las bibliotecas de cliente de WCF Data Services no esperan a que se complete este intercambio y pueden permitir que la aplicación mantenga una interfaz de usuario que siga respondiendo mientras tanto. Puede realizar operaciones asincrónicas usando un par de métodos de las clases DataServiceQuery y DataServiceContext cuyos nombres comienzan por Begin y End, respectivamente. Los métodos Begin registran un delegado al que llama el servicio cuando finaliza la operación. Se debería llamar a los métodos End en el delegado registrado para controlar la devolución de llamada desde las operaciones completadas. Cuando llame al método End para completar una operación asincrónica, debe hacer lo mismo desde la misma instancia de DataServiceQuery o DataServiceContext que usó para comenzar la operación. Cada método Begin toma un parámetro state que puede pasar un objeto de estado a la devolución de llamada. Este objeto de estado se recupera desde la interfaz IAsyncResult que se proporciona con la devolución de llamada y se usa para llamar al método End correspondiente para completar la operación asincrónica. Por ejemplo, cuando se proporciona la instancia de DataServiceQuery como parámetro state al llamar al método BeginExecute de la instancia, IAsyncResult devuelve la misma instancia de DataServiceQuery. A continuación, esta instancia de DataServiceQuery se usa para llamar al método EndExecute para completar la operación de consulta. Nota: Las bibliotecas de cliente que se proporcionan en .NET Framework para Silverlight solo admiten operaciones asincrónicas. Las bibliotecas de cliente de .NET Framework admiten las siguientes operaciones asincrónicas: Operación

Métodos BeginExecute

Ejecutar una instancia de DataServiceQuery.

EndExecute BeginExecute

Ejecutar una consulta desde la instancia de DataServiceContext.

EndExecute BeginExecuteBatch

Ejecutar una consulta por lotes desde la instancia de DataServiceContext.

EndExecuteBatch BeginLoadProperty

Cargar una entidad relacionada en la instancia de DataServiceContext.

Guardar los cambios efectuados en los objetos en la instancia de DataServiceContext

EndLoadProperty BeginSaveChanges EndSaveChanges

Consideraciones sobre los subprocesos para las operaciones asincrónicas

MCT: Luis Dueñas

Pag 105 de 128

WCF Data Service En una aplicación multiproceso, al delegado que se registra como devolución de llamada para la operación asincrónica no se le invoca necesariamente en el mismo subproceso que se usó para llamar al método Begin, encargado de crear la solicitud inicial. En una aplicación cuya devolución de llamada se debe invocar en un subproceso concreto, debe calcular explícitamente las referencias a la ejecución del método End, que controla la respuesta, para el subproceso deseado. Por ejemplo, en las aplicaciones basadas en Windows Presentation Foundation (WPF) y en las aplicaciones basadas en Silverlight, se deben calcular las referencias a la respuesta para el subproceso de interfaz de usuario mediante el método BeginInvoke en el objeto Dispatcher.

5.5.1. Cómo: Ejecutar consultas de servicio de datos asincrónicos Al usar la biblioteca de cliente de WCF Data Services , puede realizar operaciones cliente-servidor, como ejecutar consultas y guardar cambios, de forma asincrónica. Nota: En una aplicación donde se debe invocar la devolución de llamada en un subproceso concreto, debe calcular explícitamente las referencias de la ejecución del método EndExecute.

Ejemplo En el siguiente ejemplo se muestra cómo ejecutar una consulta asincrónica llamando al método BeginExecute para iniciar la consulta. El delegado alineado llama al método EndExecute para mostrar los resultados de la consulta. Public Shared Sub BeginExecuteCustomersQuery() ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) ' Define the delegate to callback into the process Dim callback As AsyncCallback = AddressOf OnCustomersQueryComplete ' Define the query to execute asynchronously that returns ' all customers with their respective orders. Dim query As DataServiceQuery(Of Customer) = context.Customers.Expand("Orders") Try ' Begin query execution, supplying a method to handle the response ' and the original query object to maintain state in the callback. query.BeginExecute(callback, query) Catch ex As DataServiceQueryException Throw New ApplicationException("An error occurred during query execution.", ex) End Try End Sub ' Handle the query callback. Private Shared Sub OnCustomersQueryComplete(ByVal result As IAsyncResult) ' Get the original query from the result. Dim query As DataServiceQuery(Of Customer) = CType(result.AsyncState, DataServiceQuery(Of Customer)) ' Complete the query execution. For Each customer As Customer In query.EndExecute(result) Console.WriteLine("Customer Name: {0}", customer.CompanyName) For Each order As Order In customer.Orders Console.WriteLine("Order #: {0} - Freight $: {1}", order.OrderID, order.Freight) Next Next End Sub

5.5.2. Cómo: Crear una aplicación asincrónica de WPF MCT: Luis Dueñas

Pag 106 de 128

WCF Data Service Con WCF Data Services , puede enlazar los datos obtenidos de un servicio de datos a los elementos de la interfaz de usuario de una aplicación de Windows Presentation Framework (WPF). También puede ejecutar operaciones con el servicio de datos de una manera asincrónica, lo que permite a la aplicación continuar respondiendo mientras espera una respuesta a una solicitud al servicio de datos. Las aplicaciones para Silverlight deben tener acceso al servicio de datos de forma asincrónica. En este tema se muestra cómo tener acceso a un servicio de datos de forma asincrónica y enlazar los resultados a los elementos de una aplicación WPF. En los ejemplos de este tema se usa el servicio de datos de ejemplo Northwind y las clases del servicio de datos del cliente generadas automáticamente. Se crean este servicio y las clases de datos del cliente al completar el tutorial rápido de WCF Data Services.

Ejemplo El código XAML siguiente define la ventana de la aplicación WPF.













La página de código subyacente siguiente para el archivo XAML ejecuta una consulta asincrónica usando el servicio de datos y enlaza los resultados a los elementos de la ventana de WPF. Imports System Imports System.Collections.Generic Imports System.Linq Imports System.Text Imports System.Windows Imports System.Windows.Controls Imports System.Windows.Data Imports System.Windows.Documents Imports System.Windows.Input Imports System.Windows.Media

MCT: Luis Dueñas

Pag 107 de 128

WCF Data Service Imports System.Windows.Media.Imaging Imports System.Windows.Navigation Imports System.Windows.Shapes Imports NorthwindClient.Northwind Imports System.Data.Services.Client Imports System.Windows.Threading '/ '/ Interaction logic for OrderItems.xaml '/ Partial Public Class CustomerOrdersAsync Inherits Window Dim context As NorthwindEntities Private Shared customerBinding As DataServiceCollection(Of Customer) Private Const customerCountry As String = "Germany" ' Change this URI to the service URI for your implementation. Private Const svcUri As String = "http://localhost:12345/Northwind.svc/" ' Define a persisted result. Private Shared currentResult As IAsyncResult Delegate Sub DispatcherDelegate() Private Sub Window_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs) ' Initialize the context. context = New NorthwindEntities(New Uri(svcUri)) ' Define the delegate to callback into the process Dim callback As AsyncCallback = AddressOf OnQueryCompleted ' Define a query that returns customers and orders for a specific country. Dim query As DataServiceQuery(Of Customer) = context.Customers.Expand("Orders") _ .AddQueryOption("filter", "Country eq '" + customerCountry + "'") Try ' Begin asynchronously saving changes Imports the ' specified handler and query object state. query.BeginExecute(callback, query) Catch ex As DataServiceClientException MessageBox.Show(ex.ToString()) End Try End Sub Private Sub OnQueryCompleted(ByVal result As IAsyncResult) ' Persist the query result for the delegate. currentResult = result ' Use the Dispatcher to ensure that the ' asynchronous call returns in the correct thread. Dispatcher.BeginInvoke(New DispatcherDelegate(AddressOf QueryCompletedByDispatcher)) End Sub ' Handle the query callback. Private Sub QueryCompletedByDispatcher() Try ' Get the original query back from the result. Dim query = CType(currentResult.AsyncState, DataServiceQuery(Of Customer)) ' Instantiate the binding collection imports the ' results of the query execution. customerBinding = New DataServiceCollection(Of Customer)(query.EndExecute(currentResult)) ' Bind the collection to the root element of the UI. Me.LayoutRoot.DataContext = customerBinding

MCT: Luis Dueñas

Pag 108 de 128

WCF Data Service Catch ex As DataServiceRequestException MessageBox.Show(ex.ToString()) End Try End Sub Private Sub saveChangesButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) ' Define the delegate to callback into the process Dim callback As AsyncCallback = AddressOf OnSaveChangesCompleted Try ' Start the asynchronous call to save changes. context.BeginSaveChanges(callback, Nothing) Catch ex As DataServiceClientException MessageBox.Show(ex.ToString()) End Try End Sub Private Sub OnSaveChangesCompleted(ByVal result As IAsyncResult) ' Persist the query result for the delegate. currentResult = result ' Use the Dispatcher to ensure that the operation returns in the UI thread. Me.Dispatcher.BeginInvoke(New DispatcherDelegate(AddressOf SaveChangesCompletedByDispatcher)) ' Handle the query callback. End Sub Private Sub SaveChangesCompletedByDispatcher() Try ' Complete the save changes operation. context.EndSaveChanges(currentResult) Catch ex As DataServiceRequestException MessageBox.Show(ex.ToString()) End Try End Sub End Class

5.6. Realizar operaciones por lotes Open Data Protocol (OData) admite procesamiento por lotes de solicitudes en un servicio basado en OData . En WCF Data Services , cada operación que use la clase DataServiceContext, como ejecutar una consulta o guardar cambios, tiene como resultado el envío de una solicitud independiente al servicio de datos. Para mantener un número razonable de conjuntos de operaciones, puede definir los lotes operacionales explícitamente. Esto garantiza que todas las operaciones del lote se envían al servicio de datos en una única solicitud HTTP, permite al servidor procesar las operaciones automáticamente y reduce el número de viajes de ida y vuelta (round trip) al servicio de datos.

Operaciones de consulta por lotes Para ejecutar varias consultas en un lote único, debe crear cada consulta en el lote como una instancia independiente de la clase DataServiceRequest. Al crear una solicitud de consulta de esta manera, la propia consulta se define como un URI y sigue las reglas para el redireccionamiento de recursos. Las solicitudes de consulta por lotes se envían al servicio de datos cuando se llama al método ExecuteBatch, que contiene los objetos de la solicitud de consulta. Este método devuelve un objeto DataServiceResponse, que es una colección de objetos QueryOperationResponse que representan las respuestas a las consultas individuales del lote, cada una de los cuales contiene una colección de los objetos devueltos por la consulta o la información del error. Cuando se produce un error en cualquier operación de consulta única en el lote, se devuelve la información del error en el objeto QueryOperationResponse para la operación fallida, pero se siguen ejecutando las operaciones restantes. Las consultas por lotes se pueden ejecutar también de forma asincrónica.

MCT: Luis Dueñas

Pag 109 de 128

WCF Data Service

Procesamiento por lotes de la operación SaveChanges Al llamar al método SaveChanges, todos los cambios detectados por el contexto se traducen en operaciones basadas en REST, que se envían como solicitudes al servicio OData . De forma predeterminada, estos cambios no se envían en un mensaje de solicitud único. Para requerir que todos los cambios se envíen en una solicitud única, debe llamar al método SaveChanges e incluir un valor de Batch en la enumeración SaveChangesOptions que proporcione al método. También puede guardar de forma asincrónica los cambios por lotes.

5.6.1. Cómo: Ejecutar consultas por lotes La biblioteca de cliente de WCF Data Services le permite ejecutar más de una consulta en el servicio de datos de un solo lote.

Ejemplo En el ejemplo siguiente se muestra cómo llamar al método ExecuteBatch para ejecutar una matriz de los objetos DataServiceRequest que contiene consultas que devuelven objetos Products y Customers del servicio de datos de Northwind. La colección de objetos QueryOperationResponse del objeto DataServiceResponse devuelto es de tipo enumerado y la colección de objetos contenida en cada QueryOperationResponse también lo es. Dim customerId = "ALFKI" ' Create the DataServiceContext using the service URI. Dim context = New NorthwindEntities(svcUri) ' Create the separate query URI's, one that returns ' a single customer and another that returns all Products. Dim customerUri = New Uri(svcUri.AbsoluteUri & "/Customers('" & customerId & "')/?$expand=Orders") Dim productsUri = New Uri(svcUri.AbsoluteUri & "/Products") ' Create the query requests. Dim customerQuery = New DataServiceRequest(Of Customer)(customerUri) Dim productsQuery = New DataServiceRequest(Of Product)(productsUri) ' Add the query requests to a batch request array. Dim batchRequests = New DataServiceRequest() {customerQuery, productsQuery} Dim batchResponse As DataServiceResponse Try ' Execute the query batch and get the response. batchResponse = context.ExecuteBatch(batchRequests) If batchResponse.IsBatchResponse Then ' Parse the batchResponse.BatchHeaders. End If ' Enumerate over the results of the query. For Each response As QueryOperationResponse In batchResponse ' Handle an error response. If response.StatusCode > 299 OrElse response.StatusCode < 200 Then Console.WriteLine("An error occurred.") Console.WriteLine(response.Error.Message) Else ' Find the response for the Customers query. If response.Query.ElementType Is GetType(Customer) Then For Each customer As Customer In response Console.WriteLine("Customer: {0}", customer.CompanyName) For Each order As Order In customer.Orders Console.WriteLine("Order ID: {0} - Freight: {1}", order.OrderID, order.Freight) Next

MCT: Luis Dueñas

Pag 110 de 128

WCF Data Service Next ' Find the response for the Products query. ElseIf response.Query.ElementType Is GetType(Product) Then For Each product As Product In response Console.WriteLine("Product: {0}", product.ProductName) Next End If End If Next ' This type of error is raised when the data service returns with ' a response code < 200 or >299 in the top level element. Catch ex As DataServiceRequestException ' Get the response from the exception. batchResponse = ex.Response If (batchResponse.IsBatchResponse) Then ' Parse the batchResponse.BatchHeaders. End If For Each response As QueryOperationResponse In batchResponse If response.Error IsNot Nothing Then Console.WriteLine("An error occurred.") Console.WriteLine(response.Error.Message) End If Next End Try

5.7. Enlazar datos a controles Con WCF Data Services , puede enlazar controles como ComboBox y ListView a una instancia de la clase DataServiceCollection. Esta colección, que hereda de la clase ObservableCollection, contiene los datos de una fuente Open Data Protocol (OData) . Esta clase representa una colección de datos dinámicos que proporciona notificaciones si se agregan o se quitan elementos. Al usar una instancia de DataServiceCollection para el enlace de datos, las bibliotecas de cliente de WCF Data Services administran estos eventos para garantizar que los objetos supervisados por DataServiceContext sigan estando sincronizados con los datos en el elemento de la interfaz de usuario enlazada. La clase DataServiceCollection(indirectamente) implementa la interfaz INotifyCollectionChanged para avisar al contexto en el momento en el que se agreguen o quiten objetos de la colección. Los objetos de tipo de servicio de datos usados con una clase DataServiceCollection también deben implementar la interfaz INotifyPropertyChanged para avisar a la clase DataServiceCollection en el momento en el que cambien las propiedades de los objetos en la colección de enlaces. Nota: Al utilizar el cuadro de diálogo Agregar referencia de servicio o la herramienta DataSvcUtil.exe con la opción /dataservicecollection para generar las clases de servicio de datos de cliente, las clases de datos generadas implementan la interfaz INotifyPropertyChanged.

Crear la colección de enlaces Cree una nueva instancia de la clase DataServiceCollection llamando a uno de los métodos de constructor de clase con una instancia DataServiceContext proporcionada y, opcionalmente, una consulta DataServiceQuery o LINQ que, cuando se ejecuta, devuelve una instancia IEnumerable. IEnumerable proporciona el origen de objetos para la colección de enlaces, que se materializan de una fuente OData . De manera predeterminada, DataServiceContext realiza automáticamente el seguimiento de los cambios realizados en los objetos enlazados y elementos insertados en la colección. Si necesita realizar el

MCT: Luis Dueñas

Pag 111 de 128

WCF Data Service seguimiento de estos cambios manualmente, llame a uno de los métodos de constructor que toma un parámetro trackingMode y especifica un valor del campo None. En el siguiente ejemplo se muestra cómo crear una instancia de la clase DataServiceCollection basada en un DataServiceContext proporcionado y una DataServiceQuery que devuelve todos los clientes con pedidos (en el ejemplo, Orders) relacionados: ' Create a new collection that contains all customers and related orders. Dim trackedCustomers As DataServiceCollection(Of Customer) = _ New DataServiceCollection(Of Customer)(context.Customers.Expand("Orders"))

Enlazar datos a elementos de Windows Presentation Foundation Dado que la clase DataServiceCollection se hereda de la clase ObservableCollection, puede enlazar los objetos a un elemento o control en una aplicación Windows Presentation Foundation (WPF) tal como lo haría si usara la clase ObservableCollection para el enlace. Una manera de enlazar los datos del servicio de datos a controles WPF es establecer la propiedad DataContext del elemento para la instancia de la clase DataServiceCollection que contiene el resultado de la consulta. En este caso, usted utiliza la propiedad ItemsSource para establecer el origen del objeto para el control. Utilice la propiedad DisplayMemberPath para especificar qué propiedad mostrar del objeto enlazado. Si está enlazando un elemento a un objeto relacionado que devuelve una propiedad de navegación, incluya la ruta de acceso en el enlace definido para la propiedad ItemsSource. Esta ruta de acceso es relativa al objeto raíz establecido por la propiedad DataContext del control primario. En el siguiente ejemplo se establece la propiedad DataContext de un elemento StackPanel para enlazar el control primario a una clase DataServiceCollection de objetos de cliente: ' Create a LINQ query that returns customers with related orders. Dim customerQuery = From cust In context.Customers.Expand("Orders") Where cust.Country = customerCountry Select cust ' Create a new collection for binding based on the LINQ query. trackedCustomers = New DataServiceCollection(Of Customer)(customerQuery, TrackingMode.AutoChangeTracking, "Customers", _ AddressOf OnMyPropertyChanged, AddressOf OnMyCollectionChanged) ' Bind the root StackPanel element to the collection ' related object binding paths are defined in the XAML. Me.LayoutRoot.DataContext = trackedCustomers

En el ejemplo siguiente se muestra la definición de enlace XAML de los controles secundarios DataGrid y ComboBox:









MCT: Luis Dueñas

Pag 112 de 128

WCF Data Service

Cuando una entidad participa en una relación de uno a varios o de varios a varios, la propiedad de navegación para la relación devuelve una colección de objetos relacionados. Cuando usted utiliza el cuadro de diálogo Agregar referencia de servicio o la herramienta DataSvcUtil.exe para generar las clases de servicio de datos de cliente, la propiedad de navegación devuelve una instancia de la clase DataServiceCollection. Esto le permite enlazar objetos relacionados a un control, así como admitir escenarios de enlace de WPF comunes, como el patrón de enlace principal-detalle para entidades relacionadas. En el ejemplo XAML anterior, el código XAML enlaza la clase principal DataServiceCollection al elemento de datos de raíz. El pedido DataGrid se enlaza a la clase DataServiceCollection de pedidos devueltos desde el objeto Customers seleccionado, que se enlaza a su vez al elemento de datos de raíz de Window.

Enlazar datos a controles de Windows Forms Para enlazar objetos a un control de Windows Form, establezca la propiedad DataSource del control en la instancia de la clase DataServiceCollection que contiene el resultado de la consulta. Nota: El enlace de datos solo se admite para los controles que realizan escuchas de los eventos de cambio mediante la implementación de las interfaces INotifyPropertyChanged y INotifyCollectionChanged. Cuando un control no admite este tipo de notificación de cambios, los cambios que se realizan en la clase DataServiceCollection subyacente no se reflejan en el control enlazado. En el siguiente ejemplo DataServiceCollection se enlaza a ComboBox: ' Create a new collection for binding based on the LINQ query. trackedCustomers = New DataServiceCollection(Of Customer)(customerQuery) 'Bind the Customers combobox to the collection. customersComboBox.DisplayMember = "CustomerID" customersComboBox.DataSource = trackedCustomers

Cuando utiliza el cuadro de diálogo Agregar referencia de servicio para generar las clases de servicio de datos de cliente, también se crea un origen de datos de proyecto basado en el DataServiceContext generado. Con este origen de datos, puede crear controles o elementos de la interfaz de usuario que muestren datos del servicio de datos simplemente arrastrando los elementos desde la ventana Orígenes de datos hasta el diseñador. Estos elementos aparecen en la interfaz de usuario de la aplicación y se enlazan al origen de datos.

Enlazar datos paginados Un servicio de datos se puede configurar para limitar la cantidad de datos consultados que se devuelven en un único mensaje de respuesta. Cuando el servicio de datos está paginando los datos de respuesta, cada respuesta contiene un vínculo que se utiliza para devolver la página siguiente de resultados. En este caso, debe cargar las páginas explícitamente llamando al método Load en la clase DataServiceCollection, especificando el URI obtenido de la propiedad NextLinkUri, como en el siguiente ejemplo: ' Create a new collection for binding based on the LINQ query. trackedCustomers = New DataServiceCollection(Of Customer)(customerQuery) ' Load all pages of the response at once. While trackedCustomers.Continuation IsNot Nothing trackedCustomers.Load(context.Execute(Of Customer)(trackedCustomers.Continuation.NextLinkUri)) End While

Los objetos relacionados se cargan de una manera similar.

Personalizar comportamientos de enlace de datos La clase DataServiceCollection le permite interceptar los eventos que se provocan al realizar cambios en la colección (como agregar o quitar un objeto) o en las propiedades de objeto en una colección. Puede modificar los eventos de enlace de datos para reemplazar el comportamiento predeterminado, que incluye las siguientes restricciones:

MCT: Luis Dueñas

Pag 113 de 128

WCF Data Service No se realiza ninguna validación dentro de los delegados. Al agregar una entidad, se agregan automáticamente las entidades relacionadas. Al eliminar una entidad, no se eliminan las entidades relacionadas. Al crear una nueva instancia de DataServiceCollection, tiene la opción de especificar los siguientes parámetros, que definen los delegados para los métodos que administran los eventos provocados cuando se cambian los objetos enlazados: entityChanged: método al que se llama cuando cambia la propiedad de un objeto enlazado. Este delegado Func acepta un objeto EntityChangedParams y devuelve un valor booleano que indica si el comportamiento predeterminado, que consiste en llamar a UpdateObject en DataServiceContext, todavía se debería producir. entityCollectionChanged: método al que se llama cuando un objeto se agrega o se quita de la colección de enlaces. Este delegado Func acepta un objeto EntityCollectionChangedParams y devuelve un valor booleano que indica si el comportamiento predeterminado, que consiste en llamar a AddObject para una acción Add o a DeleteObject para una acción Remove en DataServiceContext, aún se debe producir. Nota: WCF Data Services no realiza ninguna validación de los comportamientos personalizados que implementa el usuario en estos delegados. En el siguiente ejemplo, la acción Remove está personalizada para llamar al método DeleteObject y DeleteLink con el fin de quitar las entidades Orders_Details que pertenecen a una entidad Orders eliminada. Esta acción personalizada se realiza porque las entidades dependientes no se eliminan automáticamente cuando se elimina la entidad primaria. ' Method that is called when the CollectionChanged event is handled. Private Function OnMyCollectionChanged( _ ByVal entityCollectionChangedinfo As EntityCollectionChangedParams) As Boolean If entityCollectionChangedinfo.Action = _ NotifyCollectionChangedAction.Remove Then ' Delete the related items when an order is deleted. If entityCollectionChangedinfo.TargetEntity.GetType() Is GetType(Order) Then ' Get the context and object from the supplied parameter. Dim context = entityCollectionChangedinfo.Context Dim deletedOrder As Order = _ CType(entityCollectionChangedinfo.TargetEntity, Order) If deletedOrder.Order_Details.Count = 0 Then ' Load the related OrderDetails. context.LoadProperty(deletedOrder, "Order_Details") End If ' Delete the order and its related items For Each item As Order_Detail In deletedOrder.Order_Details context.DeleteObject(item) Next ' Delete the order and then return false since the object is already deleted. context.DeleteObject(deletedOrder) Return True Else Return False End If Else ' Use the default behavior. Return False End If

MCT: Luis Dueñas

Pag 114 de 128

WCF Data Service End Function

El comportamiento predeterminado cuando un objeto se quita de una clase DataServiceCollection utilizando el método Remove es que el objeto también se marca como eliminado en DataServiceContext. Para cambiar este comportamiento puede especificar un delegado para un método en el parámetro entityCollectionChanged al que se llama cuando se produce el evento CollectionChanged.

Enlazar datos con clases de datos de cliente personalizadas Para poder cargar los objetos en la clase DataServiceCollection, los objetos deben implementar la interfaz INotifyPropertyChanged. Las clases de cliente del servicio de datos que se generan al utilizar el cuadro de diálogo Agregar referencia de servicio o la herramienta DataSvcUtil.exe implementan esta interfaz. Si proporciona sus propias clases de datos de cliente, debe utilizar otro tipo de colección para el enlace de datos. Cuando los objetos cambian, debe administrar los eventos en los controles enlazados a datos para que llamen a los siguientes métodos de la clase DataServiceContext: AddObject: cuando se agrega un nuevo objeto a la colección. DeleteObject: cuando se elimina un objeto de la colección. UpdateObject: cuando se cambia una propiedad en un objeto de la colección. AddLink: cuando se agrega un objeto a una colección del objeto relacionado. SetLink: cuando se agrega un objeto a una colección de objetos relacionados.

5.7.1. Cómo: Enlazar datos a los elementos de WPF Con WCF Data Services , puede enlazar elementos de Windows Presentation Foundation (WPF) como la clase ListBoxo la clase ComboBox a una instancia de la clase DataServiceCollection, que controla los eventos generados por los controles para mantener la clase DataServiceContext sincronizada con los cambios efectuados en los datos de los controles.

Ejemplo El ejemplo siguiente corresponde a la página de código subyacente de una página de lenguaje XAML que define la ventana SalesOrders en WPF. Cuando se carga la ventana, se crea una DataServiceCollection dependiendo del resultado de una consulta que devuelve los clientes filtrados por país. Se cargan todas las páginas de este resultado paginado, junto con los pedidos relacionados, y se enlazan a la propiedad DataContext de StackPanel que es el control de diseño raíz para la ventana de WPF. Imports System Imports System.Collections.Generic Imports System.Linq Imports System.Text Imports System.Windows Imports System.Windows.Controls Imports System.Windows.Data Imports System.Windows.Documents Imports System.Windows.Input Imports System.Windows.Media Imports System.Windows.Media.Imaging Imports System.Windows.Navigation Imports System.Windows.Shapes Imports System.Data.Services.Client Imports NorthwindClient.Northwind Partial Public Class CustomerOrdersWpf3 Inherits Window Private context As NorthwindEntities Private trackedCustomers As DataServiceCollection(Of Customer)

MCT: Luis Dueñas

Pag 115 de 128

WCF Data Service Private Const customerCountry As String = "Germany" Private Const svcUri As String = "http://localhost:12345/Northwind.svc/" Private Sub Window_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs) Try ' Initialize the context for the data service. context = New NorthwindEntities(New Uri(svcUri)) ' Create a LINQ query that returns customers with related orders. Dim customerQuery = From cust In context.Customers Where cust.Country = customerCountry Select cust ' Create a new collection for binding based on the LINQ query. trackedCustomers = New DataServiceCollection(Of Customer)(customerQuery) ' Load all pages of the response at once. While trackedCustomers.Continuation IsNot Nothing trackedCustomers.Load(context.Execute(Of Customer)(trackedCustomers.Continuation.NextLinkUri)) End While ' Bind the root StackPanel element to the collection ' related object binding paths are defined in the XAML. Me.LayoutRoot.DataContext = trackedCustomers Catch ex As DataServiceQueryException MessageBox.Show("The query could not be completed:\n" + ex.ToString()) Catch ex As InvalidOperationException MessageBox.Show("The following error occurred:\n" + ex.ToString()) End Try End Sub Private Sub customerIDComboBox_SelectionChanged(ByVal sender As Object, ByVal e As SelectionChangedEventArgs) Dim selectedCustomer As Customer = CType(Me.customerIDComboBox.SelectedItem, Customer) Try If selectedCustomer.Orders.Count = 0 Then ' Load the first page of related orders for the selected customer. context.LoadProperty(selectedCustomer, "Orders") End If ' Load all remaining pages. While selectedCustomer.Orders.Continuation IsNot Nothing selectedCustomer.Orders.Load(context.Execute(Of Order)(selectedCustomer.Orders.Continuation.NextLinkUri)) End While Catch ex As Exception MessageBox.Show(ex.ToString()) End Try End Sub Private Sub saveChangesButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Try ' Save changes to the data service. context.SaveChanges() Catch ex As Exception MessageBox.Show(ex.ToString()) End Try End Sub End Class

El siguiente XAML define la ventana SalesOrders en WPF del ejemplo anterior.











El ejemplo siguiente corresponde a la página de código subyacente de una página de lenguaje XAML que define la ventana SalesOrders en WPF. Cuando se carga la ventana, se crea una DataServiceCollection dependiendo del resultado de una consulta que devuelve los clientes con objetos relacionados, filtrados por país. Este resultado se enlaza a la propiedad DataContext de StackPanel que es el control de diseño raíz para la ventana de WPF. Imports System Imports System.Collections.Generic Imports System.Linq Imports System.Text Imports System.Windows Imports System.Windows.Controls Imports System.Windows.Data Imports System.Windows.Documents Imports System.Windows.Input Imports System.Windows.Media Imports System.Windows.Media.Imaging Imports System.Windows.Navigation Imports System.Windows.Shapes Imports System.Data.Services.Client Imports NorthwindClient.Northwind Partial Public Class CustomerOrdersWpf Inherits Window Private context As NorthwindEntities Private trackedCustomers As DataServiceCollection(Of Customer) Private Const customerCountry As String = "Germany" Private Const svcUri As String = "http://localhost:12345/Northwind.svc/" Private Sub Window_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)

MCT: Luis Dueñas

Pag 117 de 128

WCF Data Service Try ' Initialize the context for the data service. context = New NorthwindEntities(New Uri(svcUri)) ' Create a LINQ query that returns customers with related orders. Dim customerQuery = From cust In context.Customers.Expand("Orders") _ Where cust.Country = customerCountry Select cust ' Create a new collection for binding based on the LINQ query. trackedCustomers = New DataServiceCollection(Of Customer)(customerQuery) ' Bind the root StackPanel element to the collection ' related object binding paths are defined in the XAML. Me.LayoutRoot.DataContext = trackedCustomers Catch ex As DataServiceQueryException MessageBox.Show("The query could not be completed:\n" + ex.ToString()) Catch ex As InvalidOperationException MessageBox.Show("The following error occurred:\n" + ex.ToString()) End Try End Sub Private Sub saveChangesButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) ' Save changes to the data service. context.SaveChanges() End Sub End Class

El siguiente XAML define la ventana SalesOrders en WPF del ejemplo anterior.











MCT: Luis Dueñas

Pag 118 de 128

WCF Data Service

5.7.2. Cómo: Enlazar datos mediante un origen de datos del proyecto Puede crear orígenes de datos basados en los objetos de datos generados en una aplicación cliente de WCF Data Services . Cuando se agrega una referencia a un servicio de datos usando el cuadro de diálogo Agregar referencia de servicio, se crea un origen de datos del proyecto junto con las clases generadas de datos del cliente. Para cada conjunto de entidades expuesto por el servicio de datos se crea un origen de datos. Puede crear formularios que muestran datos procedentes del servicio arrastrando estos elementos del origen de datos desde la ventana Orígenes de datos al diseñador. Estos elementos se convierten en controles enlazados al origen de datos. Durante la ejecución, este origen de datos se enlaza a una instancia de la clase DataServiceCollection, que se rellena con los objetos devueltos por una consulta al servicio de datos.

Para usar un origen de datos del proyecto en una ventana de WPF 1.

En un proyecto WPF, agregue una referencia al servicio de datos de Northwind.

2.

En la ventana Orígenes de datos, expanda el nodo Customers del origen de datos del proyecto

3.

NorthwindEntities. Haga clic en el elemento CustomerID, seleccione ComboBox en la lista y arrastre el elemento CustomerID desde el nodo Customers al diseñador. Con ello se crean los siguientes elementos de objeto en el archivo XAML de la ventana:

o

Un elemento CollectionViewSource denominado customersViewSource. La propiedad DataContext del elemento de objeto Grid de nivel superior se establece en este nuevo elemento CollectionViewSource.

4.

o

Un control ComboBox enlazado a datos denominado CustomerID.

o

Un control Label.

Arrastre la propiedad de navegación Orders al diseñador. Con ello se crean los siguientes elementos de objeto adicionales en el archivo XAML de la ventana:

o

Un

segundo

elemento

CollectionViewSource

denominado

customersOrdersViewSource, cuyo origen es customerViewSource. o

Un control DataGrid enlazado a datos denominado ordersDataGrid.

5. 6.

(Opcional) Arrastre elementos adicionales desde el nodo Customers al diseñador. Abra la página de código del formulario y agregue las instrucciones using siguientes (Imports en Visual Basic):

7.

En la clase parcial que define el formulario, agregue el código siguiente que crea una instancia de ObjectContext y define la constante customerID. Private context As NorthwindEntities Private customersViewSource As CollectionViewSource Private trackedCustomers As DataServiceCollection(Of Customer) Private Const customerCountry As String = "Germany" Private Const svcUri As String = "http://localhost:12345/Northwind.svc/"

8.

En el diseñador, seleccione la ventana. Nota: Asegúrese de que selecciona la ventana, y no el contenido situado dentro de ella. Cuando está seleccionada la ventana, el cuadro de texto Nombre situado cerca de la parte superior de la

MCT: Luis Dueñas

Pag 119 de 128

WCF Data Service ventana Propiedades debería contener el nombre de la ventana. 9. En la ventana Propiedades, seleccione el botón Eventos. 10. Busque el evento Loaded y, a continuación, haga doble clic en la lista desplegable situada junto a este evento. Visual Studio abre el archivo de código subyacente para la ventana y genera un controlador de eventos Loaded. 11. En el controlador de eventos Loaded que se acaba de crear, copie y pegue el código siguiente. ' Initialize the context for the data service. context = New NorthwindEntities(New Uri(svcUri)) ' Create a LINQ query that returns customers with related orders. Dim customerQuery = From cust In context.Customers.Expand("Orders") _ Where cust.Country = customerCountry Select cust ' Create a new collection for binding based on the LINQ query. trackedCustomers = New DataServiceCollection(Of Customer)(customerQuery) try ' Get the customersViewSource resource and set the binding to the collection. customersViewSource = CType(Me.FindResource("customersViewSource"), CollectionViewSource) customersViewSource.Source = trackedCustomers customersViewSource.View.MoveCurrentToFirst() Catch ex As DataServiceQueryException MessageBox.Show("The query could not be completed:\n" + ex.ToString()) Catch ex As InvalidOperationException MessageBox.Show("The following error occurred:\n" + ex.ToString()) End Try

12. Este código crea una instancia de DataServiceCollection para el tipo Customers basándose en la ejecución de una consulta LINQ que devuelve una interfaz IEnumerable de Customers junto con los objetos Orders relacionados desde el servicio de datos de Northwind y la enlaza con el elemento customersViewSource.

Para usar un origen de datos del proyecto en un formulario de Windows 1.

En la ventana Orígenes de datos, expanda el nodo Customers del origen de datos del proyecto NorthwindEntities.

2.

Haga clic en el elemento CustomerID, seleccione ComboBox en la lista y arrastre el elemento CustomerID desde el nodo Customers al diseñador. De esta forma se crean los controles siguientes en el formulario:

o

Una instancia de BindingSource denominada customersBindingSource.

o

Una instancia de BindingNavigator denominada customersBindingNavigator. Puede eliminar este control, dado que no será necesario.

o

Un control ComboBox enlazado a datos denominado CustomerID.

o

Un control Label.

3.

Arrastre la propiedad de navegación Orders al formulario.

4.

De esta forma se crea el control ordersBindingSource con la propiedad DataSource del control establecida en customersBindingSource y la propiedad DataMember establecida en Customers.

MCT: Luis Dueñas

Pag 120 de 128

WCF Data Service También se crea el control enlazado a datos ordersDataGridView en el formulario, acompañado 5. 6.

de un control de etiqueta con el título apropiado. (Opcional) Arrastre elementos adicionales desde el nodo Customers al diseñador. Abra la página de código del formulario y agregue las instrucciones using siguientes (Imports en Visual Basic): Imports System.Data.Services.Client Imports NorthwindClient.Northwind

7.

En la clase parcial que define el formulario, agregue el código siguiente que crea una instancia de ObjectContext y define la constante customerID. Private context As NorthwindEntities Private trackedCustomers As DataServiceCollection(Of Customer) Private Const customerCountry As String = "Germany" Private Const svcUri As String = "http:'localhost:12345/Northwind.svc/"

8.

En el diseñador de formularios, haga doble clic en el formulario. De esta forma se abre la página de código del formulario y se crea el método que administra el evento Load para el formulario.

9.

En el controlador de eventos Load, copie y pegue el código siguiente. ' Initialize the context for the data service. context = New NorthwindEntities(New Uri(svcUri)) Try ' Create a LINQ query that returns customers with related orders. Dim customerQuery = From cust In context.Customers.Expand("Orders") _ Where cust.Country = customerCountry Select cust ' Create a new collection for binding based on the LINQ query. trackedCustomers = New DataServiceCollection(Of Customer)(customerQuery) 'Bind the Customers combobox to the collection. customersComboBox.DisplayMember = "CustomerID" customersComboBox.DataSource = trackedCustomers Catch ex As DataServiceQueryException MessageBox.Show("The query could not be completed:\n" + ex.ToString()) Catch ex As InvalidOperationException MessageBox.Show("The following error occurred:\n" + ex.ToString())

10. Este código crea una instancia de DataServiceCollection para el tipo Customers basándose en la ejecución de una instancia de DataServiceQuery que devuelve una interfaz IEnumerable de

Customers desde el servicio de datos de Northwind y la enlaza con el elemento customersBindingSource.

5.7.3. Cómo: Personalizar comportamientos de enlace de datos Con WCF Data Services , puede proporcionar lógica personalizada a la que DataServiceCollection llama cuando se agrega o se quita un objeto de la colección de enlaces o cuando se detecta un cambio en una propiedad. Esta lógica personalizada se proporciona en forma de métodos, a los que se hace referencia como delegados Func, que devuelven un valor false cuando aún se debe ejecutar el comportamiento predeterminado al completarse el método personalizado y el valor true cuando se debe detener el procesamiento posterior del evento. Los ejemplos de este tema proporcionan métodos personalizados para los parámetros entityCollectionChanged y entityChanged de la clase DataServiceCollection. En los ejemplos de este tema

MCT: Luis Dueñas

Pag 121 de 128

WCF Data Service se usa el servicio de datos de ejemplo Northwind y las clases del servicio de datos del cliente generadas automáticamente. Se crean este servicio y las clases de datos del cliente al completar el tutorial rápido de WCF Data Services.

Ejemplo La siguiente página de código subyacente para el archivo XAML crea una instancia de DataServiceCollection con métodos personalizados a los que se llama cuando se producen cambios en los datos enlazados a la colección de enlaces. Cuando se produce el evento CollectionChanged, el método proporcionado evita que se elimine del servicio de datos un elemento que se ha quitado de la colección de enlaces. Cuando se produce el evento PropertyChanged, se valida el valor de ShipDate para asegurarse de que no se realizan cambios en los pedidos que ya han enviado. Imports System Imports System.Collections.Generic Imports System.Linq Imports System.Text Imports System.Windows Imports System.Windows.Controls Imports System.Windows.Data Imports System.Windows.Documents Imports System.Windows.Input Imports System.Windows.Media Imports System.Windows.Media.Imaging Imports System.Windows.Navigation Imports System.Windows.Shapes Imports System.Data.Services.Client Imports NorthwindClient.Northwind Imports System.Collections.Specialized Partial Public Class CustomerOrdersCustom Inherits Window Private context As NorthwindEntities Private trackedCustomers As DataServiceCollection(Of Customer) Private Const customerCountry As String = "Germany" Private Const svcUri As String = "http://localhost:12345/Northwind.svc/" Private Sub Window_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs) Try ' Initialize the context for the data service. context = New NorthwindEntities(New Uri(svcUri)) ' Create a LINQ query that returns customers with related orders. Dim customerQuery = From cust In context.Customers.Expand("Orders") _ Where cust.Country = customerCountry Select cust ' Create a new collection for binding based on the LINQ query. trackedCustomers = New DataServiceCollection(Of Customer)(customerQuery, _ TrackingMode.AutoChangeTracking, "Customers", _ AddressOf OnMyPropertyChanged, AddressOf OnMyCollectionChanged) ' Bind the root StackPanel element to the collection ' related object binding paths are defined in the XAML. Me.LayoutRoot.DataContext = trackedCustomers Catch ex As DataServiceQueryException MessageBox.Show("The query could not be completed:\n" + ex.ToString()) Catch ex As InvalidOperationException MessageBox.Show("The following error occurred:\n" + ex.ToString()) End Try

MCT: Luis Dueñas

Pag 122 de 128

WCF Data Service End Sub ' Method that is called when the CollectionChanged event is handled. Private Function OnMyCollectionChanged( _ ByVal entityCollectionChangedinfo As EntityCollectionChangedParams) As Boolean If entityCollectionChangedinfo.Action = _ NotifyCollectionChangedAction.Remove Then ' Delete the related items when an order is deleted. If entityCollectionChangedinfo.TargetEntity.GetType() Is GetType(Order) Then ' Get the context and object from the supplied parameter. Dim context = entityCollectionChangedinfo.Context Dim deletedOrder As Order = CType(entityCollectionChangedinfo.TargetEntity, Order) If deletedOrder.Order_Details.Count = 0 Then ' Load the related OrderDetails. context.LoadProperty(deletedOrder, "Order_Details") End If ' Delete the order and its related items For Each item As Order_Detail In deletedOrder.Order_Details context.DeleteObject(item) Next ' Delete the order and then return false since the object is already deleted. context.DeleteObject(deletedOrder) Return True Else Return False End If Else ' Use the default behavior. Return False End If End Function ' Method that is called when the PropertyChanged event is handled. Private Function OnMyPropertyChanged( _ ByVal entityChangedInfo As EntityChangedParams) As Boolean ' Validate a changed order to ensure that changes are not made ' after the order ships. If entityChangedInfo.Entity.GetType() Is GetType(Order) AndAlso _ (CType(entityChangedInfo.Entity, Order).ShippedDate < DateTime.Today) Then Throw New ApplicationException(String.Format( _ "The order {0} cannot be changed because it shipped on {1}.", _ CType(entityChangedInfo.Entity, Order).OrderID, _ CType(entityChangedInfo.Entity, Order).ShippedDate)) Return False Else Return True End If End Function Private Sub deleteButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) ' Get the Orders binding collection. If customerIDComboBox.SelectedItem IsNot Nothing Then Dim trackedOrders As DataServiceCollection(Of Order) = _ (CType(customerIDComboBox.SelectedItem, Customer)).Orders ' Remove the currently selected order.

MCT: Luis Dueñas

Pag 123 de 128

WCF Data Service trackedOrders.Remove(CType(ordersDataGrid.SelectedItem, Order)) End If End Sub Private Sub saveChangesButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Try ' Save changes to the data service. context.SaveChanges() Catch ex As Exception MessageBox.Show(ex.ToString()) End Try End Sub End Class

El código XAML siguiente define la ventana del ejemplo anterior.













5.8. Administrar el contexto del servicio de datos Nota: En este tema se describe la nueva funcionalidad en WCF Data Services. Esta nueva funcionalidad admite la versión 3 de Open Data Protocol (OData) y está disponible como actualización en la versión 4 de .NET Framework. Puede descargar e instalar la actualización desde el Centro de Descarga de Microsoft.

MCT: Luis Dueñas

Pag 124 de 128

WCF Data Service La clase DataServiceContext encapsula las operaciones admitidas en un servicio de datos determinado. Los servicios de OData carecen de estado, pero no ocurre lo mismo con el contexto. Por consiguiente, puede usar la clase DataServiceContext para mantener el estado en el cliente entre las interacciones con el servicio de datos para admitir características como la administración de cambios. Esta clase también administra las identidades y realiza el seguimiento de los cambios.

Opciones de combinación y resolución de identidad Cuando se ejecuta el objeto DataServiceQuery, las entidades de la fuente de respuesta se materializan en objetos. La forma en que las entradas de un mensaje de respuesta se materializan en objetos se basa en la resolución de identidades y depende de la opción de combinación con la que se ejecutó la consulta. Cuando varias consultas o solicitudes de carga se ejecutan en el ámbito de un DataServiceContextúnico, el cliente WCF Data Services solo realiza el seguimiento de una única instancia de objeto que tiene un valor de clave concreto. Esta clave, que se utiliza para realizar la resolución de identidades, identifica una entidad de forma inequívoca. De forma predeterminada, el cliente solo materializa una entrada de la fuente de respuesta en un objeto para las entidades de las que aún no realiza un seguimiento DataServiceContext. Esto significa que los cambios en los objetos que ya están en la memoria caché no se sobrescriben. Este comportamiento se controla especificando un valor de MergeOption para las consultas y las operaciones de carga. Esta opción se especifica estableciendo la propiedad MergeOption en DataServiceContext. AppendOnly es el valor de la opción de combinación predeterminada. De este modo, solo se materializan los objetos de entidades de las que no se realiza un seguimiento, lo que significa que los objetos existentes no se sobrescriben. Otra manera de evitar que los cambios en los objetos del cliente se sobrescriban con las actualizaciones del servicio de datos es especificar PreserveChanges. Cuando se especifica OverwriteChanges, los valores de los objetos del cliente son reemplazados por los valores más recientes de las entradas de la fuente de respuesta, aunque se hayan realizado cambios en estos objetos. Cuando se utiliza una opción de combinación NoTracking, DataServiceContext no puede enviar los cambios realizados en los objetos de cliente al servicio de datos. Con esta opción, los cambios siempre se sobrescriben con valores del servicio de datos.

Administrar la simultaneidad OData admite la simultaneidad optimista que permite al servicio de datos detectar conflictos de actualización. El proveedor del servicio de datos se puede configurar de manera que el servicio de datos compruebe los cambios en las entidades mediante un token de simultaneidad. Este token incluye una o más propiedades de un tipo de entidad que el servicio de datos valida para determinar si un recurso ha cambiado. El cliente de WCF Data Services administra los tokens de simultaneidad, que se incluyen en el encabezado eTag de las solicitudes al servicio de datos y las respuestas de este. DataServiceContext realiza el seguimiento de los cambios realizados en los objetos notificados manualmente utilizando AddObject, UpdateObject y DeleteObject o mediante DataServiceCollection. Cuando se llama al método SaveChanges, el cliente devuelve los cambios al servicio de datos. SaveChanges puede generar un error cuando los cambios de los datos en el cliente están en conflicto con los cambios en el servicio de datos. Cuando esto sucede, debe consultar de nuevo el recurso de entidad para recibir los datos actualizados. Para sobrescribir los cambios en el servicio de datos, ejecute la consulta con la opción de combinación PreserveChanges. Al llamar de nuevo al método SaveChanges, los cambios conservados en el cliente se mantienen en el servicio de datos, siempre que no se hayan realizado otros cambios en el recurso del servicio de datos.

Guardar los cambios El seguimiento de los cambios se realiza en la instancia de la clase DataServiceContext, pero los cambios no se envían al servidor inmediatamente. Una vez que haya efectuado los cambios necesarios de una actividad determinada, llame al método SaveChanges para enviar todos los cambios al servicio de datos. Se devuelve un objeto DataServiceResponse cuando finaliza la operación SaveChanges. El objeto DataServiceResponse incluye una secuencia de objetos OperationResponse que, a su vez, contienen una secuencia de instancias de EntityDescriptor o LinkDescriptor que representan los cambios que se guardaron o se intentaron realizar. Cuando se crea o modifica una entidad en el servicio de datos, la instancia de la clase EntityDescriptor contiene una referencia a la entidad actualizada, que incluye los valores de propiedad generados por el servidor, como el valor de ProductID generado en el ejemplo anterior. La

MCT: Luis Dueñas

Pag 125 de 128

WCF Data Service biblioteca cliente actualiza automáticamente el objeto de .NET Framework para que tenga estos nuevos valores. En el caso de las operaciones de inserción y actualización correctas, la propiedad de estado del objeto EntityDescriptor o del objeto LinkDescriptor asociado a la operación se establece en Unchanged y los nuevos valores se combinan mediante OverwriteChanges. Cuando una operación de inserción, actualización o eliminación produce un error en el servicio de datos, el estado de la entidad permanece como estaba antes de que se llamara al método SaveChanges y la propiedad Error de OperationResponse se establece en una instancia de DataServiceRequestException que contiene información sobre el error.

Establecer el método HTTP para las actualizaciones De forma predeterminada, la biblioteca cliente de .NET Framework envía actualizaciones a las entidades existentes como solicitudes MERGE. Una solicitud MERGE actualiza las propiedades seleccionadas de la entidad; sin embargo, el cliente siempre incluye todas las propiedades en la solicitud MERGE, incluso las propiedades que no hayan cambiado. El protocolo OData también admite el envío de solicitudes PUT para actualizar las entidades. En una solicitud PUT, una entidad existente se reemplaza básicamente por una nueva instancia de la entidad con valores de propiedad desde el cliente. Para usar las solicitudes PUT, establezca la marca del objeto ReplaceOnUpdate de la enumeración SaveChangesOptions cuando se llame a SaveChanges. Nota: Una solicitud PUT se comportará de forma distinta que una solicitud MERGE cuando el cliente no conozca todas las propiedades de la entidad. Esto puede ocurrir cuando se proyecte un tipo de entidad en un nuevo tipo en el cliente. También puede ocurrir cuando se hayan agregado nuevas propiedades a la entidad del modelo de datos del servicio y la propiedad IgnoreMissingProperties de la clase DataServiceContext se establezca en true para ignorar tales errores de asignación de cliente. En estos casos, una solicitud PUT restablecerá las propiedades que desconozca el cliente en sus valores predeterminados.

5.9. Trabajar con datos binarios La biblioteca de cliente de WCF Data Services le permite recuperar y actualizar los datos binarios de una fuente Open Data Protocol (OData) de una de las siguientes formas: Como una propiedad del tipo primitivo de una entidad. Este es el método recomendado para trabajar con objetos de datos binarios pequeños que se pueden cargar con facilidad en la memoria. En este caso, la propiedad binaria es una propiedad de entidad expuesta por el modelo de datos y el servicio de datos serializa los datos binarios como XML codificado binario de base 64 en el mensaje de respuesta. Como flujo de recursos binarios independiente. Este es el método recomendado para obtener acceso y cambiar datos de objetos binarios grandes (BLOB) que pueden representar una foto, un vídeo o cualquier otro tipo de datos codificados binarios. WCF Data Services implementa la transmisión por secuencias de datos binarios mediante HTTP como se define en OData . En este mecanismo, los datos binarios se tratan como recurso multimedia independiente de una entidad pero está relacionado con ella, que se denomina entrada de vínculo multimedia. Sugerencia: Para obtener un ejemplo paso a paso de cómo crear una aplicación cliente de Windows Presentation Foundation (WPF) que descargue archivos de imágenes binarias desde un servicio de OData que almacene fotos, vea la entrada de blog relacionada con la parte 2 del programa de proveedores de transmisión por secuencias de servicios de datos: acceso a un flujo de recursos multimedia desde el cliente.

Metadatos de entidad Una entidad que tenga un flujo de recursos multimedia relacionado se indica en los metadatos del servicio de datos mediante el atributo HasStream aplicado a un tipo de entidad que sea la entrada de vínculo multimedia. En el siguiente ejemplo, la entidad PhotoInfo es una entrada de vínculo multimedia con un recurso multimedia relacionado, indicado por el atributo HasStream.

MCT: Luis Dueñas

Pag 126 de 128

WCF Data Service











En el resto de los ejemplos de este tema se muestra cómo acceder y cambiar el flujo de recursos multimedia.

Acceder al flujo de recursos binarios La biblioteca cliente de WCF Data Services proporciona métodos para acceder a flujos de recursos binarios desde un servicio de datos basado en OData . Cuando se descarga un recurso multimedia, puede usar el URI de dicho recurso u obtener un flujo binario que contenga los datos del propio recurso multimedia. También puede cargar los datos del recurso multimedia como flujo binario.

Obtener el URI del flujo binario Cuando se recuperan ciertos tipos de recursos multimedia, como imágenes y otros archivos multimedia, suele ser más fácil usar el URI del recurso multimedia de la aplicación que administrar el propio flujo de datos binarios. Para obtener el URI de un flujo de recursos asociado con una entrada de vínculo multimedia determinada, debe llamar al método GetReadStreamUri de la instancia de la clase DataServiceContext que esté realizando el seguimiento de la entidad. En el ejemplo siguiente se muestra cómo llamar al método GetReadStreamUri para obtener el URI de un flujo de recursos multimedia que se use con el fin de crear una nueva imagen en el cliente: ' Use the ReadStreamUri of the Media Resource for selected PhotoInfo object ' as the URI source of a new bitmap image. photoImage.Source = New BitmapImage(context.GetReadStreamUri(currentPhoto))

Descargar el flujo de recursos binarios Al recuperar un flujo de recursos multimedia, es necesario llamar al método GetReadStream de la instancia de la clase DataServiceContext que esté realizando el seguimiento de la entrada de vínculo multimedia. Este método envía una solicitud al servicio de datos que devuelve un objeto DataServiceStreamResponse, que tiene una referencia al flujo que contiene el recurso. Use este método cuando la aplicación necesite el recurso binario como clase Stream. En el ejemplo siguiente se muestra cómo llamar al método GetReadStream para recuperar un flujo que se use con el fin de crear una nueva imagen en el cliente: Nota: El servicio de datos no establece el encabezado de longitud de contenido del mensaje de respuesta que contiene el flujo binario. Este valor puede no reflejar la longitud real del flujo de datos binarios.

Cargar un recurso multimedia como flujo

MCT: Luis Dueñas

Pag 127 de 128

WCF Data Service Para insertar o actualizar un recurso multimedia, llame al método SetSaveStream de la instancia de la clase DataServiceContext que esté realizando el seguimiento de la entidad. Este método envía una solicitud al servicio de datos que contiene el recurso multimedia leído desde el flujo proporcionado. En el siguiente ejemplo se muestra cómo llamar al método SetSaveStream para enviar una imagen al servicio de datos: ' Set the file stream as the source of binary stream to send to the data service. The Slug header is the file name and ' the content type is determined from the file extension. A value of 'true' means that the stream is closed by the client when ' the upload is complete. context.SetSaveStream(photoEntity, imageStream, True, photoEntity.ContentType, photoEntity.FileName)

En este ejemplo, se llama al método SetSaveStream proporcionando un valor true para el parámetro closeStream. De esta forma, se garantiza que la clase DataServiceContext cierre el flujo una vez cargados los datos binarios en el servicio de datos. Nota: Cuando llame al método SetSaveStream, el flujo no se envía al servicio de datos hasta que se llame al método SaveChanges.

MCT: Luis Dueñas

Pag 128 de 128