Version imprimable       Envoyer     
Cliquez pour évaluer et commenter
Related Articles

Les développeurs se battent souvent avec les versions des flux de travail et leurs classes associées. Matt Milner aborde les principaux problèmes liés au contrôle des versions de flux de travail et fournit des recommandations pour apporter des modifications aux définitions, activités et services des flux de travail.

Matthew Milner

MSDN Magazine Mai 2009

...

Read more!

Udi Dahan explique comment son équipe a identifié et surmonté des problèmes imprévus tout en développant une application commerciale software + services à grande échelle.

Udi Dahan

MSDN Magazine Avril 2009

...

Read more!

Écoutez une conversation entre un développeur et un professionnel de la sécurité qui a investigué sur certaines exigences majeures en matière de cycle de vie du développement de la sécurité (SDL, Security Development Lifecycle) que nous imposons à nos équipes produit ici chez Microsoft.

Michael Howard

MSDN Magazine Mai 2009

...

Read more!

Microsoft Velocity présente un cache mémoire distribué et unifié pour la consommation des applications clientes. Nous allons vous montrer comment ajouter Velocity à vos applications pilotées par des données.

Aaron Dunnington

MSDN Magazine Juin 2009

...

Read more!

Danny Simmons se penche sur certaines solutions inefficaces dont vous devez avoir conscience au moment de créer des applications à plusieurs niveaux à l'aide d'Entity Framework.

Daniel Simmons

MSDN Magazine Juin 2009

...

Read more!

Popular Articles

Vous pouvez désormais exécuter une analyse de texte efficace et élaborée à l'aide d'expressions régulières dans SQL Server 2005.

David Banister

MSDN Magazine February 2007

...

Read more!

C# allows developers to embed XML comments into their source files-a useful facility, especially when more than one programmer is working on the same code. The C# parser can expand these XML tags to provide additional information and export them to an external document for further processing. This article shows how to use XML comments and explains the relevant tags. The author demonstrates how to set up your project to export your XML comments into convenient documentation for the benefit of other developers. He also shows how to use comments ...

Read more!

Comprenez pourquoi des événements et des commandes routés Windows Presentation Foundation constituent la base de la communication entre les différentes parties de votre interface utilisateur.

Brian Noyes

MSDN Magazine Septembre 2008

...

Read more!

Chris Tavares explique comment le modèle Model View Controller de l'infrastructure ASP.NET MVC vous aide à créer des applications Web flexibles et faciles à tester.

Chris Tavares

MSDN Magazine March 2008

...

Read more!

Un gadget Sidebar est un petit outil puissant qui est étonnamment facile à créer. Découvrez les gadgets avec Donavon West.

Donavon West

MSDN Magazine August 2007

...

Read more!

Data 2.0
Exposition et utilisation de données dans le monde des services Web
Elisa et Mike Flasko
Téléchargement de code disponible ici : DataServices2008_08.exe (225 Ko)
Parcourez le code en ligne

Cet article fait référence à une version préliminaire d'ASP.NET 3.5 et de Microsoft AJAX Library. Toutes les informations contenues dans le présent document peuvent faire l’objet de modifications.
Cet article aborde les sujets suivants :
  • Le sens des services de données
  • Exposition et utilisation de données
  • Description de données avec le modèle de données d'entité
  • Sécurité des données
Cet article utilise les technologies suivantes :
ADO.NET Data Services, LINQ, le modèle de données d'entité
Rappelez-vous de la dernière application Internet riche (RIA) que vous avez créée. Comment obteniez-vous vos données ? Comment sépariez-vous ces données des informations de présentation et d'interface utilisateur envoyées au navigateur ? Et s'il existait une méthode plus simple pour faire tout cela ?
La séparation de la présentation et des données n'est pas une idée neuve, mais avec la popularité croissante des technologies RIA comme AJAX et Silverlight™, elle est devenue beaucoup plus courante. Ces technologies sont basées sur le concept de séparation de la présentation et des données pour offrir une application plus interactive et réactive.
Par exemple, les applications RIA basées sur Silverlight précompilent le code qui régit la présentation et est déployé au client via le serveur Web. Alors, après avoir atteint le navigateur Web, le code effectue un rappel à un serveur Web pour récupérer les données à afficher dans l'interface utilisateur. Les technologies comme celles-ci éliminent généralement l'option d'un processus de rendu côté serveur qui mélangerait les données et le code de présentation.
En plus de séparer la présentation et les données pour favoriser des expériences Web plus interactives et plus riches, il y a une tendance sur le Web à exposer et à utiliser des données autonomes, indépendantes de toute interface utilisateur. La prolifération d'applications orientées données comme les applications Web hybrides indique que la grande disponibilité de données intéressantes, facilement consommables permet de nouveaux scénarios d'application.
Basé sur l'observation de ces tendances, ADO.NET Data Services Framework a commencé comme méthode pour aider les développeurs cherchant à exposer et à utiliser des données via les services de leurs applications RIA. Lors de l'exploration de cet espace, deux idées clés sont apparues : créer des bibliothèques clientes et des outils universels avec les approches actuelles de services centrés sur les données est un concept difficile en lui-même et la création et la maintenance de ces services nécessitent un investissement significatif de la part du développeur. Dans cet article, nous décrirons les services de données et aborderons quelques unes des fonctionnalités clés. Si vous souhaitez plus de détails sur certaines idées relatives aux services de données, reportez-vous au message de blog intitulé « Pourquoi ADO.NET Data Services ? » sur le blog de l'équipe (go.microsoft.com/fwlink/?LinkId=120530).
En général, l'objectif d'ADO.NET Data Services Framework est de créer une infrastructure simple basée sur Representational State Transfer (REST) pour exposer et utiliser des services centrés sur les données. De tels services exposent des données en utilisant une interface uniforme devant être utilisée par les clients Web via un intranet d'entreprise ou Internet. L'infrastructure consiste en une bibliothèque de serveur pour exposer les données de manière sécurisée en tant que service et une série de bibliothèques clientes créées pour différentes applications et technologies Microsoft (Microsoft® .NET Framework, Silverlight, etc.) pour utiliser des services. La figure 1 illustre l'architecture.
Figure 1 L'architecture d'ADO.NET Data Services Framework (cliquer sur l'image pour l'agrandir)

