Showing posts with label NHibernate. Show all posts
Showing posts with label NHibernate. Show all posts

Saturday, February 20, 2010

No configuration file with NHibernate

Note: This post is based on NHibernate version 2.1 with Fluent NHibernate version 1.0 RTM.

In my previous post about Fluent NHibernate, I explained how to use Fluent NHibernate to replace your NHibernate mapping files (.hbm.config) with c# code. In this post I'll show you how to also replace the  NHibernate configuation file (.cfg.xml) with code.

This time you don't need to add references to any special library. Converting the config file to code is very, very easy. The config file is made up of a bunch of dictionary entries where each entry has a key (the NHibernate setting name) and a value (the actual value for that setting). The NHibernate has a configuration API that works the same way. So you can just take out the line that loads your config file (config.Configure("PocoLib.cfg.xml");) with a series of calls config.Properties.Add().

Before (with a .cfg.xml file) After (with code)

<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
  <session-factory>
    <property name="connection.provider">
      NHibernate.Connection.DriverConnectionProvider
    </property>
    <property name="connection.driver_class">
      NHibernate.Driver.SqlClientDriver
    </property>
    <property name="connection.connection_string">
      Server=xp-octosql;database=PocoLib;Integrated Security=SSPI;
    </property>
    <property name="dialect">
      NHibernate.Dialect.MsSql2005Dialect
    </property>
    <property name="show_sql">
      true
    </property>
    <property name='proxyfactory.factory_class'>
      NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu
    </property>
    <property name="adonet.batch_size">16</property>
  </session-factory>
</hibernate-configuration>

Configuration config = new Configuration();

config.Properties.Add("connection.provider", "NHibernate.Connection.DriverConnectionProvider, NHibernate");

config.Properties.Add("connection.driver_class", "NHibernate.Driver.SqlClientDriver");

config.Properties.Add("dialect", "NHibernate.Dialect.MsSql2005Dialect");

config.Properties.Add("connection.connection_string", "Server=xp-octosql;database=PocoLib;Integrated Security=SSPI;");

config.Properties.Add("show_sql", "true");

config.Properties.Add("proxyfactory.factory_class", "NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu");

config.Properties.Add("adonet.batch_size", "16");

  Fluently.Configure(configuration)
        .Mappings(m => m.FluentMappings.AddFromAssemblyOf<CustomerCRUDTests>())
        .BuildConfiguration();

_factory = config.BuildSessionFactory();

Saturday, December 26, 2009

Fluent NHibernate Tutorial

Note: This post is based on NHibernate version 2.1 with Fluent NHibernate version 1.0 RTM.

This tutorial introduces Fluent NHibernate. Fluent NHibernate is a "code oriented" alternative to configuring NHibernate maping using XML configuration files.

This tutorial is based on same project entities defined in my previous NHibernate tutorial. At end of this tutorial, you will have the exact same result but with entities configured using Fluent NHibernate instead of an XML configuration file.

1. Open you the Solution PocoLib.

2. Add a reference to FluentNHibernate in the PocoLibTests

3. Add a new class named CustomerMap.cs to the PocoLibTests project. The source code for CustomerMap.cs is in the left column in the table below. The code speaks for itself; it's almost identical in structure to the XML configuration file.

CustomerMap.cs Customer.hbm.xml

using FluentNHibernate.Mapping;
using PocoLib;

namespace PocoLibTests
{
  public class CustomerMap : ClassMap<Customer>
  {
    public CustomerMap()
    {
      Not.LazyLoad();

      Id(cust => cust.ID, "CustomerID");

     
      Map(cust => cust.FirstName);
      Map(cust => cust.LastName);
      Map(cust => cust.BirthDate);

      Component<Address>(cust => cust.Address,
        component =>
        {
          component.Map(address => address.Street);
          component.Map(address => address.State);
          component.Map(address => address.Country);
          component.Map(address => address.ZipCode);
        });
    }
  }
}








<class name="Customer"
        lazy="false">

  <id name="ID" column="CustomerID">
    <generator class="native"/>
  </id>

  <property name="FirstName"/>
  <property name="LastName"/>
  <property name="BirthDate"/>

  <component name="Address">


    <property name="Street"/>
    <property name="State"/>
    <property name="Country"/>
     <property name="ZipCode"/>
  </component>

</class>

4. Now, open the test class. We must change the Setup method and replace the line that loads the the configuration from Customer.hbm.xml to load it using Fluent Nhibernate.

[SetUp]
public void Setup()
{
    Configuration configuration = new Configuration();
    configuration.Configure("PocoLib.cfg.xml");

    configuration.AddXmlFile("Customer.hbm.xml");

    Fluently.Configure(configuration)
        .Mappings(m => m.FluentMappings.AddFromAssemblyOf<CustomerCRUDTests>())
        .BuildConfiguration();

    _factory = configuration.BuildSessionFactory();
}

