Tuesday, 31 March 2009

ASP.NET MVC - First Impressions

Another item on my Tech ToDo is MVC Frameworks. I have been meaning to get in to MVC frameworks for ASP.NET pretty much since I first heard of them so it has been great to finally get a chance to sit down and have a tinker.

What is MVC?

MVC stands for Model View Controllerit’s a software design pattern that aims to separate the concerns of:

The Presentation of the Data (The View)

PlaneBannerHappyFace How the data needs to be viewed should not impact any other code. Code should take the data and then build its own representation based purely on that. For example, if we had an awesome Philly steak and cheese sandwich, we could present it in a number of ways - we could have a photo, we could have a video of someone chowing down on it, or we could just put how it makes us feel on plane banner.

What changes about the actual sandwich? Nothing. Presentation changes should not require changes in the data.

The Data Itself (The Model)

PhillySteakNCheeseThe model is basically the heart of the data. This is where we represent our problem in code. It includes all of the validation, storage and general guts of the application. Think of the model as the steak and cheese in an awesome sandwich. If you were to take it away from the awesome sandwich, it would just become two mundane slices of bread. Without the bread, the steak and cheese is nothing, without the steak and cheese the bread is nothing. Without the model, the code serves no purpose. The model is an awesome sandwich with awesome filling :)

The One Who Makes It Happen (The Controller)

familyguy-burgerking So we have established that we have an awesome sandwich and several ways to present it. But how do we actually get to the point where we have a view of an actual sandwich?

Enter the controller. Think of the controller as the guy/girl you say “one awesome Philly steak and cheese sub please”, give them your cash. They then run off, take what you have said and make it happen. Without the controller, no sandwich gets made and you never get to see it.

Why Use an MVC Framework?

There are several reasons for using an MVC framework over traditional ASP.NET WebForms development:

  • They promote separation of concernsEach element of the MVC triad should not rely on the implementation of another. While there is a well-designed interface between the appropriate elements, how the interface is actually implemented should have no effect.
  • They increase testability.  Ask any WebForms developer if they test the code-behind for their pages. I guarantee most of them will say “no”. The WebForms model is not test-friendly at all (due to the crippling HttpContext class). Taking ALL of the model away from the view and placing in its own place enables you to poke and prod it to your hearts content.
  • Ability to create user-friendly, cleaner URL’s.  MVC frameworks have complete support for URL rewriting which means you can create URL’s like “myweb.com/profiles/rob” rather than “myweb.com/profile.aspx?id=1383067829”.
  • Anything else you feel should be here? Comment and let me know!

Requirements to Get Going

I am currently running Windows Vista Home Premium which comes with IIS7 (which is WAYYY better than IIS6!), but that is not required, you will need however:

That’s about it :) I do highly recommend the VWD Express Edition, it’s really rich (especially for a free editor) and TBH, there is not a great deal you cannot do in the Express version over the Pro version.

I then started working my way through the MVC Contact Manager sample application (click here and scroll to the bottom – MS please add anchors to headers! Even if you do not link to them!).

My First Thoughts

It’s NOT WebForms!

“Well Duh”

Sure, but I have been doing WebForms development for a couple of years now, so I have gotten pretty used to the pain points of it.. While I have tried to control bad habits (e.g. putting too much business stuff in the page code-behind) – it’s easy to be naughty right? MVC totally turns it all on it’s head, come in to it with an open mind and do not expect to be able to quickly port WebForms code.

Don’t Be Afraid of the Code-In-The-Markup Thing

When I started creating my Views, I got nervous about the amount of code I was placing in the ASPX. In the WebForms & VS2005 world, it was easy to do this and then get errors popping up at runtime. Based on that, I liked to avoid it where possible. You will find yourself putting a lot more code in the ASPX since, well, where else can it go? There is no code-behind :P This said though, Visual Studio 2008 is a hell of a lot smarter than 2005 and I found it picks up most boo-boo’s at compile time.

There’s Helpers Everywhere!