Description des données avec le modèle de données d'entité
Chaque service ADO.NET Data Service est décrit en termes de modèle de données d'entité en CSDL (Conceptual Schema Definition Language). Le modèle de données d'entité utilise deux concepts primaires, les entités et les associations (ou relations). Les entités sont des instances de Types d'entité (comme Client ou Employé) qui sont des enregistrements structurés avec une clé. Une clé d'entité est constituée d'un sous-ensemble des propriétés du type d'entité. La clé (Customer­Id et OrderId, respectivement) identifie de manière unique et autorise des mises à jour des instances d'entité et leur permet également de participer dans des relations. Les entités sont groupées par EntitySets (Customers est un ensemble d'instances Customer). Les associations forment des liens entre deux Types d'entité ou plus (comme Employee WorksFor Department).
Pourquoi le modèle de données d'entité a-t-il été choisi comme langage de description de données pour ADO.NET Data Services ? Il se trouve que le modèle de données d'entité s'adapte très bien aux principaux concepts Web de ressources et aux liens, ce qui en fait un candidat idéal (entités pour les ressources et associations pour les liens).
Le modèle de données d'entité a été développé avec l'objectif de devenir le modèle de données fondamental sur une suite de technologies de développeur et de serveur. Avec seulement un modèle de données à maintenir à travers de nombreuses applications, la maintenance d'application est simplifiée. Le modèle de données d'entité pourrait être utilisé pour définir un modèle non seulement pour les applications personnalisées créées sous ADO.NET Data Services, mais également comme entrées aux applications de rapport et de visualisation, aux applications de portail intranet ou aux applications de flux de travail. ADO.NET Data Services est la deuxième technologie de Microsoft s'appuyant sur le concept de modèle de données d'entité (la première, bien sûr, était ADO.NET Entity Framework). Pour plus d'informations sur le modèle de données d'entité et ADO.NET Entity Framework, reportez-vous à msdn.microsoft.com/data et « ADO.NET : Obtenez une modélisation de données flexibles grâce à Entity Framework » dans l'édition de juillet 2008 de MSDN® Magazine à l'adresse msdn.microsoft.com/magazine/700331.

Données relationnelles
Par défaut, ADO.NET Data Services travaille étroitement avec ADO.NET Entity Framework pour simplifier le processus de connexion et d'exposition d'un modèle de données par-dessus les données enregistrées dans SQL Server® ou d'autres bases de données tierces (Oracle, DB2, MySQL, pour n'en nommer que quelques-uns).
Entity Framework élève le niveau d'abstraction auquel les développeurs travaillent avec les données, ce qui signifie qu'au lieu de coder sur des lignes et des colonnes, un modèle conceptuel plus élevé est défini (tel qu'un Modèle de données d'entité) par-dessus les données relationnelles et l'application est alors programmée dans les termes de ce modèle. L'application a seulement besoin de comprendre les données dans les formes logiques pour elle et ces formes sont exprimées à l'aide d'un vocabulaire riche qui couvre des concepts comme l'héritage, les types complexes et les relations explicites.
En général, ADO.NET Data Services convertit une requête pour exécuter une opération via une ressource (comme une opération de requête HTTP sur un URI) sur l'opération équivalente sur le modèle de données que la ressource représente. Dans ce cas, puisque le modèle de données est soutenu par une base de données relationnelle, les URI sont convertis en appels de méthode Entity Framework Object Services.
Un modèle de données existant, créé en utilisant Entity Framework, peut être exposé en seulement quelques étapes dans un projet d'application Web Visual Studio® 2008 ASP.NET SP1. (Les autres types de projet et mécanismes d'hébergement sont également pris en charge, comme Windows® Communication Foundation, ou WCF, WebServiceHost.) Un modèle de données d'entité ADO.NET est d'abord créé ou importé dans une application Web ASP.NET. Avec le modèle disponible, nous pouvons alors ajouter un nouveau service de données ADO.NET. L'assistant Ajouter un nouvel élément produira un service de base et l'affichera dans l'Explorateur de solutions. Nous pouvons alors connecter ce service au modèle en modifiant la définition de classe, comme indiqué ici :
using NorthwindModel;

