Ryan LanciauxNew Media Mercenary

RhinoCommons, NHibernate and ASP.NET MVC Part 5 - LINQ to NHibernate

June 3, 2008 by ryan

Settings 

Up until now, we've been using  NHibernate Query Generator for all of our data access. Although this is a great way to retrieve our data, there is another option we can play around with -- LINQ for NHibernate. To set this up in our existing application (see Part 1, Part 2, Part 3 and Part 4 on creating the ASP.NET MVC Application) we'll first need to grab the code out of subversion https://rhino-tools.svn.sourceforge.net/svnroot/rhino-tools/experiments/NHibernate.Linq/ and build it using MSBuild or Visual Studio. After that we want to add a reference to it in our application.

Simple Code 

Next we'll want to update our controller to use Linq for NHibernate instead of NHQG (Service layer would be better place for this type of code but since this is a demo it'll be okay -- for more on using a service layer to handle all the repository code check out Michael Hanney's post on ActiveRecord, NHibernate and ASP.NET MVC). The initial NHGQ code is:

var p = Repository<Product>.FindOne(Where.Product.Title == ID);


Our LINQ for NHibernate query will look like this:

            var p = (from item in UnitOfWork.CurrentSession.Linq<Product>()

                        where item.Title == ID

                        select item).First();

It's pretty obvious that the Linq code is a bit longer than the NHQG code. Although that in itself is not a bad thing, it may turn some people away. Momentarily, we'll see some scenarios where Linq for NH is very useful.

Paging and Sorting 

One nice thing we can easily do with Linq for NHibernate is page and sort our data. If we simply want to get a list of all products it would look like this.  

            var p = (from item in UnitOfWork.CurrentSession.Linq<Product>()

                    select item

                    ).ToList()

To page/sort the data it's just a slight addition to the list all code.

            int itemsPerPage = 5;

            int startIndex = (ID.Value - 1)* itemsPerPage;

 

            var p = (from item in UnitOfWork.CurrentSession.Linq<Product>()

                    orderby item.Title ascending

                    select item

            ).Skip(startIndex).Take(itemsPerPage).ToList();


More Advanced Usage

Kyle Baley's article on Linq for Nhibernate shows a more interesting use for Linq for NHibernate; we can create a generic method that adds query criteria on the fly. This would make our code much more reusable so we're going to go ahead and make a demo class heavily based on these concepts.

    public class QueryHandler<T>

    {

        private IList<linqExpression.Expression<Func<T, bool>>>  _criteria;

        public QueryHandler()

        {

            _criteria = new List<linqExpression.Expression<Func<T, bool>>>();

        }

        public void AddCriteria(linqExpression.Expression<Func<T, bool>> LambdaFunc)

        {

            _criteria.Add(LambdaFunc);

        }

 

        public IList<T> GetList()

        {

            var query = from item in UnitOfWork.CurrentSession.Linq<T>()

                        select item;

            //Tack on our query Criteria

            foreach (var criterion in _criteria)

            {

                query = query.Where<T>(criterion);

            }

            return query.ToList();

        }

    }

Here, we've created a class that has a private list of criteria, a method to add criteria to the list and a method to get the list based on the given criteria. I realize it may be a little intimidating but we can perfom most of our select queries through this method due to the use of Generics. 

Updating the controllers to use this functionality is not too difficult. For pages that simply retrieve lists we call the GetList method without specifying any criteria:

            var queryHandler = new QueryHandler<Product>();

            var p = queryHandler.GetList().Skip(startIndex).Take(itemsPerPage).ToList();

 Pass in new lambda expressions to add query criteria

            var queryHandler = new QueryHandler<Product>();

            queryHandler.AddCriteria(item => item.Title == ID);

 

            var p = queryHandler.GetList().First();


Now we see there are multiple options for interacting with our ActiveRecord Repository. Please let me know of any changes that you would make. I've updated the demo code in Assembla -- http://svn2.assembla.com/svn/NHibernateTest - Standard disclaimer does apply (some of the code is less than ideal but for learning it should be okay).

 




