ADO.NET Dataservices avec Silverlight et NHibernate

Présentation d’ADO.NET Data Services

ADO.NET Data Services (ancien projet “Astoria”) est un des composants intéressants qui a fait son apparition avec le SP1 du.NET framework 3.5. Il permet d’exposer une source de données à travers une interface REST dont le format d’échange peut-être ATOM ou JSON.

ADS est bien sûr compatible avec ADO.NET Entity framork mais aussi toute source de données qui implémente IQueryable comme par exemple Linq pour NHibernate (1). ADO.NET Data Services s’execute au sein de WCF.

Il faut respecter certains contraintes pour que nos objets soient compris par ADS : un identifiant explicite ou désigné par attribut. Les objets liés aussi doivent être exposés ou ignorés par un attribut portant sur la propriété correspondante.

Les opérations de lecture se traduisent par un GET, une création par un PUT, une mise à jour par un POST, et une suppression par DELETE. Si on veut mettre à jours plusieurs entités en un seul appel il existe un mode BATCH qui envoie en post les opérations séparées au format MIME dans la requête.

La lecture est assez simple, voici quelques exemples d’url :

http://localhost./SubscriptionServices.svc/Subscribers pour avoir tous les subscribers

http://localhost/SubscriptionServices.svc/Subscribers(guid'7af42422-ef0d-4368-a2a9-0bc2e9b0fd32')/ pour avoir une entité en particulier par son identifiant

http://localhost./SubscriptionServices.svc/Subscribers()?$orderby=FirstName pour trier le résultat de la requête

Il existe d’autres opérateurs pour par exemple faire du paging (skip) ou du filtre. “expand” est intéressant. Par défaut ADS ne charge pas les propriétés de type complexe. Par exemple dans le modèle (très simple) suivant, la liste des adresses ne sera pas renvoyée quand on consulte une entité “subscriber”.

notre modèle simple, un subscriber a une à plusieurs addresses
notre modèle simple, un subscriber a une à plusieurs addresses

On a alors deux façons de “charger” :

Nhibernate dans tout ça ?

Linq pour NHibernate expose une interface IQueryable via une extension de méthode sur la Session (Linq()).

Mais pour que les opérations de mises à jour et de lazy loading ($expand) marchent il faut que notre source de donnée implémente IUpdatable et IExpandProvider.  Grâce à Shawn Wildermuth ceci est posssible. Il a développé une “glue” entre notre source de données et NHibernate: NHibernateContext qui implemente les deux interfaces.

Voici à quoi ressemble notre service qui expose notre modèle :

namespace OmenMag.DataWebServices

[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)] public class SubscriptionServices : DataService { public static void InitializeService(IDataServiceConfiguration config) { config.SetEntitySetAccessRule("*", EntitySetRights.All); } }

public class SubscriptionDataContext : NHibernateContext
{

    public IQueryable Subscribers
    {
        get { return ProvideSession().Linq(); }
    }

    public IQueryable

Addresses { get { return ProvideSession().Linq

(); } }

    protected override ISession ProvideSession()
    {
        return Global.SessionFactory.GetCurrentSession();
    }

}

On voit que la “plomberie” a bien été cachée :)

Chez le client ?

Pour finir le tour d’horizon j’aurai pu coder un client qui gère le JSON ou ATOM dans n’importe quelle technologie. Je voulais mettre la main à Silverlight depuis sa version 2 qui apporte une vrai mini-CLR (et surtout des contrôles) c’étati donc un peu l’occassion.

ADS fournit une librairie client (System.Data.Services.Client) qui permet via un objet DataContext de dialoguer avec un service ADS. Il existe un utilitaire qui permet de générer ce proxy (DataSvcUtil.exe). Voici un exemple de requête :

private void Load_Click(object sender, RoutedEventArgs e) { ctx = new SubscriptionDataContext(new Uri(“http://localhost./ SubscriptionServices.svc”, UriKind.Absolute));

var query = from s in ctx.Subscribers.Expand("Addresses")
                    orderby s.FirstName
                    select s;

DataServiceQuery subcrierQuery = (DataServiceQuery)query;

subcrierQuery.BeginExecute(new AsyncCallback(SubscriberLoaded),
                                                    subcrierQuery);

}

void SubscriberLoaded(IAsyncResult result) { DataServiceQuery query = (DataServiceQuery)result.AsyncState; listSubs = query.EndExecute(result).ToList(); SubscribersDG.ItemsSource = listSubs; }

Voici mon client Silverlight beta 2 (il faut savoir dépasser l’interface pour apprécier le travail :) :

le client silverlight  avec un datagrid et un formulaire de mise à jour
le client silverlight avec un datagrid et un formulaire de mise à jour

Après une modification on prévient le datacontext que l’objet a été modifié :

private void Submit_Click(object sender, RoutedEventArgs e) { Subscriber sub = (Subscriber)SubscribersDG.SelectedItem; sub.Addresses[0].Street = adrStreet.Text; ctx.UpdateObject(sub); ctx.UpdateObject(sub.Addresses[0]); };

et lors du save les modifications sont envoyées en BATCH au service

private void Save_Click(object sender, RoutedEventArgs e) { ctx.BeginSaveChanges(SaveChangesOptions.Batch, new AsyncCallback(OnSavedComplete), null); }

Voilà pour un petit tour d’horizon de ADO.NET Data Service. C’est une façon simple d’exposer des données. C’est aussi une première version. On s’inquiétera de mettre à nues ses données de cette façon, mais je pense que ADO.NET DS répond à un autre besoin qu’une API webservices “classique”. On est ici dans une optique d’API publique d’accès à un service.

Si on veut un contrôle plus fin sur ces données WCF est là, il permet aussi d’exposer une API au format JSON.

(1) : Linq pour NHibernate est en cours de ré-écriture. Etant basé sur l’API Criteria certains opérateurs ne sont pas supportés comme le SelectMany. De toute façon Linq pour NHibernate ne sera disponible que pour la version 2.1 du framework

billet publié dans les rubriques coding le