public class NorthwindService : DataService <NorthwindEntities>
{
     public static void InitializeService(IDataServiceConfiguration config)
     {

     }

}
En outre, puisque le service est verrouillé par défaut au début, nous devons travailler un peu pour définir la stratégie de sécurité à l'échelle du service pour permettre l'utilisation du service. Le code suivant définit la sécurité au niveau EntitySet lorsque le service est initialisé :
public static void InitializeService(IDataServiceConfiguration config)
{
    config.SetEntitySetAccessRule("*", EntitySetRights.All);
}
Comme vous pouvez le voir, c'est un exemple très simple de définition de la sécurité pour un service de données ADO.NET. Les autres méthodes et considérations de sécurité seront abordées plus tard.
À ce stade, nous avons un service de données ADO.NET opérationnel que nous pouvons créer et exécuter.
Une façon simple de tester votre service sans application cliente consiste juste à pointer un navigateur Web au point d'entrée du service, par exemple <host>/<vdir>/service.svc. Le point d'entrée du service retournera la réponse sous forme de XML contenant la liste d'EntitySets exposé par le service de données. Pour afficher Atom (le format par défaut retourné par un service de données ADO.NET) dans Internet Explorer®, vous devez d'abord vous assurer que la Lecture du flux dans Internet Explorer est désactivée. Ceci est possible de l'onglet Contenu de Outils | Options Internet.

Qu'en est-il des données non relationnelles ?
Pour toutes les autres sources de données ou pour utiliser les technologies d'accès de base de données supplémentaires (comme LINQ to SQL), un mécanisme permet d'exposer n'importe quelle source de données en tant que service de données ADO.NET en utilisant un modèle de plug-in.
Dans ce cas, les URI sont convertis en requêtes LINQ. Les services de données autorisent cette approche en mappant des objets qui implémentent l'interface IQueryable sur EntitySets. Ceci signifie que ADO.NET Data Services peut exposer n'importe quelle source de données pour laquelle est écrit un fournisseur IQueryable. Pour activer ceci, ADO.NET Data Services définit un mappage entre les objets CLR et les artefacts du modèle de données basé sur le modèle de données d'entité.
Bientôt, nous allons décrire étape par étape la création d'un service de données ADO.NET en nous basant sur un graphique d'objet CLR simple. Les étapes que nous illustrerons produisent un service de données créé sur une collection en mémoire d'objets. Dans un déploiement typique de production, cependant, les propriétés IQueryable affichées, représentant EntitySets n'exposeraient pas de données en mémoire, mais traduiraient plutôt les arborescences d'expression IQueryable en requêtes spécifiques à la source de données. Par exemple, LINQ to Entities traduit les arborescences d'expression IQueryable en instructions SQL.
Lorsque vous créez un service de données sur un magasin non-relationnel, le service de données est de nouveau créé dans un projet d'application Web ASP.NET. Notez cependant que cette fois, nous ignorons l'étape utilisée pour créer le modèle de données d'entité ADO.NET et nous démarrons en ajoutant un nouveau service de données ADO.NET. L'assistant Ajouter un nouvel élément produira un service de base et l'affichera dans l'Explorateur de solutions. Pour continuer avec cet exemple simple, nous devons maintenant créer les données en mémoire que nous avons l'intention d'exposer via notre service. Pour cela nous créons trois classes, dont deux sont User et Contact, où chaque User aura une série de Contacts. La troisième classe est une classe MyDataService qui expose deux propriétés publiques (Contacts et Users) comme IQueryable<Contact> et IQueryable<User>, comme indiqué à la figure 2.
public class User
{
     public int ID { get; set; }
     public string Name { get; set; }
     public IList<Contact> Contacts { get; set; }
}

public class Contact
{
     public int ID { get; set; }
     public string Name { get; set; }
     public string Email { get; set; }
}

public class MyDataService
{
     static User[] _users;
     static Contact[] _contacts;

     static MyDataService()
     {
        _users = new User[]{ new User{ID=1, Name="Mike"},
                 new User{ID=2, Name="Elisa"} };

         _contacts = new Contact[]{ new Contact{ID=1, Name="Joe", 
                              Email="Joe@contoso.com"},
                  new Contact{ID=2, Name="Bob", Email="Bob@contoso.com"},
                  new Contact{ID=3, Name="Sam", Email="Sam@contoso.com"},
                  new Contact{ID=4, Name="Carl", Email="Carl@contoso.com"}, 
                  new Contact{ID=5, Name="Abby", Email="Abby@contoso.com"},
                  new Contact{ID=6, Name="Annie", Email="Annie@contoso.com"},
                  };

         _users[0].Contacts = new List<Contact>();
         _users[0].Contacts.Add(_contacts[0]);
         _users[0].Contacts.Add(_contacts[1]);
         _users[0].Contacts.Add(_contacts[4]);

         _users[1].Contacts = new List<Contact>();
         _users[1].Contacts.Add(_contacts[2]);
         _users[1].Contacts.Add(_contacts[3]);
         _users[1].Contacts.Add(_contacts[5]);
    }

