lundi 1 février 2010

Portage du tutoriel Contact Manager de Entity Framework à NHibernate (et Oracle)

De la même maniere que mon cher collègue Michel, sur son blog (Linq To SQL). Je vais vous présentez comment migrer de Entity Framework à NHibernate + Oracle.

Tout d'abord les informations nécessaires

Création du modèle
Tout de suite, ici il ne s'agit pas de faire du drag'n drop de vos tables avec de beaux object Visual Studio ! Il faut coder lignes par lignes notre modele de persistance.
Tout d'abord supprimez :
  • le fichier "modele" .edmx
  • les références inutiles (System.Data.Entity)
  • le repository : EntityContactManagerRepository.cs
1. Ajout de notre classe de base
A ce niveau, il n'y a pas de génération automatique de classe. Pour cela, ajouter a votre dossier "models" une nouvelle classe nommée "Contact.cs". Celle-ci contiendra tout simplement les attributs représentant la base de données.
public class Contact
{
    public virtual int Id { get; set; }
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
    public virtual string Phone { get; set; }
    public virtual string Email { get; set; }
}
2. Ajout du mapping
Si vous vous êtes déjà quelques peu documenté sur NHibernate (et Hibernate également), vous devez savoir que tout fonctionne par fichier de configuration xml. Le fichier de configuration xml, vous permet de dire a quelle classe correspond telle table et a quel attribut correspond tel champs de votre table. De plus merci de vous référé a ce billet sur l'explication des séquences avec NHibernate et Oracle.
Pour cela ajoutez un nouveau fichier xml, nommé Contact.hbm.xml dans votre dossier "models".


  
    
    
    
    
      SEQ_EBN_CONTACTS
    

    
    
    
    

  


Important : Sur ce fichier xml (Contact.hbm.xml) vous devez préciser les "actions de génération" en précisant que ce fichier est une ressource incorporée, sans cela votre projet ne reconnaitra par votre mapping. De plus, si vous le souhaitez vous pouvez préciser le ou les schémas (xsd) pour faciliter l'écriture (Intellisence), pour cela cliquer sur le fichier et dans la fenêtre de propriétés XML sélectionner les 2 schémas dans votre dossier d'installation d'NHibernate (nhibernate-configuration.xsd et nhibernate-mapping.xsd).
J'avais écrit un billet précisant cette manipulation, mais sur mon poste il semblerait que pour tout nouveau projet il faille préciser ces fichiers (dommage ?)


Ajout de HttpModule
Pour faire fonctionner NHibernate, il est nécessaire d'initialiser une "SessionFactory" et un "Session". C'est sur cette "Session" que toutes les actions en base de données se réalisent CRUD (Session.Update([...]), Session.Delete([...]) ...)
Pour cela nous allons ajouter cette gestion de session a travers l'implementation de l'interface "IHttpModule", permettant entre autre de démarrer la session en début de requete (beginRequest) et de fermer la session en fin de requete de page (endRequest).
Ajoutons donc dans notre dossier "models", une classe NHSessionPerRequest.cs qui gèrera ces actions.
Pensez, si vous ne l'avez pas déja fait a rajouter les références à NHibernate (NHibernate.dll)
using NHibernate;
using NHibernate.Context;
using NHibernate.Cfg;

public class NHSessionPerRequest : IHttpModule
{
       
    private const string CurrentSessionKey = "nhibernate.current_session";
    private static readonly ISessionFactory sessionFactory;

    static NHSessionPerRequest()
    {
        sessionFactory = new Configuration().Configure().BuildSessionFactory();
    }

    public static ISession GetCurrentSession()
    {
        HttpContext context = HttpContext.Current;
        ISession currentSession = context.Items[CurrentSessionKey] as ISession;

        if (currentSession == null)
        {
            currentSession = sessionFactory.OpenSession();
            context.Items[CurrentSessionKey] = currentSession;
        }
        return currentSession;
    }

    public static void CloseSession()
    {
        HttpContext context = HttpContext.Current;
         ISession currentSession = context.Items[CurrentSessionKey] as ISession;

         if (currentSession == null)
         {
             // No current session
             return;
         }

         currentSession.Close();
         context.Items.Remove(CurrentSessionKey);
     }

