Traduction

Cet article est la traduction la plus fidèle possible de l'article original de Brad Abrams, Business Apps Example for Silverlight 3 RTM and .NET RIA Services July Update: Part 20: NHibernate.

Article

Je m'éclate avec cette série où je mets à jour ma simple démo d'application métier du Mix09. Dans cette partie, je voulais explorer l'une des solutions d'accès aux données pour .NET les plus populaires : NHibernate. De nombreux clients trouvent que la flexibilité de NHibernate facilite le développement d'applications maintenables et testables. Soit dit en passant, je pense que NHibernate est un excellent exemple du dynamisme de la communauté open source .NET, que je voudrais soutenir.

Vous pouvez trouver la série complète ici.

Cette démo nécessite les éléments suivants (tout est 100 % gratuit) :

Téléchargez aussi les fichiers de la démo complète, et jetez un œil à l'application en cours d'exécution.

En fait, tout ce que je voulais faire, c'était modifier mon DomainService pour qu'il ne récupère plus ses données d'Entity Framework mais de NHibernate. Le reste de l'application reste effectivement inchangé.

Image non disponible

Pour commencer, j'ai récupéré le code du tutoriel précédent, et j'ai supprimé le fichier edmx.

Voilà ci-dessous le code que j'utilise pour créer la SessionFactory NHibernate. L'interface fluide rend cela très facile à configurer.

 
Sélectionnez

  1: static ISessionFactory SessionFactory = CreateSessionFactory();
  2: static ISessionFactory CreateSessionFactory()
  3: {
  4:     return Fluently.Configure()
  5:       .Database(
  6:         MsSqlConfiguration.MsSql2005
  7:         .ConnectionString(c => c
  8:             .Is(ConfigurationManager.ConnectionStrings["MainConnStr"].ConnectionString)
  9:             )
 10:       )
 11:       .Mappings(m =>
 12:        m.FluentMappings.AddFromAssemblyOf<SuperEmployeeDomainService>())
 13:        .BuildSessionFactory();
 14:  
 15: }

Ensuite, regardons la classe de mapping. Elle indique comment les données de la base sont mappées sur une classe .NET.

 
Sélectionnez

  1: public class SuperEmployeesMap : ClassMap<SuperEmployee>
  2: {
  3:     public SuperEmployeesMap()
  4:     {
  5:         WithTable("SuperEmployees");
  6:         Id(x => x.EmployeeID);
  7:         Map(x => x.Name);
  8:         Map(x => x.Gender);
  9:         Map(x => x.Issues);
 10:         Map(x => x.LastEdit);
 11:         Map(x => x.Origin);
 12:         Map(x => x.Publishers);
 13:         Map(x => x.Sites);
 14:     }
 15: }

Et voilà le type sur lequel je mappe les données. Remarquez les attributs de validation pour RIA Services pour effectuer la validation sur le client et sur le serveur.

 
Sélectionnez

  1: public class SuperEmployee
  2: {
  3:         [Key]
  4:         [ReadOnly(true)]
  5:         public virtual int EmployeeID {get; set;}
  6:  
  7:         [RegularExpression("^(?:m|M|male|Male|f|F|female|Female)$", 
  8:             ErrorMessage = "Gender must be 'Male' or 'Female'")]
  9:         public virtual string Gender { get; set; }
 10:  
 11:         [Range(0, 10000,
 12:             ErrorMessage = "Issues must be between 0 and 1000")]
 13:         public virtual Nullable<int> Issues { get; set; }
 14:  
 15:         public virtual Nullable<DateTime> LastEdit { get; set; }
 16:  
 17:         [Required]
 18:         [StringLength(100)]
 19:         public virtual string Name { get; set; }
 20:  
 21:         public virtual string Origin { get; set; }
 22:  
 23:         public virtual string Publishers { get; set; }
 24:  
 25:         public virtual string Sites { get; set; }
 26:     }