    public IQueryable<User> Users
    {
         get { return _users.AsQueryable<User>();}
    }

    public IQueryable<Contact> Contacts
    {
         get { return _contacts.AsQueryable<Contact>(); }
    }
}
Maintenant que nous avons déterminé une source de données, nous retournons en territoire familier, en modifiant la définition de classe pour qu'elle pointe à notre source de données, comme ici :
public class contacts : 
  DataService<ADONETDataServiceNonRelSample.MyDataService>
{
    public static void InitializeService(IDataServiceConfiguration config)
    {
          config.SetEntitySetAccessRule("*", EntitySetRights.All);
    }
}
Encore une fois, nous devons réaliser une certaine quantité de travail initial pour définir la stratégie de sécurité, puisque le service initial est verrouillé par défaut. Le code présente le même ensemble de paramètres de stratégie de sécurité au niveau d'EntitySet, comme nous l'avons vu plus tôt. À ce stade, nous avons de nouveau un service de données ADO.NET opérationnel que nous pouvons créer et exécuter. Le test du service peut être réalisé en utilisant simplement Internet Explorer pour naviguer au point de terminaison de service.

Format URI uniforme
L'URI (Uniform Resource Identifier) uniforme est l'un des concepts primaires formant ADO.NET Data Services, il définit un format d'URI relativement simple, pourtant expressif, permettant aux applications d'interroger des ensembles d'entités ou des entités seules, de même que de parcourir les relations existant entre ces entités. La structure de cet URI permet aux agents et aux contrôles de comprendre facilement comment naviguer dans les données présentées par un service.
Lorsque nous avons commencé le test de nos services initiaux, nous avons vu le format URI fondamental pointant au service de données lui-même. En nous basant sur cet URI fondamental, nous pouvons interroger le service en utilisant le format suivant, comme ceci :
http://<host>/<vdir>/<service.svc>/<EntitySet>[(<Key>)
[/<NavigationProperty>[(<Key>)/...]]] 
Par exemple, pour continuer avec notre exemple Northwind précédent, si nous ajoutons /Customers à la fin de l'URI de service de données, <host>/<vdir>/NorthwindService.svc/Customers, nous retournons tous les clients dans l'ensemble d'entités clients, dans ce cas tous les Customers dans la base de données Northwind. Si, d'autre part, nous voulons retourner une seule entité Customer du service Northwind, nous pourrions utiliser la valeur clé de l'entité, puisque Customer est une entité à clé unique. En demandant l'URI <host>/<vdir>/NorthwindService.svc/­Customers('ALFKI'), nous retournons le seul Client avec la clé « ALFKI ». La propriété de navigation est ajoutée à un URI de requête d'entité seul d'une manière similaire. En ajoutant /Customers('ALFKI')/Orders, nous parcourons la relation dans notre modèle entre les Clients et les Commandes, retournant toutes les Commandes associées au Client identifié par la clé « ALFKI ».
Le format d'URI décrit ci-dessus autorise la requête fondamentale et le parcours des entités de notre magasin, mais il ne tient pas compte de la nécessité de contrôler davantage la sortie de requêtes ou d'imposer des contraintes sur elles. Pour atteindre ces objectifs, nous utiliserons une série de paramètres de chaîne de requête facultatifs pris en charge par ADO.NET Data Services, notamment $filter, $expand, $orderby, $skip et $top. Les paramètres de chaîne de requête sont ajoutés à l'URI suivant le caractère ?.
Par exemple, pour interroger tous les clients à Londres, nous ajouterions /Customers?$filter=City eq 'London' à l'URI de service. Pour interroger un client spécifique (avec la clé « ALFKI ») et récupérer les commandes de ventes apparentées, nous ajoutons /Customers('ALFKI')?$expand=Orders à l'URI de service. Pour trier des résultats par Ville dans l'ordre croissant (asc), nous ajoutons /Customers?$orderby=City asc l'URI de service (ou desc pour un ordre décroissant).
Pour une liste complète des formats URI pris en charge par ADO.NET Data Services, consultez la documentation de Data Services, disponible à l'adresse go.microsoft.com/fwlink/?LinkId=120539.