5. Delete the Customer.hbm.xml file from the PocoLib project and from the /debug/bin/ folder of the PocoLibTests project.

6. You can now run the tests tests.

That's it for this quick Fluent NHibernate tutorial.

Sunday, November 8, 2009

LINQ to NHibernate Tutorial

Note: This post applies to NHibernate 2.1 with LINQ to NHibernate version 1.0.

Starting with LINQ to NHibernate is really simple; you just have to add a reference to NHibernate.Linq.dll to your host project (a test project, an ASP.NET app).

Here is a unit test to demonstrate a simple LINQ query.  This test uses the same entities defined in my previous NHibernate tutorial.

[Test]
public void SelectWithPredicate()
{
    using (var session = _factory.OpenSession())
    {
        //Act
        var customers = from cust in session.Linq<Customer>()
                        where cust.LastName.StartsWith("St")
                        select cust;
        //Assert
        Assert.AreEqual(1, customers.Count());
    }
}

The Linq() method is an extension method defined in NHibernate.Linq.dll. It provides the entry point to execute Linq queries on your NHibernate entities. When you run that test, NHibernate generates the following statement:

SELECT count(*) as y0_ FROM Customer this_
WHERE this_.LastName like @p0;@p0 = 'St%'

In an upcoming post I will explore the capabilities and limitations of LINQ to NHibernate. For now, here is the full test class I used to discover LINQ to NHibernate:

using System.Diagnostics;
using System.Linq;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Linq;
using NUnit.Framework;
using PocoLib;

namespace PocoLibTests
{
    [TestFixture]
    public class CustomerLINQTests
    {
        private ISessionFactory _factory;

        [SetUp]
        public void Setup()
        {
            Configuration configuration = new Configuration();
            configuration.Configure("PocoLib.cfg.xml");
            configuration.AddXmlFile("Customer.hbm.xml");

            _factory = configuration.BuildSessionFactory();

            SetupDatabase();
        }

        [TearDown]
        public void TearDown()
        {
            PurgeDatabase();
        }

        [Test]
        public void SelectAllCustomers()
        {
            using (var session = _factory.OpenSession())
            {
                //Act
                var customers = from cust in session.Linq<Customer>()
                                select cust;
                //Assert
                Assert.AreNotEqual(4, customers.Count());
            }
        }

        [Test]
        public void SelectWithPredicate()
        {
            using (var session = _factory.OpenSession())
            {
                //Act
                var customers = from cust in session.Linq<Customer>()
                                where cust.LastName.StartsWith("St")
                                select cust;
                //Assert
                Assert.AreEqual(1, customers.Count());
            }
        }

        private void SetupDatabase()
        {
            using (var session = _factory.OpenSession())
            {
                Customer c = new Customer();
                c.FirstName = "Mike";
                c.LastName = "Laroco";
                c.Address.Country = "USA";
                session.SaveOrUpdate(c);

                c = new Customer();
                c.FirstName = "Bubba";
                c.LastName = "Stuart";
                c.Address.Country = "USA";
                c.Address.State = "Florida";
                session.SaveOrUpdate(c);

                c = new Customer();
                c.FirstName = "Ricky";
                c.LastName = "Carmichael";
                c.Address.Country = "USA";
                c.Address.State = "Florida";
                session.SaveOrUpdate(c);

                c = new Customer();
                c.FirstName = "Jean-Sebastien";
                c.LastName = "Roy";
                c.Address.Country = "Canada";
                c.Address.State = "Quebec";
                session.SaveOrUpdate(c);

                session.Flush();
            }

            Debug.WriteLine("end prepare data");

        }

        private void PurgeDatabase()
        {
            using (var session = _factory.OpenSession())
            {
                session.Delete("from Customer");
                session.Flush();
            }
        }

    }
}

Thanks

Thursday, November 5, 2009

NHibernate Tutorial

Note: This post is based on NHibernate version 2.1.

This is a tutorial to get you started with NHibernate.

At the end of this tutorial you will have:

  • A class library project containing one entity
  • A NHibernate configuration file
  • A NHibernate mapping file to map your entities
  • A database running on SQL Server 2005 (changing to another version of SQL server is very simple)
  • A NUnit test project

1 - Create a new class library project named PocoLib in a new solution. This project will contain your entity classes.

2 - In the same solution, create a new class library project called PocoLibTests

3 - In the PocoLibTests project add references to

  • PocoLib project
  • nunit.framework.dll
  • nhibernate.dll
  • LinFu.DynamicProxy
  • NHibernate.ByteCode.LinFu

