Biblioteca cliente de .NET (marco de trabajo de los servicios de datos de ADO.NET)

Los servicios de datos de ADO.NET incluyen la biblioteca cliente de .NET para aplicaciones que usen .NET Framework y los servicios de datos de ADO.NET. Las aplicaciones que usen esta biblioteca trabajan con los resultados del servicio de datos como objetos .NET. La exploración transversal la administran los objetos Common Language Runtime (CLR).

La biblioteca cliente de .NET usa HTTP y el formato AtomPub. Esta biblioteca trabaja bien en redes corporativas y en entornos de Internet. Todo lo que se necesita es la conectividad en el nivel HTTP al servicio de datos, directamente o mediante proxies.

System.Data.Services.Client

Para usar la biblioteca cliente, agregue una referencia al ensamblado System.Data.Services.Client al proyecto. La biblioteca cliente se puede usar desde cualquier tipo de proyecto, incluidos los proyectos de formularios Windows Forms, Windows Presentation Foundation y de sitios web.

Las dos construcciones principales de la biblioteca cliente son las clases DataServiceContext y DataServiceQuery. DataServiceContext representa el contexto en tiempo de ejecución con un servicio de datos especificado. Aunque los servicios de datos no tenga estado, el contexto sí los tiene y el estado del cliente se mantiene entre interacciones para la compatibilidad de características como la administración de cambios.

La clase DataServiceQuery representa una consulta en el almacén especificado mediante la sintaxis de los URI de los servicios de datos de ADO.NET. Para ejecutar una consulta y obtener los resultados con el formato de objetos .NET, enumere el objeto de la consulta, por ejemplo, mediante la construcción de C# foreach o For Each de Visual Basic.

Para representar cada una de las entidades definidas en el servicio de datos como objetos .NET, se deben definir las clases correspondientes para la aplicación cliente. Una opción es definir las clases manualmente y la otra, la herramienta DataSvcUtil.exe que se explica en el próximo encabezado.

El ejemplo siguiente muestra una definición manuscrita para la clase Address basada en los datos de la base de datos de ejemplo AdventureWorks incluida en SQL Server 2005. El ejemplo siguiente usa la entidad Address y un pequeño segmento de código que ejecuta una consulta en el servicio. Las propiedades del elemento Address devuelto se muestran como resultado.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.Services.Client;


namespace DataServiceClient
{

    public class Address
    {
        public int AddressID { get; set; }
        public string AddressLine1 { get; set; }
        public string AddressLine2 { get; set; }
        public string City { get; set; }
        public DateTime ModifiedDate { get; set; }
        public string PostalCode { get; set; }
        public Guid rowguid { get; set; }       
        public int StateProvinceID { get; set; }

    }

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }       


        private void button_Click(object sender, EventArgs e)
        {
            DataServiceContext ctx = new
                 DataServiceContext(new Uri("http://localhost:1492/AdvWksSales.svc/"));


          // This example expects the user to enter
             // an integer representing AddressID in textBox1.

             DataServiceQuery<Address> query =
                 ctx.CreateQuery<Address>(
                   "SalesOrderHeader(45678)/Address");

             StringBuilder output = new StringBuilder();

             foreach (Address address in query)
             {
                output.Append("Id: " + address.AddressID +
                        " Line 1: " + address.AddressLine1 + "\r\n");

             }

            richTextBox1.Text = output.ToString();



        }

El resultado se muestra a continuación.

Id: 70 Address Line1: 1792 Belmont Rd. City: Monroe

Utilizar la herramienta DataSvcUtil

El enfoque de escribir manualmente las clases funciona para un número reducido de tipos. Sin embargo, cuando el esquema del servicio de datos es más complejo, el número y el tamaño de las clases que se van a mantener se convierte en demasiado complejo para el mantenimiento manual. Una opción mejor es usar la herramienta de generación de código denominada DataSvcUtil.exe que se incluye en los servicios de datos de ADO.NET. La herramienta genera las clases .NET a partir de la definición de servicio de datos.

El archivo DataSvcUtil.exe se encuentra en el directorio \WINDOWS\Microsoft.NET\Framework\v3.5\. La línea de comandos toma un argumento: la URL base a los servicios de datos para los que se van a generar tipos. Por ejemplo, si se ejecuta el servicio Northwind en el servidor de desarrollo de Visual Studio en http://localhost:1234/Northwind.svc, la línea de comandos para generar las clases es:

