Business Apps Example for Silverlight 3 RTM and .NET RIA Services July Update: Part 20: NHibernate

I am having a blast with the series where I am updating my simple Mix 09 Business Application demo.  In this part, I wanted to explore one of the most popular data access solution for .NET – NHibernate.  Many customers finds that the flexibility of NHibernate makes it easier for them to build maintainable and testable applications.  As an aside, I think NHibernate is an excellent example of the vibrant open source community on .NET that I’d like to support. 

You can see the full series here.

The demo requires (all 100% free and always free):

  1. VS2008 SP1
  2. Silverlight 3 RTM
  3. .NET RIA Services July '09 Preview
  4. NHibernate (w/ NHibernate Linq  ) and Fluent NHibernate

Also, download the full demo files and check out the running application.

 

Basically what I wanted to do was switch over my DomainService from getting its data from Entity Framework  to getting its data from NHibernate.  The rest of the application stays effectively the same.  

image

To start with I grabbed the code from the previous walk through, deleted the edmx file. 

Below is the code I use to create the NHibernate SessionFactory.  The fluent interface makes it very easy to configure. 

    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: }

Next, let’s look at the mapping class.. this shows how the data from the database are mapped into a .NET Class. 

    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: }

And here is the type I map the data into.  Notice this has the validation attributes for RIA Services to do the validation on the client and server. 

    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:     }

 

That’s the bulk of the NHibernate specific code.    Now let’s look at the DomainService..

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

First, in line 2, notice I have factored some of the plumbing code into a base class.  This is very simple (not production ready) example.. we will look it in more detail later. Then we return the static SessionFactory the the base class.  The SessionFactory is static such that it is created only once per AppDomain rather than on each request.

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

Here we use NHibernate Linq to return an IQueryable that RIA Services can use to do paging, filtering, etc. 

Then we have Insert and Update which are very basic..

    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: }

Now, let’s take a look at the NHibernateDomainService base class… Again this is very basic, just to show the concepts..

    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:     }

Notice in the constructor we create the Session… then we close the session in the Dispose() method. 

The interesting bit is handling the ChangeSet… Notice we use a transaction to wrap the calls to our update\create methods.  This ensures that if their is any failure we roll back the changes (this is the contract for DomainService)…

Now, because we plug into the DomainService pattern, we get our silverlight client with paging, filtering, etc

image

…and validated editing.

image 

 

But we also get ASP.NET support via the DomainDataSource as in this example from the sitemap.aspx

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

 

And we get a REST based interface via the Astoria support for DomainService.

image

 

And we get ASP.NET Dynamic Data Support, again with paging, filtering,

image

and validating editing.

image

And all the other presentation tiers..

 

Wow, pretty cool..

For more on NHibernate, check out Ayende’s blog

For more NHibernate and DomainService, check out Chris van de Steeg’s blog post ASP.NET MVC, DynamicData, Domain-/RiaServices, Unity and NHibernate: Part 1