Note: You do not have to add any references to nhibernate dlls in the PocoLib project. Your entity classes will not have any knowledge of NHibernate; they are real POCO. If you eventually use your entities in a Web app or Web Services project (most likely), it will be that app that will need those same references (except for Nunit of course).

4 - In PocoLibTests, rename class1.cs to CustomerCRUDTests.cs to your project.

using NHibernate;
using NHibernate.Cfg;
using NUnit.Framework;
using PocoLib;

namespace PocoLibTests
{
    [TestFixture]
    public class CustomerCRUDTests
    {

        [Test]
        public void Insert()
        {
            Customer c = new Customer()
                { FirstName = "Bubba", LastName = "Stuart" };

            c.Address.Country = "USA";
            c.Address.State = "Florida";

            using (ISession session = _factory.OpenSession())
            {
                session.SaveOrUpdate(c);
                session.Flush();
            }
        }
    }
}

This test does not compile for now but that gives you an idea of what we are going to work with.

5 - In the PocoLib project rename class1.cs to Address.cs:

namespace PocoLib
{
    public class Address
    {
        private string _zipCode;
        private string _country;
        private string _state;
        private string _street;

        public string Street
        {
            get { return _street; }
            set { _street = value; }
        }

        public string State
        {
            get { return _state; }
            set { _state = value; }
        }

        public string Country
        {
            get { return _country; }
            set { _country = value; }
        }

        public string ZipCode
        {
            get { return _zipCode; }
            set { _zipCode = value; }
        }
    }
}

6 - In the PocoLib project add a new class named Customer.cs:

using System;
using System.Collections.Generic;

namespace PocoLib
{
    public class Customer
    {
        private int _iD;
        private DateTime? _birthDate;
        private string _lastName;
        private string _firstName;
        private Address _address = new Address();

        public int ID
        {
            get { return _iD; }
            set { _iD = value; }
        }

        public string FirstName
        {
            get { return _firstName; }
            set { _firstName = value; }
        }

        public string LastName
        {
            get { return _lastName; }
            set { _lastName = value; }
        }

        public DateTime? BirthDate
        {
            get { return _birthDate; }
            set { _birthDate = value; }
        }

        public Address Address
        {
            get { return _address; }
            set { _address = value; }
        }

    }
}

As you can see, the model is simple: a customer has a few properties and an Address.

Note: At this point, the test project still does not compile because the _factory is not defined yet. We will define it really soon.

7 - Create a new database with a Customer table

CREATE DATABASE PocoLib
GO

USE PocoLib
GO

CREATE TABLE [dbo].[Customer](
[CustomerID] [int] IDENTITY(1,1) NOT NULL,
[FirstName] [varchar](50) NULL,
[LastName] [varchar](50) NOT NULL,
[BirthDate] [datetime] NULL,
[Street] [varchar](50) NULL,
[State] [varchar](50) NULL,
[Country] [varchar](50) NULL,
[ZipCode] [varchar](50) NULL)
GO