C'est l'essentiel du code spécifique à NHibernate. Revenons maintenant au DomainService.

 
Sélectionnez

  1: [EnableClientAccess()]
  2: public class SuperEmployeeDomainService : NHibernateDomainService
  3: {
  4:     public SuperEmployeeDomainService() :
  5:         base(SessionFactory) { }

D'abord, à la ligne 2, remarquez que j'ai factorisé une partie du code de "plomberie" dans une classe de base. C'est un exemple très simple (pas prêt pour la production). On y reviendra plus en détail après. Ensuite, on envoie la SessionFactory à la classe de base. La SessionFactory est statique afin qu'elle soit créée une seule fois par AppDomain, plutôt qu'à chaque requête.

 
Sélectionnez

  1: public IQueryable<SuperEmployee> GetSuperEmployees()
  2: {
  3:     return Session.Linq<SuperEmployee>()
  4:             .Where(e => e.Issues > 10);
  5: }

Ici on utilise NHibernate Linq pour renvoyer un IQueryable que RIA Services peut utiliser pour faire la pagination, le filtrage, etc.

On a ensuite l'insertion et la mise à jour qui sont très basiques.

 
Sélectionnez

  1: public void InsertSuperEmployee(SuperEmployee superEmployee)
  2: {
  3:     Session.Save(superEmployee);
  4: }
  5:  
  6: public void UpdateSuperEmployee(SuperEmployee currentSuperEmployee)
  7: {
  8:    Session.Update(currentSuperEmployee);
  9: }

Maintenant, regardons la classe de base NHibernateDomainService. Encore une fois, c'est assez basique, juste pour montrer les concepts.

 
Sélectionnez

  1: public class NHibernateDomainService : DomainService
  2: {
  3:  
  4:     protected ISession Session;
  5:    
  6:     public NHibernateDomainService(ISessionFactory sessionFactory)
  7:     {
  8:         this.Session = sessionFactory.OpenSession();
  9:     }
 10:  
 11:  
 12:     protected override void Dispose(bool disposing)
 13:     {
 14:         Session.Dispose();
 15:         base.Dispose(disposing);
 16:  
 17:     }
 18:     protected override void ExecuteChangeSet(ChangeSet changeSet)
 19:     {
 20:         using (var trans = Session.BeginTransaction())
 21:         {
 22:             base.ExecuteChangeSet(changeSet);
 23:             trans.Commit();
 24:         }
 25:     }

Remarquez qu'on crée la session dans le constructeur. Ensuite on la ferme dans la méthode Dispose().

La partie intéressante est la gestion du ChangeSet. Remarquez qu'on utilise une transaction pour encadrer les appels à nos méthodes de mise à jour et de création. Cela permet de s'assurer que s'il y a une erreur, on annule les changements (c'est le contrat pour DomaineService).

Maintenant, puisqu'on s'intègre dans le modèle du DomainService, on a notre client Silverlight avec la pagination, le filtrage, etc.

Image non disponible

Et la validation de la saisie

Image non disponible

Mais on a aussi le support d'ASP.NET via la DomainDataSource, comme dans cet exemple tiré du fichier sitemap.aspx.

 
Sélectionnez

  1: <asp:DomainDataSource runat="server" ID="SitemapDataSource" 
  2:     DomainServiceTypeName="MyApp.Web.SuperEmployeeDomainService" 
  3:     SelectMethod="GetSuperEmployees" />

Et on a une interface basée sur REST via le support d'Astoria pour DomainService.

Image non disponible

Et on a le support d'ASP.NET Dynamic Data, toujours avec la pagination, le filtrage, etc.

Image non disponible

Et la validation de la saisie.

Image non disponible

Et toutes les autres couches de présentation.

Pas mal, non ?

Pour plus d'informations sur NHibernate, faites un tour sur le blog d'Ayende.

Pour plus d'informations sur NHibernate et DomainService, allez voir le billet blog de Chris van de Steeg ASP.NET MVC, DynamicData, Domain-/RiaServices, Unity and NHibernate: Part 1.

Conclusion

Ceci conclut la vingtième partie de cette série. La partie suivante traitera des données hiérarchiques.

Remerciements

Je tiens ici à remercier Brad Abrams pour nous avoir autorisé à traduire son article.
Je remercie également ClaudeLELOUP pour sa relecture et ses propositions.