C:\Program Files\Microsoft Visual Studio 9.0\VC>"C:\WINDOWS\Microsoft.NET\Framework\v3.5\DataSvcUtil.exe"
 /out:c:\northwind.cs /uri:http://localhost:1365/Northwind.svc

El resultado del comando es un archivo de C# (los tipos de Visual Basic se pueden generar mediante el modificador /language:VB) que contiene una clase para cada uno de los tipos de entidades del servicio de datos.

Las clases generadas tienen miembros que representan valores y asociaciones primitivos que facilitan la navegación por el modelo de objetos.

Servicios de datos LINQ to ADO.NET

La biblioteca cliente de .NET admite consultas del servicio de datos mediante consultas LINQ, además de consultar un servicio de datos en una llamada a DataServiceContext.CreateQuery como se muestra debajo del encabezado Microsoft Data Web Client. Para obtener más información sobre LINQ, vea LINQ to Entities. La biblioteca cliente controla los detalles de la asignación de la instrucción de LINQ a un URI del servicio de datos de destino y la recuperación de los recursos especificados como objetos .NET. El siguiente ejemplo muestra cómo recuperar todos los clientes de la ciudad de Londres con el conjunto de resultados devuelto ordenado por nombre de compañía.

using System;
using System.Data.Services.Client;
using System.Linq;
using NorthwindModel;

namespace TestApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            NorthwindEntities ctx = new 
            NorthwindEntities(
              new Uri("http://localhost:1365/Northwind.svc"));

            var q = from c in ctx.Customers 
                    where c.City == "London"
                    orderby c.CompanyName
                    select c;

            foreach (var cust in q)
            {
                Console.WriteLine(cust.CompanyName);
            }
        }
    }
}
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 utilizada por los servicios de datos. Se producirá una excepción si la consulta no se puede asignar a un URI del servicio de datos de destino. Para obtener más información acerca de REST, vea Servicios y semántica de REST (marco de trabajo de servicios de datos de ADO.NET).

Nota

Los ejemplos de este encabezado y de los dos encabezados siguientes utilizan la base de datos de ejemplo Northwind. El ejemplo de Northwind se puede descargar de: http://go.microsoft.com/fwlink/?linkid=24758.

Asociaciones

El seguimiento y la administración de las asociaciones entre objetos los realiza la clase DataServiceContext. Puede cargar objetos asociados "rápidamente" o en función de las necesidades. Las cargas "rápida" y "lenta" se describen en la documentación de ADO.NET Entity Framework en Querying Data as Objects and Shaping Query Results. Los formatos URL se describen en Esquema de direccionamiento simple de datos con identificadores uniformes de recursos (URI) (marco de trabajo de los servicios de datos de ADO.NET).

Para cargar las entidades asociadas según las necesidades, se usa el método LoadProperty de la clase DataServiceContext. El ejemplo siguiente muestra cómo retrasar la carga de las entidades Product asociadas con las entidades Category.

using System;
using System.Data.Services.Client;
using NorthwindModel;

namespace TestApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            DataServiceContext ctx = new
              DataServiceContext(
                  new Uri("http://localhost:1365/Northwind.svc"));

            DataServiceQuery<Categories> categories = 
                       ctx.CreateQuery<Categories>("/Categories");

            foreach (Categories c in categories)
            {
                 Console.WriteLine(c.CategoryName);

                 ctx.LoadProperty(c, "Products");

                 foreach (Products p in c.Products)
                 {
                    Console.WriteLine("\t" + p.ProductName);
                 }
              }
            }
        }
    }
}

Puede que necesite objetos asociados en algunos escenarios y desee evitar la latencia agregada de una solicitud más para recuperarlos. En este caso, puede usar la opción expand de la URL. La biblioteca cliente reconoce que los resultados incluyen entidades tanto de nivel superior como asociadas y las materializa como gráfico de objetos. Este proceso se denomina carga rápida. La carga rápida es parecida al ejemplo anterior pero carga los productos relacionados en un único ciclo de ida y vuelta al servicio de datos.

El ejemplo siguiente muestra la opción de expansión que incluye los productos relacionados con categorías.

using System;
using System.Data.Services.Client;
using NorthwindModel;