Notice how we are using two classes (Customer and Address) but a single Customer table. It is much cleaner to use two classes at the class design level. But since it is a one-to-one relationship, saving the address in the same table as customer makes total sense (it's simpler, faster and feel more natural).

8 - In the PocoLib project, add an xml file named PocoLib.cfg.xml. That will be your main NHibernate configuration file. It's arguable whether this file belongs to the PocoLib class library or to the host application (the test project in this case), but for now let's put it in PocoLib.

<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
  <session-factory>
    <property name="connection.provider">
      NHibernate.Connection.DriverConnectionProvider
    </property>
    <property name="connection.driver_class">
      NHibernate.Driver.SqlClientDriver
    </property>
    <property name="connection.connection_string">
      Server=localhost;database=PocoLib;Integrated Security=SSPI;
    </property>
    <property name="dialect">
      NHibernate.Dialect.MsSql2005Dialect
    </property>
    <property name="show_sql">
      true
    </property>
    <property name='proxyfactory.factory_class'>
      NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu
    </property> 
  </session-factory>
</hibernate-configuration>

  • Change the connection string (line 10) if needed.
  • If you are not on SQL Server 2005, you can change the other settings. For instance, set the dialect setting to Nibernate.Dialect.MsSql2008Dialect if your on SQL Server 2008.

9 - In the PocoLib project, add an xml file named Customer.hbm.xml. That will be mapping file used to tell NHibernate where to persist and load our entities. (The same question about where this file should reside applies here too).

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="PocoLib"
                   namespace="PocoLib">

  <class name="Customer" lazy="false">
    <id name="ID" column="CustomerID">
      <generator class="native"/>
    </id>
    <property name="FirstName"/>
    <property name="LastName"/>
    <property name="BirthDate"/>
    <component name="Address">
      <property name="Street"/>
      <property name="State"/>
      <property name="Country"/>
      <property name="ZipCode"/>
    </component> 
  </class>
</hibernate-mapping>

I think the content of the file pretty much speaks for itself but here are a few things worth mentioning:

  • By default, NHibernate assumes that your database column names match your class property names.
  • The <id> element identifies the primary key column. The <generator class="native"> indicates that SQL Server will generate the primary key value; not NHibernate.
  • The Customer is defined as a <class> in the mapping. This is what NHibernate calls an entity.
  • The Address is declared inside the Customer as an<component> element. That basically means that the Address is a part of the Customer and it can't live without a customer. NHibernate components are also know as Value Object in Domain Driven Design.

10 - Copy the PocoLib.cfg.xml and Customer.hbm.xml to the test library \bin\debug\ folder.

11 - Add the missing _factory to the test class. Add this code at the top of the test class:

        private ISessionFactory _factory;

        [SetUp]
        public void Setup()
        {
            Configuration configuration = new Configuration();
            configuration.Configure("PocoLib.cfg.xml");
            configuration.AddXmlFile("Customer.hbm.xml");

            _factory = configuration.BuildSessionFactory();
        }

  • The first line create a Configuration object.
  • The second line loads the PocoLib.cfg.xml.
  • The 3rd line loads the mapping file for the Customer class.
  • The 4th line create a factory that we will use to create NHibernate Sessions (a connection)

13 - Run the Insert() test. Go look in the database and be amazed (or not). Notice how SQL statements are written to the output window (If you are using Test Driven at least). You can turn that on or off by changing the <property name="show_sql">true</property> in the .cfg.xml file.

12 - Here are the other CRUD tests. I'm sorry but I don't feel like writing all the Asserts; this is just a test project to experiment with the CRUD operations.

using System.Collections.Generic;
using NHibernate;
using NHibernate.Cfg;
using NUnit.Framework;
using PocoLib;

namespace PocoLibTests
{
    [TestFixture]
    public class CustomerCRUDTests
    {
        private ISessionFactory _factory;

        [SetUp]
        public void Setup()
        {
            Configuration configuration = new Configuration();
            configuration.Configure("PocoLib.cfg.xml");
            configuration.AddXmlFile("Customer.hbm.xml");

            _factory = configuration.BuildSessionFactory();
        }

        [Test]
        public void Insert()
        {
            Customer c = new Customer()
               { FirstName = "Bubba", LastName = "Stuart" };

            c.Address.Country = "USA";
            c.Address.State = "Florida";

            using (ISession session = _factory.OpenSession())
            {
                session.SaveOrUpdate(c);
                session.Flush();
            }
        }

        [Test]
        public void Update()
        {
            // Arrange : Create customer in order to update it
            Customer c = new Customer() 
                { FirstName = "Bubba", LastName = "Stuart" };
            using (ISession session = _factory.OpenSession())
            {
                session.SaveOrUpdate(c);
                session.Flush();
            }
            int id = c.ID;

            //Act : update
            using (ISession session = _factory.OpenSession())
            {
                c = session.Get<Customer>(id);
                c.FirstName = "James";
                session.SaveOrUpdate(c);
                session.Flush();
            }
        }

        [Test]
        public void Delete()
        {
            // Arrange : Create customer in order to delete it
            Customer c = new Customer()
                { FirstName = "Bubba", LastName = "Stuart" };
            using (ISession session = _factory.OpenSession())
            {
                session.SaveOrUpdate(c);
                session.Flush();
            }
            int id = c.ID;

            //Act : Delete
            using (ISession session = _factory.OpenSession())
            {
                c = session.Get<Customer>(id);
                session.Delete(c);
                session.Flush();
            }
        }

        [Test]
        public void Query()
        {
            // Arrange : Create a customer in order to query it
            Customer c = new Customer() 
                 { FirstName = "Bubba", LastName = "Stuart" };
            using (ISession session = _factory.OpenSession())
            {
                session.SaveOrUpdate(c);
                session.Flush();
            }

            IList<Customer> list = null;

            //Act:
            using (ISession session = _factory.OpenSession())
            {
                list = session.CreateQuery(
                   "from Customer where LastName is not null")
                  
.List<Customer>();
            }

            //Assert:
            Assert.AreNotEqual(0, list.Count);

        }

    }
}

I hope this helps.

BTW: I started learning NHibernate just a few days ago. I'm wrote this post to serialize what I have learned so far (and hopefully help people at the same time). I'm sure this approach is not ideal. If you have comments on how I could improve this tutorial, just let me know.

Thanks