One thing that surprised me with MVC is the amount of static helper classes and extensions methods there are. TBH they are probably needed (especially due to the above point) but this is all stuff adding to the learning curve. Be prepared to learn a whole load of new names!

Get Used to Convention over Configuration

While I admit I have not had a chance to dig deeper into how the MVC elements are all glued together by the runtime, it is immediately obviously after creating your first triad that there is some “convention” being assumed under the hood. For example, here is a typical method in a controller:

   1: public ActionResult Edit(int id)
   2: {
   3:     // Get the Contact with the given ID.
   4:     var contact = 
   5:         (from c in _entities.ContactSet
   6:            where c.ID == id
   7:            select c).FirstOrDefault();
   8:     return View(contact);
   9: }

See the return View(contact) statement? Notice I am not actually telling it what view to return? That’s because MVC automatically runs off and searches the Views\{Controller}\{Action} folder. This is all due to the notion of “convention over configuration”. Rather than expect developers to chunk out lots of code to say where to go, establish a standard and go there instead.

Validation Logic in the Controller

Again, since I am still real fresh to ASP.NET MVC, I am not sure if there is another/better way to do this. During the tutorial it gets you to add some validation logic to the “Edit” action for a contact. Here is the code:

   1: [AcceptVerbs(HttpVerbs.Post)]
   2: public ActionResult Create([Bind(Exclude = "Id")] Contact contactToCreate)
   3: {
   4:     // Validate the Data Passed.
   5:     if (contactToCreate.FirstName.Trim().Length == 0)
   6:         ModelState.AddModelError("FirstName", "First name is required.");
   7:     if (contactToCreate.LastName.Trim().Length == 0)
   8:         ModelState.AddModelError("LastName", "Last name is required.");
   9:  /* snip... */

As you can see, where are performing some validation against the data passed in by the user and then reporting any errors to the ModelState. The ModelState is then available to the view to work with (in a real de-coupled manner, it’s really a dictionary). There are also helper methods in the view like Html.ValidationSummary to display errors reported in the ModelState using a HTML unordered list. Now, I know this is all really useful, but I am not sure how I feel about having the validation of the Model in the Controller.. I would expect the validity of the Model to be verified by the model itself, or surely you are creating some duplication of effort? Would love to hear others thoughts on this? (I am totally willing to accept it is just me being a pain in the ass and I should just accept and love the usefulness of it all!)

In Summation

After a few evenings of tinkering with ASP.NET I can’t help but feel like I am only just looking at the surface, let alone touching it. It looks to be a very powerful and already rich platform (compared to WebForms) which is impressive since it is still very much in it’s infancy.

While it will take some work making my brain work more web-like (i.e. without a PostBack/Event Model) I can feel already it is a much cleaner, smarter way to work. Above all, I am enjoying it. The pattern makes so much sense, allowing me to focus on the task at hand (e.g. coding the model is all about the model).

I’ve yet to really start working with it (e.g. sites from scratch, unaided and test-first) but I don’t think it will be long before I am doing that! Then I look forward to posting hopefully more useful and hands-on posts!

What are your thoughts?

Sunday, 29 March 2009

Missing Entity Framework Templates and Visual Studio 2008

VS2008LogoWanting to play with the different technologies on my “Tech ToDo” and following on from my first Tech Day with NHibernate, I wanted to play with Microsoft’s Entity Framework (EF). EF is (to my understanding) a beefed up version of LINQ to SQL.

So, I happily go ahead and download the .NET Framework 3.5 SP1, install and eagerly run off to add my EF data model to my project.. But I can’t, there is no item for it in the templates. The tutorial (actually an MVC one) said there should be, the readme said there should be, but there wasn’t.

Steps Attempted (to no avail)

Following a tweet from @JeremySkinner, I checked that the assemblies (System.Data.Entity*) had installed OK to the reference assemblies folder (C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5) – they had. Next please!

Following on from that, I then thought about the version of Visual Studio I am running.. Mine is actually under the DreamSpark license as I am currently studying with a MS partner, but I got it wayyyy back when 2008 was still in beta. I thought maybe that was it. So I run off to the DreamSpark web site and download a new version and key. I then did a repair install of .NET 3.5 SP1, and again, no dice. Next please!

I then think, maybe the install is just crazy. So I totally uninstall VS2008 and all related components, and reinstall from the new disk downloaded from DreamSpark. Guess what? No dice. And to top it off, I could no longer create MVC projects as it was failing with “project type is not supported by this installation”. I was actually able to resolve this by running “devenv /setup” from the VS command prompt. At this point we are back at square one.

Stumbling Across the Resolution

At this point, I am getting pretty pissed. I figure “screw it”, I will just check out LINQ to SQL instead. So I begin cobbling together a site using LINQ to SQL instead, only to get an error when I try adding my database – oh yeah, I uninstalled MS SQL Server Express 2005 earlier.. No problem, I wanted to upgrade to SQL Server Express 2008 anyway! I begin the install, and fail the pre-requisite check because I don’t have VS2008 SP1 installed..

I thought this was odd, as I was convinced this was installed as an update - oh well, I need it anyway, so I downloaded and installed that. Then I thought “does the EF need VS SP1 too?

NOTE: It never failed in the install and never asked for it.

I then went back and did a repair install of .NET Framework SP1 and then fired up VS again.. And guess what had appeared..

image

Happy face :)