Sécurité du service de données
La sécurité est toujours le premier souci lorsque l'on parle d'une technologie Web, surtout si elle permet d'atteindre et de manipuler des données critiques. Par conséquent, la sécurité a été une considération essentielle au cours du cycle de développement des services de données.
Pour fournir l'authentification, ADO.NET Data Services reprend une bonne partie de l'infrastructure d'authentification existante d'ASP.NET et de WCF pour offrir une expérience d'authentification intégrée à travers les applications et par le biais du service. Si vous avez un site ASP.NET qui utilise l'authentification (soit l'un des fournisseurs d'authentification intégrés soit un fournisseur personnalisé définissant correctement l'entité de contexte HTTP), ADO.NET Data Services peut optimiser le mécanisme que vous avez choisi pour établir l'identité actuelle (entité) de la requête donnée. De même, si votre service de données est hébergé en dehors d'ASP.NET (WCF ou via un hôte personnalisé), l'hôte peut choisir également d'utiliser n'importe quel mécanisme d'authentification tant qu'il fournit des API pour qu'un auteur de service de données puisse accéder à l'entité de la requête.
Par défaut, tout nouveau service de données ADO.NET est entièrement verrouillé, toutes les entités, opérations de service et métadonnées étant inaccessibles sans accès en lecture ou en écriture. Cela dit, il existe différents mécanismes pour contrôler la stratégie d'autorisation et exécuter des validations par requête sur votre service de données.
L'une des premières mesures qu'un développeur de service de données prendra sera d'ouvrir l'accès aux ressources exposées par le service de données. Dans nos exemples, nous avons présenté un cas simple de définition d'une stratégie d'accès de lecture/écriture pour l'ensemble du service. Ceci est réalisé pour chaque service en utilisant la méthode InitializeService. Les stratégies définies étaient des stratégies EntitySet autorisant ou limitant l'accès aux ensembles d'entités dans le modèle et qui déterminaient quels ensembles d'entités et associations apparentées étaient disponibles à partir du point de terminaison de métadonnées du service. Bien que les deux stratégies présentées dans ces exemples ouvrent un accès à l'ensemble du modèle pour la lecture et l'écriture, nous pourrions être beaucoup plus spécifiques, en définissant une stratégie différente pour EntitySets et en incluant des stratégies composées le cas échéant, comme à la figure 3.
public class MyService : DataService<NorthwindEntities>
{
   public static void InitializeService(
      IDataServiceConfiguration config)
   {
       // '/Customers' entities are enabled for all read and write 
       // operations
       config.SetEntitySetAccessRule("Customers", 
          EntitySetRights.All);

       //  URI '/Orders' is disabled, but '/Orders(1)' is enabled for read 
       //  only 
       config.SetEntitySetAccessRule("Orders",
           EntitySetRights.ReadSingle);

       //  Can insert and update, but not delete Products
       config.SetEntitySetAccessRule("Products",
           EntitySetRights.WriteInsert |
           EntitySetRights.WriteUpdate);
   }
}
Dans de nombreux scénarios, les services de données seront également nécessaires pour exécuter la logique de validation lorsque les entités entrent dans le service de données (pour les insertions, les mises à jour ou les suppressions) et pour limiter l'accès aux entités sur une base par requête. Pour ces scénarios, vous pouvez utiliser des intercepteurs, ce qui permet à un développeur de service de données d'ajouter une validation personnalisée ou une logique de stratégie d'accès dans le pipeline de traitement de requête/réponse d'un service de données. Par exemple, si nous voulons permettre à des clients de récupérer seulement leurs commandes et pas celles passées par les autres clients, nous devrions implémenter un intercepteur de requête, comme à la figure 4. L'intercepteur de requête retourne un prédicat à ajouter à la requête qui est alors envoyé au magasin de données, ce qui évite les voyages supplémentaires au magasin de données pour récupérer les informations de contrôle d'accès.
public class nw : DataService<NorthwindModel.NorthwindEntities>
{
     public static void InitializeService(IDataServiceConfiguration config)
     {
         config.SetEntitySetAccessRule("Orders", EntitySetRights.All);
     }

     [QueryInterceptor("Orders")]
     public Expression<Func<Orders,bool>> OnQueryOrders()
     {
         return o => o.Customer.ContactName == 
             HttpContext.Current.User.Identity.Name          
     }
}
De même, les intercepteurs peuvent également être ajoutés pour les insertions, les mises à jour et les suppressions comme intercepteurs de modifications. Les intercepteurs de modifications n'ont pas de valeur de retour et prennent deux arguments, un objet du type contenu dans EntitySet et une énumération UpdateOperations qui définit l'action (mise à jour, insertion ou suppression) demandée dans la ressource. L'intercepteur peut changer l'objet qui lui est passé ou définir la référence à une instance tout à fait différente. Si la méthode lance une exception, l'opération est abandonnée, aucune modification n'est effectuée dans la base de données et une erreur est retournée à l'agent client.

Côté client : accès au service
De manière générale, nous avons traité HTTP comme l'API pour les services de données et ainsi beaucoup travaillé pour nous assurer qu'elle est simple à utiliser directement au niveau HTTP. Ceci signifie que non seulement il est facile d'interpréter les charges de service, mais aussi que toute plate-forme avec une pile HTTP peut utiliser facilement des services de données. Cela dit, si vous utilisez une plate-forme Microsoft pour accéder à des services de données, le développement côté client est encore simplifié grâce aux bibliothèques clientes ADO.NET Data Services. Ceci présente un modèle de programmation plus naturel pour les applications écrites avec .NET Framework, Silverlight et ASP.NET AJAX pour cibler des services de données, manipuler les résultats en termes d'objets et gérer des facteurs tels que le parcours d'associations. Les bibliothèques clientes font abstraction des détails des requêtes et des réponses HTTP et garantissent également une expérience plus uniforme à travers chacune des piles clients.
Les bibliothèques clientes .NET Framework et Silverlight consistent en deux types principaux : la classe DataServiceContext et la classe DataServiceQuery. DataServiceContext représente le contexte d'exécution pour un service de données donné. Les services de données eux-mêmes sont sans état ; cependant, le contexte dans lequel le développeur interagit ne l'est pas et l'état sur le client est maintenu entre les interactions pour prendre en charge des fonctionnalités comme la résolution d'identité et la simultanéité d'exécution optimiste. L'objet DataServiceQuery représente une requête spécifique sur le magasin défini en utilisant la syntaxe d'URI. Pour exécuter une requête et en obtenir les résultats sous forme d'objets .NET, énumérez simplement sur l'objet de requête. Pour cela, vous pouvez utiliser la construction standard « ForEach » en C# ou « For Each » en Visual Basic®, par exemple.
Pour utiliser les entités définies dans un service de données comme objets .NET sur le client, des classes correspondantes doivent être définies pour l'application client. Il existe trois méthodes principales pour cela. Une solution consiste à les définir manuellement. La figure 5 présente une simple définition écrite manuellement pour la classe Region et un petit morceau de code qui exécute une requête sur les régions et imprime leurs références et leurs descriptions dans la sortie.
namespace TestApplication
{
  public class Region
  {
     public int RegionID { get; set; }
     public string RegionDescription { get; set; }
  }

  class Program
  {
     static void Main(string[] args)
     {
        DataServiceContext ctx = new
           DataServiceContext("http://localhost:25115/NorthwindService.svc");

        DataServiceQuery<Region> regions =
            ctx.CreateQuery<Region>("/Region?$orderby=RegionID");

         foreach (Region r in regions)
         {
            Console.WriteLine(r.RegionID + ", " + r.RegionDescription);
         }
      }
   }
}
Une approche plus commune consiste à utiliser le code produit par Visual Studio. Même si l'écriture manuelle des classes est efficace lorsque le service compte seulement un petit nombre de types, au fur et à mesure que le schéma de service de données devient plus complexe, le nombre et la taille des classes devant être créées manuellement et maintenues augmentent également de manière significative.
La manière la plus courante de produire les classes nécessaires consiste à utiliser l'assistant Ajouter une référence de service dans Visual Studio. De la même manière que pour utiliser Ajouter une référence de service pour un service WCF, cliquez simplement avec le bouton droit sur le Projet et choisissez Ajouter une référence de service (voir la figure 6).
Figure 6 Ajouter une référence de service (cliquer sur l'image pour l'agrandir)
Dans la boîte de dialogue Ajouter une référence de service, entrez l'URI du point d'entrée du service comme Adresse. Dans notre exemple, nous entrons <host>/<vdir>/NorthwindService svc. Ceci produira des classes basées sur la définition de service de données et les ajoutera au projet.
Une autre option est d'utiliser l'outil de ligne de commande datasvcutil.exe pour produire les classes basées sur la définition de service de données. Cet outil est fourni avec ADO.NET Data Services et se trouve dans le répertoire Windows\Microsoft.Net\Framework\V3.5. L'outil prend comme argument l'URI du point d'entrée du service de données de même que le nom et l'emplacement du fichier de sortie à produire, comme ceci :
"\Windows\Microsoft.Net\Framework\V3.5\datasvcutil.exe" 
    /out:C:\NorthwindSample\northwind.cs /uri:"http://<host>/<vdir>/
    NorthwindService.svc"
La sortie par défaut de l'outil de ligne de commande est un fichier C# contenant une classe pour chacun des types d'entité décrits dans le service de données (les types Visual Basic peuvent être produits en utilisant le commutateur /language:VB). Chaque classe produite a des membres représentant les valeurs et les associations primitives décrites dans le service. Ceci permet aux développeurs de naviguer dans un graphique d'entités associées en utilisant le modèle objet directement.

La bibliothèque cliente .NET
La bibliothèque cliente ADO.NET Data Services .NET présente un modèle de programmation familier aux développeurs qui écrivent des applications en utilisant .NET Framework et les services de données. En coulisses, la bibliothèque cliente utilise HTTP et le format AtomPub, donc elle opère naturellement sur les environnements de réseaux d'entreprise et Internet, ne nécessitant qu'une connectivité de niveau HTTP simple au service de données, directe ou indirecte (par exemple, par proxy).
La bibliothèque cliente peut être utilisée à partir de n'importe quel type de projet, y compris Windows Forms, Windows Presentation Foundation (WPF) et les projets Web. Pour utiliser la bibliothèque cliente ci-dessus, vous aurez besoin d'ajouter une référence à l'assembly System.Data.Services.Client.dll.

La bibliothèque cliente Silverlight
Le client Silverlight n'est pas fourni avec ADO.NET Data Services, mais dans le SDK Silverlight 2.0, ce qui offre une expérience plus intégrée pour le développement d'applications Silverlight. Une différence entre la bibliothèque cliente Silverlight et les bibliothèques clientes .NET et AJAX est que Silverlight 2.0 ne prend pas en charge le développement synchrone. Par conséquent, le développement avec la bibliothèque cliente Silverlight doit utiliser des API asynchrones, suivant le modèle asynchrone begin/end courant. Ceci signifie également que certaines des API que vous pourriez utiliser dans les deux autres bibliothèques clientes n'ont pas été activées dans la bibliothèque cliente Silverlight.

La bibliothèque cliente AJAX
La bibliothèque ASP.NET AJAX est actuellement disponible à l'adresse codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=13357. À l'instar de la bibliothèque cliente .NET abordée précédemment, la bibliothèque cliente AJAX fait abstraction des détails HTTP pour que le développeur d'applications puisse travailler directement avec les objets JavaScript au lieu d'analyser et de créer manuellement les requêtes et les réponses HTTP. Un nombre de pages Web également disponibles sur le site de CodePlex expliquent comment utiliser la bibliothèque de service de données pour les applications AJAX (voir codeplex.com).

Interrogation d'un service de données
Dans la figure 5, les requêtes sur le service ont été créées en appelant la méthode DataServiceQuery CreateQuery et en passant une requête URI. Comme alternative, la bibliothèque vous autorise également à formuler des requêtes de service de données en utilisant LINQ. La bibliothèque cliente mappe l'instruction LINQ à un URI dans le service de données cible et récupère les ressources spécifiées sous forme d'objets .NET. Le code suivant montre comment récupérer tous les clients dans la ville de Londres avec les résultats triés par nom d'entreprise :
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);
}
En utilisant LINQ to ADO.NET Data Services, l'ensemble de requêtes exprimables en syntaxe LINQ est plus large que celui proposé par la syntaxe URI basée sur REST de Data Services. Si une requête ne peut pas être mappée à un URI valide dans le service de données cible, une exception sera générée.
Une façon de mieux appréhender les types de requêtes qui peuvent et ne peuvent pas être mappés aux URI consiste à penser au parcours hiérarchique. Toute requête nécessitant deux pivots ou plus, (par exemple, les jointures ou les sous-requêtes qui utilisent des clauses comme Any), ne peut actuellement pas être mappée à un URI. Les requêtes à un pivot et les parcours d'associations, toutefois, fonctionnent généralement bien.
Jusqu'ici nous avons interrogé des entités simples. Les associations entre les objets sont également suivies et gérées par DataServiceContext et les objets associés peuvent être chargés de façon dynamique ou en fonction du besoin, en utilisant les formats d'URL abordés dans la section Format URI uniforme. Pour charger des entités associées en fonction des besoins (chargement différé), utilisez la méthode LoadProperty sur la classe DataServiceContext, comme à la figure 7.
// get a single category
DataServiceQuery<Categories> categories =
               context.CreateQuery<Categories>("/Categories(1)");

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

     context.LoadProperty(c, "Products");

     foreach (Products p in c.Products)
     {
          Console.WriteLine("\t" + p.ProductName);
     }
}
Dans certains scénarios, nous pouvons vouloir éviter également la boucle supplémentaire nécessaire pour extraire les entités, en préférant les charger avec la requête originale. Dans ce cas, l'option expand est spécifiée sur l'URI. La bibliothèque cliente reconnaît que le résultat inclut des entités au plus haut niveau et associées et les matérialisera toutes sous forme de graphique objet unique. La figure 8 charge dynamiquement des produits apparentés dans une seule boucle au service de données.
public IList<Products> GetProducts(string productName, 
    Categories category)
{
     int categoryId = category.CategoryID;
     string uri = serviceUri + "/Categories(" + categoryId + ")/Products?";
     if (!String.IsNullOrEmpty(productName)) 
     {
         uri = uri + "$filter=ProductName eq '" + productName + "'&";
     }
     uri = uri + "$expand=Categories";
     Uri queryUri = new Uri(uri);
     try
     {
         IEnumerable<Products> products = 
            context.Execute<Products>(queryUri);

         return products.ToList();
     }
     catch (Exception)
     {
         return null;
     }                  
}