     public static void CloseSessionFactory()
     {
         if (sessionFactory != null)
         {
             sessionFactory.Close();
         }
     }

     public void Dispose() { }

     public void Init(HttpApplication context)
     {
         context.BeginRequest += BeginRequest;
         context.EndRequest += EndRequest;
     }

     private static void BeginRequest(object sender, EventArgs e)
     {
         ISession session = GetCurrentSession();
     }

     private static void EndRequest(object sender, EventArgs e)
     {
         CloseSession();
     }
}
Il est important de rajouter dans votre Web.config, a la section la référence a notre classe IHttpModule comme ceci : 

Ajout du repository
Enfin pour coordonner le tout, il nous faut la dernière pièce du puzzle qui permet de lier tout ce que nous avons fait. Pour cela, ajouter au dossier "models" une classe (qui herite de l'interface du tutoriel : IContactManagerRepository), nommons la NHContactManagerRepository.cs. Nous utiliserons Linq (avec NHibernate qui le permet depuis la version 2.1 de mémoire), n'oublier donc par les références (NHibernate.Linq.dll). Implémenter les méthodes de l'interface comme ceci :
using NHibernate;
using NHibernate.Linq;

public class NHContactManagerRepository : IContactManagerRepository
{

    // Utilisation SESSION (sessionfactory NH) == EntitieContext
    public ISession Session
    {
        get { return NHSessionPerRequest.GetCurrentSession(); }
    }

    public Contact GetContact(int id)
    {            
        var query = from c in Session.Linq< contact >()
                    where c.Id == id
                    select c;

        return query.FirstOrDefault();
    }

    public IEnumerable< contact > ListContacts()
    {
        return Session.Linq< contact >().ToList();                        
    }

    public Contact CreateContact(Contact contactToCreate)
    {
        Session.Transaction.Begin();
        Session.Save(contactToCreate);
        Session.Transaction.Commit();
        return contactToCreate;           
    }

    public Contact EditContact(Contact contactToEdit)
    {            
        Session.Transaction.Begin();
        Session.Update(contactToEdit);
        Session.Transaction.Commit();
        return contactToEdit;            
    }

    public void DeleteContact(Contact contactToDelete)
    {
        Session.Transaction.Begin();
        Session.Delete(contactToDelete);            
        Session.Transaction.Commit();
    }
}

Rien de très compliquer, j'ai repris les méthodes une a une qui était créées depuis la source du tutoriel, et je les aient modifiées en conséquence.

Configuration
Avant de lancer notre application modifié il reste quelques petits détails a réaliser. Rajouter la chaine de connexion et paramétrage pour préciser que nous utiliserons NHibernate avec Oracle :
Dans la section "configsections"
Et en fin de fichier (avant  )
    
      
      NHibernate.Driver.OracleClientDriver
      User ID=admin;Password=123;Data Source=WEBLYO.world
      true
      NHibernate.Dialect.Oracle10gDialect
      false
      100
      NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle
      

    
  

Ajouter également la dll suivante qui est le proxy (cf web.config) : NHibernate.ByteCode.Castle.dll
Tout les autres élements de la solution reste inchangés.

Conclusion
La conclusion de cette migration est double. On remarque bien qu'il n'est pas si simple de changer de modèle de données, surtout entre Microsoft (Sql Server) et Orcale. Ceci étant due au fait, que Visual Studio en fait trop tout seul pour nous avec le drag'n drop ne facilitant pas la compréhension des choses. Avec NHibernate, tout les éléments doivent être compris pour pouvoir les coder correctement. Du mapping au codage de nos propre classes (POCO) ainsi que le contexte de l'application (Session/ISession vs. ContactManagerDBEntities ...) Mais aussi, on remarque que toute la couche de validation et logique métier n'a pas bouger d'un poil ! Ça c'est plutôt génial. La migration n'est pas aussi simple que celle de michel entre Entity Framework et Linq To Sql ... (on reste Microsoft...)

Aucun commentaire:

Enregistrer un commentaire