The Moral of the Story..

Always make sure you have ALL the updated kit.. It can be hard to keep track of it all when “waves” are coming out (e.g. this “SP1” wave across the .NET 3.5/2008 family of products).

I thought I would share this slight revelation since I have noticed via Google I am not alone!

Tuesday, 24 March 2009

Tech Day #1 – NHibernate

image In my previous post “My Tech ToDo – I can haz skillz?” I mentioned that I had reduced my hours at work, giving me 2 days a month to do whatever the hell I want. I have decided to devote these days to a given tech or subject to sit down and really have a chance to get acquainted.

Now, I know one day is most likely not enough to completely grok a tech or subject, hell some of them can take a lifetime to master – but it is a heck of a lot more than I have been able to do previously.

Anyway, the first of my Tech Days was dedicated to NHibernate.

What is NHibernate?

Briefly for anyone unsure:

Good ORM’s will take all that away from you, they will take the objects you create, a map of what those objects contain and how you would like them stored and then provide an interface to perform all the database operations for you.

Enter NHibernate.

Getting Started

I decided to opt for a real quick-and-dirty app to just get playing with it to see how it worked. All I really knew about NHibernate before getting started was:

  • It’s free and open source (available at Sourceforge).
  • NHibernate uses an XML file to map the POCO’s (objects) to the data store – but other options are available (such as Castle Active Record).
  • NHibernate’s own configuration is driven by an XML file also.
  • NHibernate has quite a startup performance cost, but should be little performance loss over a regular DAL (make sure you know what a singleton is to overcome this!).
  • NHibernate can cope with polymorphism in a number of ways.
  • NHibernate has the ability to work with log4net to give lots of logging information.
  • NHibernate can generate the database schema for you, based on your classes automatically (w00t!).

So, I had a pretty broad understanding of NHibernate (thanks a lot to @ICooper’s presentation at a local UG meeting!) to begin with, it was really a case of seeing it in action and understanding the setup process..

Setup

I thought it would be good to try and keep the application as simple as possible, so I can focus on getting NHibernate to run and play with it. Therefore the code should be considered as “Lab” code only – not recommended for production at all! I decided on:

  • A standard WinForms app as the host application.
  • xUnit for testing, but the test code will be mashed up with the host application (this saves on duplicating the setup for NHibernate).
  • I will only work with simple CRUD operations on a simple object, leaving polymorphism for later.
  • I only wanted to change the NHibernate configuration just enough to get the thing running :)
  • I would use a portable MS SQL CE data file as the data store (this has both positive and negatives – local file with no SQL service required, but also needs the drivers/libraries to be imported into the project). If you are running SQL Server (and I expect all dev’s will be – if not, grab SQL Express) then it may be easier to use an MDF DB.