namespace TestApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            DataServiceContext ctx = new 
               DataServiceContext(
                      new Uri("http://localhost:1365/Northwind.svc"));

            // get a single category
            DataServiceQuery<Categories> categories = 
        ctx.CreateQuery<Categories>("/Categories(1)?$expand=Products");

            foreach (Categories c in categories)
            {
                //Console.WriteLine(c.CategoryName);
                richTextBox1.Text = c.CategoryName;

                foreach (Products p in c.Products)
                {
                    //Console.WriteLine("\t" + p.ProductName);
                    richTextBox1.Text = richTextBox1.Text + 
                                    "\r\n\t" + p.ProductName ;
                }
            }
        }
    }
}

Compatibilidad de actualizaciones

Para crear una nueva instancia del servicio de datos, cree el objeto de .NET y, a continuación, llame a AddObject de la instancia de DataServiceContext que se use, pasando el objeto y el conjunto de entidades de destino, como se muestra en el siguiente fragmento de código.

using System;
using Microsoft.Data.WebClient;
using NorthwindModel;

namespace TestApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            DataServiceContext ctx = new
              DataServiceContext(
                  new Uri("http://localhost:1365/Northwind.svc"));

            Categories cNew = new Categories();
            cNew.CategoryName = "NewCategory1";
            cNew.Description = "Add Item Test.";

            ctx.AddObject("Categories",cNew);
            ctx.SaveChanges();
        }
    }
}

Después de crear o modificar una entidad del servicio de datos, éste devuelve una copia actualizada de la entidad, incluidos los valores de propiedad que puedan haberse actualizado como resultado de los desencadenadores de la base de datos o claves generadas automáticamente. La biblioteca cliente actualiza automáticamente el objeto de .NET con estos nuevos valores.

Para modificar una instancia de entidad existente, primero ejecute una consulta de ese objeto, efectúe los cambios deseados y, a continuación, llame al método UpdateObject para indicar a la biblioteca cliente que debe enviar una actualización para ese objeto.

Nota

El siguiente ejemplo usa una clase de contexto NorthwindEntities creada por la herramienta de generación de código descrita anteriormente y se deriva de DataServiceContext. Esta clase derivada proporciona propiedades específicas del servicio para simplificar su código.

El siguiente ejemplo muestra la sintaxis de actualización mediante el método UpdateObject.

using System;
using Microsoft.Data.WebClient;
using System.Linq;
using NorthwindModel;

namespace TestApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            NorthwindEntities ctxN =
               new NorthwindEntities(
                     new Uri("http://localhost:1365/Northwind.svc"));

            var c1 = (from c in ctxN.Categories
                     where c.CategoryName == "NewCategory1"
                     select c).First();

            c1.CategoryName = "UpdatedCategory";

            //ctxN.AttachTo("Categories", c1);
            ctxN.UpdateObject(c1);
            ctxN.SaveChanges();
        }
    }
}

Para eliminar una instancia de entidad, llame al método Delete del objeto DataServiceContext.

El seguimiento de los cambios se realiza en la instancia de DataServiceContext, pero los cambios no se envían al servidor inmediatamente. Una vez que haya efectuado los cambios necesarios de una actividad especificada, llame a SaveChanges para enviar todos los cambios al servicio de datos.

Actualizaciones de asociación

La infraestructura de actualización también puede administrar los cambios de asociación. Se pueden cambiar las asociaciones entre los objetos de .NET y que la biblioteca cliente los refleje como operaciones de creación o eliminación de asociaciones de la interfaz HTTP. Para ilustrar este punto, el siguiente ejemplo crea un elemento Product en la base de datos Northwind y lo asocia con un elemento Category existente. Las categorías y los productos participan en una asociación uno a varios; por lo tanto, un producto especificado tiene una categoría específica. El código siguiente muestra el uso del método Add para agregar una asociación Product a Category.

using System;
using Microsoft.Data.WebClient;
using System.Linq;
using NorthwindModel;

namespace TestApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            NorthwindEntities ctx = new 
             NorthwindEntities(new Uri(http://localhost:1234/Northwind.svc));

            var c1 = (from c in ctx.Categories
                     where c.CategoryName == "UpdatedCategory"
                     select c).First();

            Products p = new Products();
            p.ProductName = "TestProduct";
            p.Discontinued = false;
            p.QuantityPerUnit = "1";
            p.ReorderLevel = 100;
            p.UnitPrice = 1.1M;
            p.UnitsInStock = 200;
            p.UnitsOnOrder = 0;
            ctx.AddObject("Products", p);

            // Add binding between product and category
            p.Categories = c1;
            ctx.SetLink(p, "Categories", c1);

            ctx.SaveChanges();

            Console.ReadKey();
        }
    }

