Partager via


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

Comments

  • Anonymous
    August 06, 2009
    So which one do you think is the easiest to get the data from? I still think of entity framework as the one :) Anyway Brad have you ever tried implementing a File Upload control to silverlight app with RIA services environment? I'm following your series up until now and kinda getting problems with retrieving the string of the filename and inserting it into EF database..

  • Anonymous
    August 06, 2009
    Could you use other validation framework? Like Castle Validator or NHibernate Validator

  • Anonymous
    August 07, 2009
    Hi Brad, Its really is a amazing series and it shows how easy to access data different ways. But with this series of multiple options, i want to know which one is the best way to go, which one you would choose to implement it.

  • Anonymous
    August 07, 2009
    Brilliant article. I'm tempted to use NHibernate Linq with MVC and Dynamic Data and POCO classes (with attributes) as a stop gap until .NET 4.0 (with EF 2.0) RTM comes out.

  • Anonymous
    August 07, 2009
    While I applaud you for showing it with NHibernate (you're braver than some of your blue bad brethren), I would argue that including the validation attributes in the POCO classes creates a hard link to the validation assemblies.  This is creating more and more tight coupling.  I still think that having validation outside the code is the better model. I know its possible, but I can't find the demo. Did you do this?

  • Anonymous
    August 07, 2009
    Hey Shawn, You can use Buddy Classes, which looks like an excellent approach to me. See http://blog.pagedesigners.co.nz/archive/2009/08/06/asp.net-mvc-2-ndash-buddy-classes-for-your-models.aspx

  • Anonymous
    August 08, 2009
    Thanks for these Sessions Brad! The only piece I'm missing is an example with Delete - including filtering and sorting. From looking at the Silverlight forum it seems that there are some issues with that in the July CTP - or am I missing something... (http://silverlight.net/forums/t/112232.aspx and http://silverlight.net/forums/p/111826/254733.aspx#254733).

  • Anonymous
    August 08, 2009
    Is relationship working in this scenario ? I tried RIA Services with NHibernate in various ways but Association/Include isn't working in the client side code generated by ria services. Maybe I'm wrong. You wrote a great series of articles but maybe introducing a model with a relationships can give it more value.

  • Anonymous
    August 09, 2009
    cool stuff~~ thanks for share~

  • Anonymous
    August 10, 2009
    re: validation - I don't think buddy-classes is a good plan - you seem to get lots of repeated code, because you're essentially duplicating the class, then decorating the duplicate's properties.   Instead, have you seen http://fluentvalidation.codeplex.com ?  You create a validator per class, then chuck all of them into your IoC container, pulling them out at need.  It's subtly different from the buddy-classes approach out of dynamic data - well, IMO anyway. Brad; it's great to see someone on msdn blog about things outside of the environs of MS :-)

  • Anonymous
    August 11, 2009
    Brad, Could you show an example of advanced custom validation that involves accessing another entity instance, Such as an Item entity from a Order Item entity to validate that stock is available for the item being added to an order. How do you create or access a DataContext in shared code? Thanks

  • Anonymous
    August 11, 2009
    Peter, FluentValidation looks like a much better approach! It sure deserves more media coverage. Thanks for sharing.

  • Anonymous
    August 13, 2009
    Brad, since you switched from Entity Framework to NHibernate... which one do you think applies better for this scenario? at the end of the series which one will your demo be using and why?

  • Anonymous
    August 15, 2009
    Yeah - I noticed you conveniently left out another entity in your example to use for any associations (one-to-many, many-to-one, many-to-many). Because there is NO RIA support for ISet or HashedSet<T> which are commonly used interface/classes for entity associations. But at least you can pretend.

  • Anonymous
    August 25, 2009
    The comment has been removed

  • Anonymous
    September 18, 2009
    Do you have a SQL 2005 version? I don't have 2008 in my environment. Looking at the code I created a SuperEmployees table but when I run the app I get No property or field 'EmployeeID' exists in type 'Object' even though there is an EmployeeId field in the table (I made it a PK and it is an Identity field).

  • Anonymous
    October 21, 2009
    I have same problem with SQL 2005 version. Can anybody help?