Getting Started

I originally download the reference documentation from the website, but I soon gathered that it may be out of date (followed closely and had problems getting it to run) – so I did another search and came across this article by Gabriel Schenker which is absolutely fantastic. I strongly recommended working from that article. You can see that I did.

Download & Symbols Import

Before doing ANYTHING within the application, I would strongly recommend downloading the latest release from Sourceforge and installing the symbols in the “Required_bins” folder into Visual Studio. To do this, copy the files into the “C:\Program Files\Microsoft Visual Studio 9.0\Xml\Schemas” (adjust path as may be necessary) – this will give you Intellisense support for the configuration files.

SQL Server Compact Edition Required Files

If you are using the SQL Server service rather than the standalone SDF files, then obviously skip this step. If not, then please review the notes in this article under “Test the Setup”. It works fine and I would only be regurgitating content so I thought I would rather save myself some typing and share the love :)

Creating the Configuration for NHibernate

To begin with, I started to enter the configuration into the App.config file as instructed in the reference documentation. I then decided to opt against that (especially since the “.config” files for Web/Windows apps can get pretty beefy as it is) and store it in its own configuration file.

Here is the configuration file “hibernate.cfg.xml” that I used:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2:  
   3: <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
   4:  
   5:     <session-factory>
   6:       <!-- Tell NHibernate to use the MS SQL 2000 Dialect -->
   7:       <property name="dialect">NHibernate.Dialect.MsSqlCeDialect</property>
   8:  
   9:       <!-- .. And our connection information (basically used to create a SqlConnection -->
  10:       <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
  11:       <property name="connection.driver_class">NHibernate.Driver.SqlServerCeDriver</property>
  12:       <property name="connection.connection_string">Data Source=Data.sdf</property>
  13:       <property name="show_sql">true</property>
  14:       <property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
  15:       
  16:       <!-- Here we tell NHibernate the Assembly that contains the POCO's we want to map -->
  17:       <mapping assembly="NHibernateTechDay"/>
  18:       
  19:     </session-factory>
  20:  
  21: </hibernate-configuration>

Now, everything in this file is important! Most of the configuration elements are reasonably self-explanatory:

  • dialect tells NHibernate what version of SQL Server (or whatever DBMS you are using) – this means it can use optimisations that are available.
  • connection.provider is used to tell which assembly NHibernate should use for the data connection. In this case, we are using a specialist driver for SQL Server CE so we tell it to use a separate driver.
  • connection.driver_class with relation to the above, tells NHibernate what driver to actually use.
  • connection.connection_string is the actual connection string to use to to connect to the data (note: if you copy the one auto-created by VS, you will need to change it slightly like the example above).
  • show_sql will cause SQL statements that are executed by NHibernate to be output to the “Output” window. Very useful for debugging and just seeing what is going on under the hood.
  • proxyfactory.factory_class tells NHibernate which assembly to use to generate the proxy classes that are used in the automatic schema generation. In this case, I was using Castle Active Record.
  • mapping tells NHibernate that an assembly contains mapping information to be used by NHibernate (we will create the map in a bit). In this case, this is simply the assembly name of the project.

Once the configuration file is completed (be sure to use the name “hibernate.cfg.xml” – this is what NHibernate will be looking for), check the properties and ensure it’s build action is “Copy if newer” or “Copy always” (I prefer copy always), this will ensure the configuration file is output to the build directory as expected.

That should really do it for the core configuration!

Creating the POCO

The next step is to create the object that we would like to be persisted in the database. Me being a cat lover, I opted for the incredibly complex class definition of:

   1: public class Cat
   2:     {
   3:         public virtual string ID { get; set; }
   4:         public virtual string Name { get; set; }
   5:         public virtual string Type { get; set; }
   6:     }

Glutton for punishment, right? You are probably wondering:

  • Why am I using a string for an ID?
    Why not? The reason in this case is mainly because I was working from an example. I will play with different types for identity soon.
  • Why are the properties all virtual? This is a good question, the reason for this is because the auto-schema generator (in this case Active Record) requires that the properties to be exposed are virtual, so it can override them and do some magic.

So I now have a domain object that I wish to persist, how does the ORM know where to put the data in the database? This is where the “map” comes in..

Creating the Map File

The map file dictates to NHibernate “what goes where” in the database. This is the map file for the “Cat” class created earlier (saved as "Cat.hbm.xml”):

   1: <?xml version="1.0" encoding="utf-8" ?>
   2:  
   3: <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
   4:   namespace="NHibernateTechDay.Domain" assembly="NHibernateTechDay">
   5:  
   6:   <class name="Cat" table="Cat">
   7:  
   8:     <id name="ID">
   9:       <column name="CatID" not-null="false" />
  10:       <generator class="uuid.hex" />
  11:     </id>
  12:  
  13:     <property name="Name">
  14:       <column name="Name" length="16" not-null="true" />
  15:     </property>
  16:  
  17:     <property name="Type" />
  18:     
  19:   </class>
  20:   
  21: </hibernate-mapping>

So, in this file we have:

  • hibernate-mapping – the “namespace” attribute contains the namespace of the domain object that we are mapping, while the “assembly” attribute contains the name of the assembly in which it resides.
  • class dictates the actual name of the class as well as the name of the table the objects will be stored within in the database.
  • id elements contain information about how the identity of the record is both stored and generated.
    • column the name of the database column the identity field is stored in (also marked as “not null” in this case so it must be set).
    • generator is used to specify the generator that is used to create the unique ID. There are a whole bunch of these available.
  • property elements then map the properties of the class to their related fields in the database. Constraints are applied as attributes (you can see each cat must have a name which is a maximum of 16 chars long).

And we are done! Save the file and then be sure to set its “Build Action” (in the properties window) to “Embedded Resource”. This means the XML file will actually become a part of the project assembly. When NHibernate fires up and reads it configuration, it will pick up the “mapping” elements “assembly” attribute and then hunt for XML files in the assembly. Also ensure you use the name “ClassName.hbm.xml” – convention over configuration!

We now [theoretically] have a working NHibernate configuration with a POCO that is ready to be persisted. What should we do next? Write a test!

Testing the Configuration

Using your testing framework of choice, simply add the following test code:

   1: using Xunit;
   2: using NHibernate.Cfg;
   3: using NHibernate.Tool.hbm2ddl;
   4:  
   5: namespace NHibernateTechDay.Tests
   6: {
   7:     public class NHibernate_Configuration
   8:     {
   9:         [Fact]
  10:         public void can_generate_schema()
  11:         {
  12:             var cfg = new Configuration();
  13:             cfg.Configure();
  14:             new SchemaExport(cfg).Execute(false, true, false, false);
  15:         }
  16:     }
  17: }

All we are doing here is basically newing up the NHibernate configuration (which automatically reads everything it needs) and then asking it to export/generate the database schema. If this fails, there will be something wrong with your configuration somewhere. Review and correct as necessary. I had a few errors due to bad configuration and missing SQL Server CE driver files, but I found the error messages to be very explanatory and quickly had them fixed.

Creating the Repository

DDD advocates, or even just those of us that like to write testable &, maintainable code will be familiar with the Repository Pattern. We will create a repository interface to perform the CRUD operations for our Cat data as well:

   1: using System.Collections.Generic;
   2:  
   3: namespace NHibernateTechDay.Domain
   4: {
   5:     public interface ICatRepository
   6:     {
   7:         void Add(Cat cat);
   8:         void Update(Cat cat);
   9:         void Remove(Cat cat);
  10:         Cat GetByID(string id);
  11:         Cat GetByName(string id);
  12:         ICollection<Cat> GetByType(string type);
  13:     }
  14: }

As you can see, we are not defining rocket science here, just the basic operations that we will require our data store to do. We will now create a concrete implementation of this, using NHibernate’s framework to actually do the heavy-lifting.

Creating a Static “Helper” Class

All the work that NHibernate performs takes place within what is called a Session (not to be confused with ASP.NET Sessions). The Session contains all of the reflected object data that NHibernate uses to generate the SQL statements. When the Session fires up and runs off to scan the assemblies designated in it’s configuration file. While this is useful, it is also VERY expensive, reflection is powerful but it doesn’t come cheap! Therefore, it makes sense to create a helper class to keep a reference to the Session so we only ever have to create it once. What pattern do we need here? Yup, the Singleton. Below is the code I used for the static helper class:

   1: using NHibernate;
   2: using NHibernate.Cfg;
   3: namespace NHibernateTechDay
   4: {
   5:     public sealed class NHibernateHelper
   6:     {
   7:         private static readonly ISessionFactory sessionFactory;
   8:         private static ISession currentSession;
   9:  
  10:         static NHibernateHelper()
  11:         {
  12:             sessionFactory = new Configuration().Configure().BuildSessionFactory();
  13:         }
  14:  
  15:         public static ISession GetCurrentSession()
  16:         {
  17:             if (currentSession == null || !currentSession.IsOpen)
  18:                 currentSession = sessionFactory.OpenSession();
  19:  
  20:             return currentSession;
  21:         }
  22:  
  23:         public static void CloseSession()
  24:         {
  25:             if (currentSession == null)
  26:                 return;
  27:  
  28:             currentSession.Close();
  29:         }
  30:  
  31:         public static void CloseSessionFactory()
  32:         {
  33:             if (sessionFactory != null)
  34:                 sessionFactory.Close();
  35:         }
  36:     }
  37: }

Note the singleton ISessionFactory – this is the object that does the heavy reflection stuff. We then have some static utility methods to get the current session and close it etc.

The Cat Repository

   1: using NHibernateTechDay.Domain;
   2: using NHibernate;
   3: using NHibernate.Criterion;
   4:  
   5: namespace NHibernateTechDay.Repositories
   6: {
   7:     public class CatRepository : ICatRepository
   8:     {
   9:         #region ICatRepository Members
  10:  
  11:         public void Add(Cat cat)
  12:         {
  13:             using (ISession session = NHibernateHelper.GetCurrentSession())
  14:             {
  15:                 using (ITransaction trans = session.BeginTransaction())
  16:                 {
  17:                     session.Save(cat);
  18:                     trans.Commit();
  19:                 }
  20:             }
  21:         }
  22:  
  23:         public void Update(Cat cat)
  24:         {
  25:             using (ISession session  = NHibernateHelper.GetCurrentSession())
  26:                 using (ITransaction trans = session.BeginTransaction())
  27:                 {
  28:                     session.Update(cat);
  29:                     trans.Commit();
  30:                 }
  31:         }
  32:  
  33:         public void Remove(Cat cat)
  34:         {
  35:             throw new System.NotImplementedException();
  36:         }
  37:  
  38:         public Cat GetByID(string id)
  39:         {
  40:             using (ISession session = NHibernateHelper.GetCurrentSession())
  41:                 using (ITransaction trans = session.BeginTransaction())
  42:                 {
  43:                     return session.Get<Cat>(id);
  44:                 }
  45:         }
  46:  
  47:         public Cat GetByName(string name)
  48:         {
  49:             using (ISession session = NHibernateHelper.GetCurrentSession())
  50:             {
  51:                 Cat cat = session
  52:                     .CreateQuery("from Cat c where c.Name=:name")
  53:                     .SetString("name", name)
  54:                     .UniqueResult<Cat>();
  55:                 return cat;
  56:             }
  57:         }
  58:  
  59:         public System.Collections.Generic.ICollection<Cat> GetByType(string type)
  60:         {
  61:             using (ISession session = NHibernateHelper.GetCurrentSession())
  62:             {
  63:                 var cats = session
  64:                     .CreateCriteria(typeof (Cat))
  65:                     .Add(Restrictions.Eq("Type", type))
  66:                     .List<Cat>();
  67:                 return cats;
  68:             }
  69:         }
  70:  
  71:         #endregion
  72:     }
  73: }

Quite a bit of content in there, I again recommend going through this article to see how it is put together (a lot of the code was modelled from there). The repository pattern is much loved by DDD and Testing advocates, makes sense – we don’t want to be talking to our data layer directly.

Tests for the Repository

Rather than type out all of the above code in one hit, I actually tested each part of the CRUD operation(s) one at a time and then implemented the code to pass the test. For completeness, here is the test code used:

   1: using NHibernateTechDay.Domain;
   2: using NHibernate;
   3: using NHibernate.Cfg;
   4: using NHibernate.Tool.hbm2ddl;
   5: using Xunit;
   6: using NHibernateTechDay.Repositories;
   7: using System.Collections.Generic;
   8:  
   9: namespace NHibernateTechDay.Tests
  10: {
  11:     public class CatRepository_Tests
  12:     {
  13:         private ISessionFactory _sessionFactory;
  14:         private readonly Configuration _cfg;
  15:  
  16:         private readonly Cat[] _cats = new Cat[]
  17:             {
  18:                 new Cat() { Name="Smithy", Type="Fat"},
  19:                 new Cat() { Name="Floss", Type = "Smelly"},
  20:                 new Cat() { Name="Poppy", Type="Fluffly"},
  21:                 new Cat() { Name="Phoebe's Cat", Type = "Smelly"}
  22:             };
  23:  
  24:         public CatRepository_Tests()
  25:         {
  26:             _cfg = new Configuration();
  27:             _cfg.Configure();
  28:             _sessionFactory = _cfg.BuildSessionFactory();
  29:             SetupContext();
  30:             CreateInitialData();
  31:         }
  32:  
  33:         private void CreateInitialData()
  34:         {
  35:             using (ISession session = _sessionFactory.OpenSession())
  36:                 using (ITransaction trans = session.BeginTransaction())
  37:                 {
  38:                     foreach (var cat in _cats)
  39:                     {
  40:                         session.Save(cat);
  41:                     }
  42:                     trans.Commit();
  43:                 }
  44:         }
  45:  
  46:         private void SetupContext()
  47:         {
  48:             new SchemaExport(_cfg).Execute(false, true, false, false);
  49:         }
  50:  
  51:         [Fact]
  52:         public void can_add_new_cat()
  53:         {
  54:             var cat = new Cat() {Name = "Smithy", Type = "Fat"};
  55:             ICatRepository repository = new CatRepository();
  56:             repository.Add(cat);
  57:         }
  58:  
  59:         [Fact]
  60:         public void can_add_new_cat_and_query_by_id()
  61:         {
  62:             var cat = new Cat() {Name = "Smithy", Type = "Fat"};
  63:             ICatRepository repository = new CatRepository();
  64:             repository.Add(cat);
  65:             
  66:             // use the session to try and load the product.
  67:             using (ISession session = NHibernateHelper.GetCurrentSession())
  68:             {
  69:                 var fromDB = session.Get<Cat>(cat.ID);
  70:                 Assert.NotNull(fromDB);
  71:                 Assert.NotSame(fromDB, cat);
  72:                 Assert.Equal(cat.Name, fromDB.Name);
  73:                 Assert.Equal(cat.Type, fromDB.Type);
  74:             }
  75:         }
  76:  
  77:         [Fact]
  78:         public void can_update_existing_cat()
  79:         {
  80:             var cat = _cats[0];
  81:             cat.Name = "Fluffy Kitty";
  82:             ICatRepository repository = new CatRepository();
  83:             repository.Update(cat);
  84:  
  85:             using (ISession session = NHibernateHelper.GetCurrentSession())
  86:             {
  87:                 var fromDB = session.Get<Cat>(cat.ID);
  88:                 Assert.Equal(cat.Name, fromDB.Name);
  89:                 Assert.NotSame(cat, fromDB);
  90:             }
  91:         }
  92:  
  93:         [Fact]
  94:         public void can_get_cat_by_id()
  95:         {
  96:             ICatRepository repository = new CatRepository();
  97:             var fromDB = repository.GetByID(_cats[0].ID);
  98:  
  99:             Assert.NotNull(fromDB);
 100:             Assert.NotSame(_cats[0], fromDB);
 101:             Assert.Equal(_cats[0].ID, fromDB.ID);
 102:         }
 103:  
 104:         [Fact]
 105:         public void can_get_cat_by_name()
 106:         {
 107:             ICatRepository repository = new CatRepository();
 108:             var exp = _cats[0];
 109:             var fromDB = repository.GetByName(exp.Name);
 110:  
 111:             Assert.NotNull(fromDB);
 112:             Assert.NotSame(exp, fromDB);
 113:             Assert.Equal(exp.ID, fromDB.ID);
 114:         }
 115:  
 116:         [Fact]
 117:         public void can_get_cats_by_type()
 118:         {
 119:             ICatRepository repository = new CatRepository();
 120:             var fromDB = repository.GetByType("Smelly");
 121:  
 122:             Assert.Equal(2, fromDB.Count);
 123:             Assert.True(IsInCollection(_cats[1], fromDB));
 124:             Assert.True(IsInCollection(_cats[3], fromDB));
 125:         }
 126:  
 127:         private bool IsInCollection(Cat cat, ICollection<Cat> fromDB)
 128:         {
 129:             foreach (var catinDB in fromDB)
 130:             {
 131:                 if (cat.ID == catinDB.ID)
 132:                     return true;
 133:             }
 134:             return false;
 135:         }
 136:     }
 137: }

Whoa! Hang on a Minute! Where’s the Difference?

Yup, at this point I was first thinking the same.. Up until now we have done pretty much the same as we would normally do:

  • Create some POCO’s which represent our domain model.
  • Create an interface for the repository.
  • Implement the interface, taking the POCO’s and then creating some DAL code to persist them.

All we have actually done is just waste time messing around with creating XML files, right?

Wrong!

It has not been a waste of time! The benefit here is maintenance. Think about it, if we added one single property to a class, we would then need to go through all the CRUD operations within the DAL (or perhaps stored procedures) and update their parameters. All we need to do with NHibernate is add a single “property” element to the mapping file and we are done. Also remember that the database schema can be generated for you. This makes development much quicker where the domain model can be evolving quickly and you are struggling to keep the database relational model up to date. All this goes away with an ORM.

In Summation

A really useful day. I had a lot of fun having finally got the chance to sit down and get a NHibernate app up and running. Am I now an NHibernate expert? No of course not! But this is not the aim of the “Tech Days”, the point of these days is to actually give me a chance to sit down and give a tech a fair shot at convincing me to invest more time into it.

I found NHibernate to be relatively quick to understand, especially once you have these core concepts down:

  • The hibernate.cfg.xml configures NHibernate.
  • The ClassName.hbm.xml files map the object data to fields in the database.
  • The Session is used to perform a “Unit of Work”.. I started off thinking these are essentially SQL Transactions. While this is not technically correct (you perform Transactions within a Session) – the concept is very similar. A Unit of Work essentially tracks the changes made to objects (without changing the database) – when the Session is flushed, then the database is updated (using Transactions as well). This provides a real safe pattern to work with.
  • http://www.thisoldtoy.com/new-images/images-ok/1000s/fp1059-shape-ball.JPGThe difference between how Object models work and Relational Databases is known as the “Object-Relational Impedance Mismatch”. This is just nerd-speak for “they are different”, we have all played with that stupid ball and shape thing right? Same thing, you can’t fit a square in a triangle hole.
  • NHibernate has some up-front cost both in terms of development and application performance. However, the ease of maintenance it creates will likely pay you back very quickly. The lack of requirement for a real DAL can mean you focus on writing the code to solve the problem - not the problem of saving the solutions data for the original problem!

Now, I didn’t intend for this post to be any sort of tutorial. More of an overview of what I did on my first tech day, a chance to see some of the code and hopefully explain what I have learned in a way that helps others.

If anyone feels that I have misinterpreted any of the information I came across on my travels, please feel free to comment and put me straight!

Happy NHibernating!