La administración de modificaciones en gráficos de objetos arbitrarios con asociaciones bidireccionales es un problema complejo. Existen bibliotecas avanzadas como Entity Framework de ADO.NET que proporcionan administradores de estado muy eficaces y coherentes para el control de gráficos parcialmente materializados. La biblioteca cliente de ADO.NET, por otra parte, está diseñada para una superficie mínima y proporciona sólo las primitivas necesarias con el fin de habilitar las operaciones de servicio de datos de asignación en objetos .NET.

El siguiente gráfico muestra entidades de origen y destino, así como entidades que requieren diversos métodos de actualización de asociaciones. Siga los ejes horizontal y vertical para encontrar métodos válidos que llamar.

Destino →

Origen ↓

NULL

Agregado

Modificado

Eliminado

Sin cambios

Agregado

SetLink

AddLink

SetLink

AddLink

SetLink

no disponible

AddLink

SetLink

Modificado

SetLink

AddLink

SetLink

AddLink

AttachLink

DeleteLink

SetLink

DeleteLink

AddLink

AttachLink

DeleteLink

SetLink

Eliminado

SetLink

no disponible

DeleteLink

DeleteLink

DeleteLink

Sin cambios

SetLink

AddLink

AddLink

AttachLink

DeleteLink

SetLink

DeleteLink

AddLink

AttachLink

DeleteLink

SetLink

AttachLink sólo funciona si tanto el origen como el destino tienen el estado Sin cambios. AttachLink no mantiene las relaciones. No use AddLink a menos que el resultado deseado sea que una llamada a SavingChanges no guarde el vínculo.

Si dispone de una relación entre Products y Catagories y la propiedad Products.Categories es una colección, utilice objCtx.AddLink(prod, "Categories", c1);

Si Products.Cateories es sólo una referencia, utilice objCtx.SetLink(prod, "Categories", c1);

Durante SaveChanges en una entidad de origen en el estado Added, HTTP POST incluye vínculos a otras entidades cuyo estado es Modificado o Sin cambios.

El método AttachLink crea un vínculo en el estado EntityStates.Unchanged. Éste es un mecanismo para que los usuarios creen un vínculo, cuyo seguimiento lo realiza el contexto, que no se enviará al servidor.

Autenticación de la biblioteca cliente

La biblioteca cliente de .NET utiliza la compatibilidad de .NET Framework para el protocolo HTTP. Entre otras cosas, la infraestructura del marco de trabajo controla los esquemas de autenticación en HTTP automáticamente cuando se proporciona con un conjunto de credenciales.

De forma predeterminada, la biblioteca cliente no proporciona credenciales a la pila HTTP. Sin embargo, puede establecer la propiedad Credentials en DataServiceContext para señalar un objeto que implemente la interfaz ICredentials. Para obtener más información sobre credenciales, vea WebRequest.Credentials.

Interacciones asincrónicas con el servicio de datos

Las aplicaciones web se deben diseñar para hospedar una latencia superior entre cliente y servidor que las aplicaciones que se ejecutan en redes internas. El uso de interacciones asincrónicas con el servidor ayuda a mantener una interfaz de usuario sensible mientras la aplicación espera una respuesta del servidor.

En esta versión, la biblioteca cliente de ADO.NET admite el modo de funcionamiento asincrónico para muchas de las operaciones disponibles de la clase DataServiceContext, como cambios de recuperación y guardado. El ejemplo siguiente muestra el uso de la API asincrónica de la biblioteca cliente en código.

using System;
using Microsoft.Data.WebClient;
using System.Linq;
using NorthwindModel;

namespace TestApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            NorthwindEntities ctx = new 
                NorthwindEntities(new Uri(http://localhost:51905/nw.svc));

            DataServiceQuery<Customers> q = ctx.Customers;

            q.BeginExecute(
                    delegate(IAsyncResult ar)
                    {
                        foreach (Customers c in q.EndExecute(ar))
                        {
                            Console.WriteLine(c.CompanyName);
                        }
                    },
                    null);


            Console.ReadKey();
        }
    }
}

Vea también

Conceptos

HttpWebRequest GET (marco de trabajo de los servicios de datos de ADO.NET)
Crear servicios de datos de ADO.NET

Otros recursos

Entity Data Model