RhinoCommons, NHibernate and ASP.NET MVC Part 4 - The MVC Application

May 26, 2008 by ryan

Finally we're to the point where we can see all our hard work come together. We have most of the hard work done but we still have a lot of ground to cover. If you haven't been following along, please check out Setting Up The Assemblies, Configuring the Application and Developing the Model.

Unit of Work

In some of my initial tests with NHibernate and ASP.NET MVC Pattern I kept seeing the benefits of having a Unit of Work or Session Per Request (that is opening and closing the NHibernate session at the begining and end of the http request respectively). To Recap a little, I started to write my own Session Per Request, however, Chad Myers pointed me to the Rhino Commons project which already implemented this. I think it's worthwhile becuase I don't really like putting NHibernate session code in my controller plus as Martin Fowler writes

A Unit of Work keeps track of everything you do during a business transaction that can affect the database. When you're done, it figures out everything that needs to be done to alter the database as a result of your work.

Luckily, with RhinoCommons, it's pretty easy to implement this pattern (check out Ayende's post on this). In a standard web forms application, we would normally create a Global.aspx that inherits UnitOfWorkApplication. Since we're using ASP.NET MVC, however, we don't necessarily want to go that route. As Michael Hanney notes on his post on MVC and Castle we can have our Global.asax inherit from UnitOfWorkApplication.

 

    public class GlobalApplication : UnitOfWorkApplication

    {

 

        public override void Application_Start(object sender, EventArgs e

        { 

            base.Application_Start(sender, e); 

            RegisterRoutes(RouteTable.Routes); 

        }

...

     }

If you know of another way to do this, please be sure to let me know. Also, the routing still works as it normally would -- we're just running this code first to instantiate the UnitOfWork.

In our controllers we can call our reference our Hibernate repositories and classes without specifying an ISession.

        public ActionResult InsertProductGroup(string Title)

        {

            ProductGroup pg = new ProductGroup();

            pg.Title = Title;

            Repository<ProductGroup>.Save(pg);

            UnitOfWork.Current.Flush();

            return RenderView("AddProductGroup");

        }

Notice we're still flushing our data -- but it makes the controllers a LOT cleaner. Imagine if we had to instantiate and clean up our session in each controller.

One further thing, the UnitOfWorkApplication supports both short and long conversations. I'm not going to go too much into that but if your application requires keeping objects around for a couple HTTP Requests before saving to the database Long Conversation may be the way to go. See Ayende's Wiki for more on this

NHibernate Query Generator
In the first post of this series we looked at what it takes to setup NHibernate Query Generator (NHQG from here out). Now we get to use it to make some really nice looking code (in a later post, however, we'll be using LINQ to NHibernate). If we've set up the tool as mentioned in the first post (listed earlier), all we have to do is run the tool and make sure the generated code is added to the project.

NHQG lets us use a fluent interface to set filters on our Hibernate queries; this results in code that, in my opinion, is very easy to write and understand later on. If we wanted to Find one Product with a specific title, our code would look something like this:

        public ActionResult ViewProduct(string ID)

        {

            var p = Repository<Product>.FindOne(Where.Product.Title == ID);

            if (p != null)

            {

                return RenderView("DisplayProduct",

                                  p); 

            }

            return RenderView("DisplayProduct");

        }

The Where.Product.Title == ID is all from the NHQG autogenerated code. Now we actually have something to show for all our configuration and setup work. Soon, we're going to take a look at using LINQ to NHibernate instead of NHQG. In the meantime, I have checked all the code in to my svn at Assembla. My standard disclaimer on demo code applies here too :) This is just demo code for the sake of example. Some of it is far from ideal but great for learning.

Check out / update with SubVersion from the following location: http://svn2.assembla.com/svn/NHibernateTest





RhinoCommons, NHibernate and ASP.NET MVC Part 3 - The Model

May 22, 2008 by ryan

We're going to keep moving now that everything is setup (see part one for setup) and configured (see part two for configuration).

First off we are going to create our classes. The original classes and SQL tables are posted below (these may look familiar -- they are from my initial NHibernate post)


Initial Product

    public class Product

    {

        private IList<Product> _RelatedProducts;

        private IList<ProductGroup> _ProductGroups;

 

        public Product()

        {

            _RelatedProducts = new List<Product>();

            _ProductGroups = new List<ProductGroup>();

        }

 

        public virtual string ID { get; private set; }

        public virtual string Title { get; set; }

        public virtual string ImagePath { get; set; }

        public virtual string Description { get; set; }

 

        public virtual IList<Product> RelatedProducts

        {

            get { return _RelatedProducts; }

            set { _RelatedProducts = value; }

        }

        public virtual IList<ProductGroup> ProductGroups

        {

            get { return _ProductGroups; }

            set { _ProductGroups = value; }

        }

    }


Initial ProductGroup

    public class ProductGroup

    {

        public virtual string ProductGroupID { get; set; }

        public virtual string Title { get; set; }

        public virtual IList<Product> Products { get; set; }

    }


SQL Tables

CREATE TABLE [dbo].[SimpleProducts](

    [ProductID] [char](32) NOT NULL,

    [Title] [nvarchar](50) NOT NULL,

    [ImagePath] [nvarchar](300) NULL,

    [Description] [nvarchar](500) NULL

)

 

CREATE TABLE [dbo].[RelatedProductsLookup](

    [ProductID] [char](32) NOT NULL,

    [RelatedProductID] [char](32) NOT NULL

)

 

CREATE TABLE [dbo].[ProductsProductGroupsLookup](

    [ProductGroupID] [char](32) NULL,

    [ProductID] [char](32) NULL

)

 

CREATE TABLE [dbo].[ProductGroups](

    [ProductGroupID] [char](32) NOT NULL,

    [Title] [nvarchar](50) NULL


ActiveRecord Classes
In a traditional NHibernate application, we would write usually our mapping files at this time(see my other NHibernate post for more on that). Since we're using the ActiveRecord pattern, however, we can specify all our mappings inline with the classes. It is important to note that this would not be a pure domain because we're placing our mappings inside the model. Warning if you're sensitive to using Attributes this may not be the code for you...

Our classes will now begin with an ActiveRecord attribute over the class; our properties will begin with Property/HasAndBelongsToMany/etc. Please note, for the sake of the example, I'm being extremely verbose with my attributes. If your table/column names match the class/property names, some of the additional info in the attribute is not necessary.

    [ActiveRecord(Table="SimpleProducts")]

    public class Product

    {

        private IList<Product> _RelatedProducts;

        private IList<ProductGroup> _ProductGroups;

 

        public Product()

        {

            _RelatedProducts = new List<Product>();

            _ProductGroups = new List<ProductGroup>();

        }

 

 

        [PrimaryKey(Column="ProductID", Generator=Castle.ActiveRecord.PrimaryKeyType.UuidHex)]

        public virtual string ID { get; private set; }

 

        [Property(NotNull=true, Length=50, Column="Title")]

        public virtual string Title {get; set; }

 

        [Property(Length=300, NotNull=false, Column="ImagePath")]

        public virtual string ImagePath { get; set; }

 

        [Property(NotNull = false, Length = 500, Column="Description")]

        public virtual string Description { get; set; }

 

        [HasAndBelongsToMany(Table="RelatedProductsLookup", ColumnKey="ProductID", ColumnRef="RelatedProductID")]

        public virtual IList<Product> RelatedProducts

        {

            get { return _RelatedProducts; }

            set { _RelatedProducts = value; }

        }

 

        [HasAndBelongsToMany(Table="ProductsProductGroupsLookup", ColumnKey="ProductID", ColumnRef="ProductGroupID")]

        public virtual IList<ProductGroup> ProductGroups

        {

            get { return _ProductGroups; }

            set { _ProductGroups = value; }

        }

    }


    [ActiveRecord(Table="ProductGroups")]

    public class ProductGroup

    {

        [PrimaryKey(Column="ProductGroupID", Generator=Castle.ActiveRecord.PrimaryKeyType.UuidHex)]

        public virtual string ProductGroupID { get; set; }

 

        [Property(NotNull=true, Length=50, Column="Title")]

        public virtual string Title { get; set; }

 

        [HasAndBelongsToMany(Table="ProductsProductGroupsLookup", ColumnKey="ProductGroupID", ColumnRef="ProductID")]

        public virtual IList<Product> Products { get; set; }

    }


Repository Object
Another added benefit of using the Castle Active Record library is that we can use the Repository<T> for all of our object persistence. Instead of creating our own implementation of IRepository, we can write code like this to save / retrieve / update objects.

Selecting an object (our product IDs are HEX UUID's so this is not exactly accurate)

var p = Repository<Product>.Get(23); 


Saving / Updating

Repository<Product>.Save(p);


The repository is pretty nice -- we can save all of our objects outside of the domain, which makes for a much cleaner design. Next time, we'll be looking at the extremely simple MVC application powered by this model and NHibernate Query Generator. Continue to Part 4

 




RhinoCommons, NHibernate and ASP.NET MVC Part 2 - Configuration

May 20, 2008 by ryan

Following up on my last post, we're going to setup a project and get everything ready for the code (we'll be doing the coding very soon -- I promise).  First off, create a new MVC application (make sure you're using the latest preview from codeplex) and a new Class library. From here, you'd normally want to want to do some TDD to create your model but that's a little outside the scope of this example.

Add the references to Boo, Castle, NHibernate, RhinoCommons and Log4Net to the MVC application. In the class library, add Castle.ActiveRecord, Iesi.Collections, NHibernate, Rhino.Commons and Rhino.Commons.NHibernate. Switch over to your web.config file and Underneath the ConfigSections node add the following custom tags:

        <section name="activerecord" type="Castle.ActiveRecord.Framework.Config.ActiveRecordSectionHandler, Castle.ActiveRecord" />

        <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >

            <section name="Rhino.Commons.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>

        </sectionGroup>   

Next add the specific custom tag properties somewhere after the </ConfigSections> : 

    <activerecord isWeb="true">

        <config>

            <add key="hibernate.connection.driver_class"

                value="NHibernate.Driver.SqlClientDriver" />

            <add key="dialect"

                value="NHibernate.Dialect.MsSql2005Dialect" />

            <add key="hibernate.connection.provider"

                value="NHibernate.Connection.DriverConnectionProvider" />

            <add key="hibernate.show_sql"

                value="false" />

            <add key="connection.connection_string" value="Data Source=___________;Initial Catalog=NHibernateTest;Integrated Security=True" />

        </config>

    </activerecord>

These active record settings should be pretty straight-forward but for more information on specific dialects or other properties check out the Castle's Configuration Reference. Be sure to swap out my Data Source and Initial Catalog settings with yours.

    <applicationSettings>

        <Rhino.Commons.Properties.Settings>

            <setting name="WindsorConfig"

                    serializeAs="String">

                <value>windsor.boo</value>

            </setting>

        </Rhino.Commons.Properties.Settings>

    </applicationSettings>

With this tag, we're telling Castle that we're going to configure Windsor with a boo file instead of an xml document. Ayende Rahien pointed out in the comments that this tag is no longer necessary as long as the file is named windsor.boo

Windsor Configuration With Boo 

Up until this point, we've been dealing with the web.config to configure our application -- now we want to configure Windsor but instead of using another xml file, we're going to use a boo file. What is Boo you might ask? According to wiki...

Boo is an object oriented, statically typed programming language developed starting in 2003, which seeks to make use of the Common Language Infrastructure support for Unicode, internationalization and web style applications, while using a Python-inspired syntax and a special focus on language and compiler extensibility. 

The mere fact that you can use a programming language instead of an XML file to configure Windsor is pretty sweet. I would be lying if I claimed to know boo very well, however, the Exesto and Hibernating-Forums samples (from the Rhino-Tools project) have enough information to get you up and running. I plan on learning boo well enought to create my own config files from scratch but in the mean time, here's what my boo file looks like (heavily influenced by the sample applications mentioned above)...

import Rhino.Commons

import System.Reflection

import Castle.Core

import Castle.Services.Transaction

import Castle.Facilities.AutomaticTransactionManagement

 

activeRecordAssemblies = ( Assembly.Load("ProductModelActiveRecord"), )

 

Component("active_record_repository", IRepository, ARRepository)

Component("active_record_unit_of_work",

    IUnitOfWorkFactory,

    ActiveRecordUnitOfWorkFactory,

    assemblies: activeRecordAssemblies )

Check out Ayende's comment for a more succinct way to register these components. As you might have noticed, I still have to set up the colors for boo files in Visual Studio :) What this file is doing is loading the assemblies and setting up the repository / unit of work (we'll see those in action in the next parts of this series). Your project configuration should be all set. Next time we will actually be writing some code so stick around for that. View Part Three - The Model





RhinoCommons, NHibernate and ASP.NET MVC Part 1 - Setup

May 19, 2008 by ryan

After my last post about the unit of work with NHibernate, Chad Myers mentioned that I should take a look at Ayende's Rhino Commons (because the Unit of Work stuff is already being handled). Since I am not a big fan of reinventing the wheel I decided I would give it a shot. There's going to be another post in the near future about how to get Rhino Commons, Castle ActiveRecord and ASP.NET MVC working together but for now, it would be good to make sure all the necessary components are installed on your machine.

  1. Make sure you have a subversion client -- Tortoise SVN or the command prompt is what I use but any subversion client should be fine.
  2. If you don't already have Nant installed on your machine download and install that
  3. Download and build the following (Ayende mentions, the trick is not opening in Visual Studio):
  4. Next you're going to want to setup the NHibernate Query Generator (we're going to use Linq to NHibernate in a later example but for now get this installed). This should be a part of the Rhino-tools package but if you want you can download the binaries. Then setup the application as an external tool in Visual Studio (my settings are posted below). see James Hollingworth's post for more info
    • Command: C:\program files\nhqg\NHQG.exe
    • Arguments: /Lang:cs /InputFilePattern:$(BinDir)/ProductModelActiveRecord.dll /OutputDirectory:$(ProjectDir)/Queries /BaseNamespace:Queries
    • Initial Directory: $(TargetDir)
  5. Finally make sure you're running the preview 3 drop of the ASP.NET MVC Framework -- you can get that here from CodePlex
You should now have everything setup. It may be good to take a look at the Exesto application in the rhino-tools\SampleApplications directory to get an introduction to the Rhino-tools / binsor / castle settings that we'll be looking at later on. Finally, if you are not familiar with the ASP.NET MVC Framework take a look Fredrik Normen's step by step guide. In the next couple of days, I will be posting how to wire these tools together for quick web application development. Stay Tuned.




NHibernate Session Per Request

May 11, 2008 by ryan

A little earlier on, I had posted a simple example of NHibernate in an MVC application as well as some initial questions I had about NHibernate Session management. In response to my question, Matt Hinze mentioned that the session should be transparent to the controller and posted some links to various articles explaining how to achieve this. I finally had a chance to play around with this type of session management and spent most of my time looking through Billy McCafferty's NHibernate Best practices on CodeProject. As I've mentioned before, I'm very new to NHibernate so some of the things I'm doing may not be ideal.

HTTP Module

First off, I created an HTTP module; this is where the session will be opened and closed (by accessing the session manager class). You'll notice on the Init that event handlers have been added. I'm using the same session manager class that I was using in my last example, however, now it's being initialized / closed here (rather than in each controller action).

    public class SessionModule : IHttpModule

    {

        private ISession _session;

 

        public void Init(HttpApplication context)

        {

            context.BeginRequest += new EventHandler(OpenSession);

            context.EndRequest += new EventHandler(CloseSession);

        }

 

        private void OpenSession(object sender, EventArgs e)

        {

            _session = SessionManager.GetCurrentSession();

        }

 

        private void CloseSession(object sender, EventArgs e)

        {

            _session.Flush();

            _session.Close();

        }

 

        public void Dispose()

        {

            _session = null;

        }

    }


Next, I added this class to the HttpModules section of the web.config

<add name="SessionModule" type="ProductModel.Session.SessionModule"/>

Controller Code

Finally, since the NHibernate Session is being created / closed in the HTTP Module, I no longer have to use the using statement in every controller. Instead, I'm just setting a ISession = to the SessionManagers Current session.

   public ActionResult AddProduct(string ID)

   {

       ISession session = SessionManager.GetCurrentSession();

       return RenderView("AddProduct",

           new SimpleProductRepository(session).List());

   }

Wrapping Up

There's still a bit more I want to explore with this method of Session Management (maybe transactions). Also I would be interested to test out the threading / performance implications of going this route. I've updated the code on my assembla site. If you're interested, you can check it out here http://svn2.assembla.com/svn/NHibernateTest/ (keep in mind it's demo code -- not anything remotely resembling anything I would use in production). I would really appreciate any suggestions / feedback that you may have!

kick it on DotNetKicks.com  




Using NHibernate (years after I should have been)

April 30, 2008 by ryan

As mentioned in my last post, I've been starting to use NHibernate in some of my more recent projects. I checked it out years ago and I completely hated it (maybe becuase I was a newer developer -- not totally sure). More recently, however, I've realized some of the benefits of Domain Driven Design and thought its about time to give Hibernate another shot. I'm admittedly pretty new to Hibernate so any feedback would be appreciated!

Classes 

    public class ProductGroup

    {

        public virtual string ProductGroupID { get; set; }

        public virtual string Title{ get; set;}

        public virtual IList<SimpleProduct> Products { get; set; }

    } 

    public class SimpleProduct

    {

        private IList<SimpleProduct> _relatedProducts;

 

        public virtual string ID { get; set; }

        public virtual string Title { get; set; }

        public virtual string ImagePath { get; set; }

        public virtual string Description { get; set; }

        public virtual IList<SimpleProduct> RelatedProducts

        {

            get{ return _relatedProducts; }

            private set { _relatedProducts = value;  }

        }

 

        public virtual void AddRelatedProduct(SimpleProduct product)

        {

            if (_relatedProducts == null)

                _relatedProducts = new List<SimpleProduct>();

            _relatedProducts.Add(product);

        }

 

    }

We're going to start out with some very contrived classes (normally would start with tests but this is just for the hibernate concepts)... These classes should be pretty straight forward; they consist only of some basic properties and list methods.

Data Tables 

Next, we need to create our tables to hold the data coming from the Classes -- I've created a product table / product group table and a lookup table for both (there are many-to-many references in both SimpleProduct and ProductGroup). 

CREATE TABLE [dbo].[SimpleProducts](
    [ProductID] [char](32) NOT NULL,
    [Title] [nvarchar](50) NOT NULL,
    [ImagePath] [nvarchar](300) NULL,
    [Description] [nvarchar](500) NULL
)
   
CREATE TABLE [dbo].[RelatedProductsLookup](
    [ProductID] [char](32) NOT NULL,
    [RelatedProductID] [char](32) NOT NULL
)

CREATE TABLE [dbo].[ProductsProductGroupsLookup](
    [ProductGroupID] [char](32) NULL,
    [ProductID] [char](32) NULL
)

CREATE TABLE [dbo].[ProductGroups](
    [ProductGroupID] [char](32) NOT NULL,
    [Title] [nvarchar](50) NULL
)    

 You will want to set the Primary Key of the SimpleProduct and Product Groups table to be the ID. 

Hibernate Mappings

Next we're on to the Hibernate mapping files. These files link the columns in the database to the fields/properties in the domain classes. Please check the project documentation for more detailed information on the Hibernate Schemas.

<?xml version="1.0" encoding="utf-8" ?>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"

                  namespace="ProductModel" assembly="ProductModel" default-lazy="false">

  <class name="ProductGroup" table="ProductGroups">

    <id name="ProductGroupID">

      <column name="ProductGroupID" sql-type="char(32)" not-null="true" />

      <generator class="uuid.hex" />

    </id>   

    <property name="Title">

      <column name="Title" length="50" not-null="true" />

    </property>   

    <bag name="Products"  table="ProductsProductGroupsLookup" lazy="false">

      <key column="ProductGroupID" />

      <many-to-many class="SimpleProduct" column="ProductID" />     

    </bag>       

  </class

</hibernate-mapping>

Inside the hibernate mapping tag, we have an element called class where we're defining the relationship between the class and the table in the database. From there we're defining the properties -- Most of this should be pretty straight forward but there are a couple things I would like to focus on. 

    <id name="ProductGroupID">

      <column name="ProductGroupID" sql-type="char(32)" not-null="true" />

      <generator class="uuid.hex" />

    </id>   

 This node is defining the unique ID for the class -- the ID is being created for each item and looks a little something like this: 46abbefc08d14b49a5d15c8a4dd69ff2

    <bag name="Products"  table="ProductsProductGroupsLookup" lazy="false">

      <key column="ProductGroupID" />

      <many-to-many class="SimpleProduct" column="ProductID" />     

    </bag>       

  </class

Here we're defining a many-to-many relationship with Products -- this is bumping up against the lookup table we defined earlier to get all SimpleProducts associated with this class. currently the bag is defining a many-to-many relationship. We're going to assume that a product can exist under any number of product groups -- for instance if we have a video game system it could be under both the Home Entertainment and Electronics product group. See the project documentation for other types of relationships (one-to-many, etc).

SimpleProduct Mapping:

<?xml version="1.0" encoding="utf-8" ?>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"

                  namespace="ProductModel" assembly="ProductModel" default-lazy="false">

  <class name="SimpleProduct" table="SimpleProducts">

    <id name="ID">

      <column name="ProductID" sql-type="char(32)" not-null="true" />

      <generator class="uuid.hex" />

    </id>   

    <property name="Title">

      <column name="Title" length="50" not-null="true" />

    </property>   

    <property name="ImagePath">

      <column name="ImagePath" length="300" not-null="false" />

    </property>   

    <property name="Description">

      <column name="Description" length="500" not-null="false" />

    </property>       

    <bag name="RelatedProducts"  table="RelatedProductsLookup" lazy="false">

      <key column="ProductID" />

      <many-to-many class="SimpleProduct" column="RelatedProductID" />     

    </bag>       

  </class

</hibernate-mapping>
 

 

Accessing The Data

Now that we've defined all of our mappings its time to create some methods to access our data.

    public class ProductGroupRepository

    {

        private ISession _session;

 

        public ProductGroupRepository(ISession session)

        {

            _session = session;

        }

 

        public ProductGroup GetByTitle(string Title)

        {

                return _session.CreateCriteria(typeof(ProductGroup))

                    .Add(Expression.Eq("Title", Title))

                    .UniqueResult<ProductGroup>();

        }

 

        public IList<ProductGroup> List()

        {

                return _session.CreateCriteria(typeof(ProductGroup))

                    .List<ProductGroup>();

 

        }

    }

 

The ISession is the Hibernate object to use when accessing the data -- from what it looks like, these should pretty much line up with a unit of work (More on that here).

In the other methods of the class, we first need to define what type of object we're looking for. In case it's not obvious, we're specifying that in the CreateCriteria section. In the List() method, we're returning a list (of ProductGroups) -- there are no filters or other criteria defined for this operation. In the GetByTitle() method, however,  we're stating that the Title for the product must match the Parameter 'Title'.

Demo Application

I've created a quick demo application using MVC and NHibernate. As I said, I'm still very new to hibernate so there's a lot I'm trying to learn (for instance where the ISession should initially be created and closed in an MVC application). Please let me know of any way that this could be better -- generally I'm writing this to help solidfy my thoughts on the technology, help others (hopefully) and also improve from others feedback. 

If you have tortoise or some other svn client, you can grab the code from http://svn2.assembla.com/svn/NHibernateTest/ -- You will need to change the database connection to whatever your db is. Again I would like to reiterate, it's demo code -- not even close to anything that I would ever use in production but worked out okay for testing. 

kick it on DotNetKicks.com

 

Disclaimer: Use this code at your own risk. 








© 2008 Ryan Lanciaux :: powered by BlogEngine.NET