Mappage de commande
Avec les requêtes via ADO.NET Data Services mappés par le biais de requêtes HTTP Get, comment les requêtes Create, Update et Delete sont-elle exécutées ? Chacune des quatre opérations CRUD (Create, Retrieve, Update, Delete) sont mappées à un verbe HTTP différent avec Retrieve mappée sur GET, Create mappée sur POST, Update mappée sur PUT et Delete mappée sur DELETE. Tout comme avec les requêtes, la bibliothèque cliente utilisée (dans ce cas, la bibliothèque cliente .NET) fera abstraction des détails et utilisera le verbe HTTP approprié, autorisant ainsi le développeur à utiliser tout à fait facilement les API fournies.
Pour créer une instance d'une entité dans le service de données, créez simplement l'objet .NET, remplissez-le avec les données désirées, puis appelez AddObject sur l'objet DataServiceContext, en passant le nouvel objet et le nom de l'EntitySet auquel l'objet sera ajouté, comme ici :
public void AddProduct(Products product)
{
     context.AddObject("Products", product);
     context.AttachTo("Categories", product.Categories);
     context.SetLink(product, "Categories", product.Categories);
     DataServiceResponse r = context.SaveChanges();
}
Il est également possible d'ajouter ou de modifier les associations entre les objets .NET et de faire en sorte que la bibliothèque cliente reflète ces modifications comme opérations de création/suppression d'association. Par exemple, le code précédent crée un Product dans la base de données Northwind et l'associe à une Category existante. Les catégories et les produits participent à une association un-à-plusieurs, donc, un produit donné a une catégorie spécifique.
Une fois qu'une entité est créée, le service de données en retournera une copie récente, y compris les valeurs qui ont été mises à jour suite à des déclencheurs dans la base de données, des clés générées automatiquement, etc. La bibliothèque cliente mettra alors automatiquement à jour l'objet .NET avec les nouvelles valeurs.
Pour modifier une instance existante d'entité, l'objet doit d'abord être extrait par le client et suivi par DataServiceContext. Le développeur interrogera d'abord ou attachera l'objet au contexte, apportera les modifications désirées à ses propriétés et appellera alors la méthode UpdateObject. La méthode UpdateObject ordonne à la bibliothèque cliente d'envoyer une mise à jour pour cet objet :
public void UpdateProduct(Products product)
{
    Categories newCategory = product.Categories;
    context.AttachTo("Products", product);
    context.AttachTo("Categories", newCategory);
    context.UpdateObject(product);
    context.SetLink(product, "Categories", newCategory);

    context.SaveChanges();
}
Pour supprimer une instance d'entité, l'objet doit de nouveau être contenu par le client et suivi par DataServiceContext. Après le suivi, nous pouvons effectuer un appel simple à la méthode DeleteObject sur l'instance de contexte pour marquer l'objet pour la suppression :
public string DeleteProduct(Products product)
{
     context.AttachTo("Products", product);
     context.DeleteObject(product); 
     context.SaveChanges();
}
Dans tous les exemples susmentionnés, le dernier appel de méthode effectué sur le contexte a été un appel à la méthode SaveChanges. Les modifications sont suivies dans l'instance DataServiceContext mais pas envoyées tout de suite au serveur. Une fois toutes les modifications requises pour une activité donnée terminées, un appel à SaveChanges les soumet au service de données.

Jusqu'ici, chacun des exemples a eu pour résultat une seule requête HTTP et serveur pour chaque opération de client. Cette approche (l'opération unique par requête HTTP) fonctionne bien dans certains scénarios, mais il est souvent plus judicieux de grouper plusieurs opérations et de les envoyer au service de données dans une requête HTTP unique. Ceci réduira le nombre de boucles au service de données et autorisera une étendue logique d'atomicité pour les collections d'opérations. Pour répondre à ces exigences, le client prend en charge l'envoi de groupes d'opérations CUD (Create, Update, Delete) et de groupes d'opérations Query au service de données dans une requête HTTP unique. Le code suivant illustre comment deux requêtes ou plus peuvent être envoyées au service de données dans un seul lot :
var q = (DataServiceRequest)from o in context.SalesOrder
                            select o;

// send two queries in one batch request to the data service
DataServiceResponse r = service.ExecuteBatch(
    new DataServiceRequest<Customer>(new 
      Uri("http://localhost:25115/NorthwindService.svc/Customers")),
      q);
L'envoi d'un groupe d'opérations CUD en lot au service de données s'effectue en utilisant un appel simple à la méthode SaveChanges et en passant SaveChangesOptions.Batch comme seul paramètre. Cette valeur de paramètre ordonne au client de grouper toutes les opérations de modification en attente dans un lot et de les envoyer au service de données en tant que groupe atomique. Lors de l'envoi des opérations de modification comme lot avec SaveChangesOptions.Batch, soit toutes les modifications s'achèveront avec succès, soit aucune des modifications ne sera appliquée.

En savoir plus
Même si cet article donne assez d'informations pour démarrer avec ADO.NET Data Services, il reste toujours un certain nombre de sujets susceptibles de vous intéresser. Pour plus d'informations, rendez-vous sur le blog de l'équipe Data Services blogs.msdn.com/astoriateam et sur le centre de développement de données MSDN à l'adresse msdn.microsoft.com/data sous l'en-tête ADO.NET Data Services.

Elisa Flasko est responsable de programme au sein de l'équipe de Programmabilité des données chez Microsoft, notamment les technologies ADO.NET, les technologies XML et les technologies de connectivité SQL Server. Vous pouvez la contacter à l'adresse blogs.msdn.com/elisaj.

Mike Flasko est responsable de programme au sein du groupe Programmabilité des données SQL chez Microsoft. Vous pouvez contacter Mike sur son blog à l'adresse blogs.msdn.com/mflasko.

Page view tracker