NHibernate Query Example using CreateCriteria


    I’m a big fan of NHibernate. I love how it abstracts out the data portion of your application and allows you to work from the perspective of the domain rather than the database.

    So in building my domain repositories, I have several options to query the database using NHibernate:

    • I can use HQL in CreateQuery.
    • I can use named queries in GetNamedQuery.
    • I can use SQL directly in CreateSqlQuery.
    • I can use the type of object in a CreateCriteria.

    Short of using Ayende’s NHibernate Query Generator to strongly type my HQL queries, I like using the CreateCriteria call.

    The Example

    To begin with, I’m not a fan of generic repository interfaces with a ton of generics-based CRUD operations (IRepository). I’d rather just limit my repository code to what is needed for operating with that aggregate root (driven by the domain, of course).

    For this example, I have an interface in my domain project called CommissionPeriodRepository (yes, no ‘I’ in front of my interfaces – again, personal preference… lol). The implementation class, NHibernateCommissionPeriodRepository, resides in an NHibernate specific project implementing the repository interfaces. Finally, I’m assuming that the NHibernate session (Unit of Work is used here, but is NHibernate specific) is managed by the calling application, committing and rolling back changes as necessary. 

        public class NHibernateCommissionPeriodRepository : CommissionPeriodRepository
        {
            #region CommissionPeriodRepository Members
    
      
      
            public CommissionPeriod GetCommissionPeriodFor(DateTime date)
            {
                return UnitOfWork.GetCurrentSession()
                    .CreateCriteria(typeof (CommissionPeriod))
                    .Add(Expression.Lt("Startdate", date))
                    .Add(Expression.Ge("Enddate", date))
                    .UniqueResult<CommissionPeriod>();
            }
    
      
      
            #endregion
        }

    Pretty much all the code is doing is:

    1. Pulling the current NHibernate session.
    2. Creating a criteria object for a CommissionPeriod domain object.
    3. Pulling the unique CommissionPeriod object that has a start date less than that passed in date and an end date greater than or equal to it.

    That’s it. Simple stuff. Gotta love it.

    ReSharper Keymappings


    As part of the install of ReSharper 3.0 install into Visual Studio 2008, I “accidentally” selected the Visual Studio keymappings to try it out.

    Yikes… what a mistake. I couldn’t stand it. It turned out to be some form of tough love.

    I’m afraid that I’m too used to ReSharper and none of the Visual Studio keymappings are worth losing the ReSharper keymappings for (come on… no Ctrl-B?!?!). lol

    So I changed it back:

    image

    As somebody on the team once said, Visual Studio is just a glorified form of Notepad without ReSharper. lol

    Interesting Silverlight demo site


    Interesting demo of Silverlight and AJAX for eCommerce.

    Video on the demo site and a product image on the demo site.

     
    The site isn’t anything particularly revolutionary, but a good example of Silverlight in action.
     

    Please contribute to Team Hanselman’s Diabetes Walk 2007


    After watching Scott Hanselman participate in several of the open spaces at Alt.Net this weekend, I have a different level of appreciation for Scott’s blog. He is not only prolific in his blogging and very knowledgeable, but “in the real world” he is well spoken, has a good sense of humor, and can hold his own in an open space format with good points, good questions and tactful responses.

    On his blog, Scott has a link to his Team Hanselman’s Diabetes Walk 2007 page. On it, you can contribute. I would like to say that I contributed a small amount simply because Scott’s tips have been so dang useful over the past few years – his tools list alone has been priceless – but my dad is type II and it does suck. So please contribute what you can.

    Thanks.

    The “Domain Model”


    There has been some recent discussion on User Stories, the Ubiquitous Language of a domain, and Domain Models. I have been fairly confused because I am not clear on what folks have meant by the “Domain Model.” Some folks seem to be referring to the true code construct that results from 1) having a user story define some desired behavior, 2) discussion of this behavior, and 3) refinement of the concepts into the code construct.

    From the beginning of Evan’s book, I think of the domain model as:

     “A domain model is not a particular diagram; it is the idea that the diagram is intended to convey. It is not just the knowledge in a domain expert’s head; it is a rigorously organized and selective abstraction of that knowledge. A diagram can represent and communicate a model, as can carefully written code, as can an English sentence.”

    Yes, the domain language does drive the model. And user stories drive that original discussion. But the model is not just code. Once discussed, refined, and hammered out, it is these concepts of the domain model that enter the ubiquitous language, whether or not those concepts end up in code.

    My $2. I don’t disagree with other folks out there – just explaining what I’ve always thought of the model.

    What do you guys think?
     

     

    Castle’s ActiveRecord: Not for the Domain Purist in you…


     I finally “broke down” and began using ActiveRecord just to try it out. Here’s the tutorial that I used. Now, don’t get me wrong – ActiveRecord is built on top of NHibernate and it’s one of the fastest ways to start working with your persistence layer.

    So what’s the problem?

    Well, it’s this:

    using Castle.ActiveRecord;

    namespace Foo.Domain

    {

        [ActiveRecord]

        public class Dealer : ActiveRecordBase<Dealer>

        {

            private int id;

            private string contactFirstName;

            private string contactLastName;

            private string businessName;

            private Address address;

            private string email;

            private PhoneNumber contactPhoneNumber;

            private PhoneNumber faxNumber;

     

            public Dealer()

            {

                address = new Address();

                contactPhoneNumber = new PhoneNumber();

                faxNumber = new PhoneNumber();

            }

     

            [PrimaryKey(PrimaryKeyType.Identity, “DealerId”, Access=PropertyAccess.NosetterCamelcase)]

            public int Id

            {

                get { return id; }

            }

     

            [Property]

            public virtual string BusinessName

            {

                get { return businessName; }

                set { businessName = value; }

            }

     

            [Property]

            public virtual string ContactFirstName

            {

                get { return contactFirstName; }

                set { contactFirstName = value; }

            }

     

            [Property]

            public virtual string ContactLastName

            {

                get { return contactLastName; }

                set { contactLastName = value; }

            }

     

            [Property]

            public virtual string Email

            {

                get { return email; }

                set { email = value; }

            }

     

            [Nested(“ContactPhoneNumber”)]

            public virtual PhoneNumber ContactPhoneNumber

            {

                get { return contactPhoneNumber; }

                set { contactPhoneNumber = value; }

            }

     

            [Nested(“FaxNumber”)]

            public virtual PhoneNumber FaxNumber

            {

                get { return faxNumber; }

                set { contactPhoneNumber = value; }

            }

     

            [Nested]

            public virtual Address Address

            {

                get { return address; }

                set { address = value; }

            }

        }

    }

    Those attributes are tagging persistence concerns all over my domain object!! The class also inherits from ActiveRecordBase, a persistence focused class having nothing to do with the domain itself.

    Finally, the class if forced to expose a setter for the value objects: ContactPhoneNumber, FaxNumber, and Address. The class should not have to allow external objects to directly set properties that access value objects. An external object can directly overwrite the value object references managed by my Dealer aggregate. (Please tell me I’m wrong on this.)

    Of course, these are known drawbacks to using AR and it’s really only a concern for domain purists out there (who me?). All in all, ActiveRecord is great for getting started with simple domains that map fairly easily to their relevant database tables.

    Here’s all I had to do in my test to get AR Unit Testing working.

    1. I borrowed and slightly modified the unit testing base class, AbstractModelTestCase, from this article. I modified it to support usage of NDbUnit (the original, not my modified version):

            [SetUp]

            public virtual void Init()

            {

                sqlDbUnit = new SqlDbUnitTest(GetConnectionString());

     

                PrepareSchema();

                CreateScope();

            }

     2. I wrote my specifications utilizing an NDbUnit schema and data file. It was as easy as:

    using Foo.Domain;

    using NUnit.Framework;

    using NDbUnit.Core;

     

    namespace Specifications.Foo.Domain.ActiveRecord

    {

        [TestFixture]

        public class ADealerListWithOneDealer : AbstractModelTestCase

        {

            protected override void PrepareSchema()

            {

                sqlDbUnit.ReadXmlSchema(@”….SchemasDealerSchema.xsd”);

                sqlDbUnit.ReadXml(@”….TestDataTestDataForADealerListWithOneDealer.xml”);

     

                sqlDbUnit.PerformDbOperation(DbOperationFlag.CleanInsert);

            }

     

            [Test]

            public void ShouldHaveASizeOfOne()

            {

                FlushAndRecreateScope();

                Dealer[] dealers = Dealer.FindAll();

                Assert.AreEqual(1, dealers.Length, “Should have a length of 1, given the test data.”);          

            }

        }

    }

    As you can see, almost no persistence code was written to accomplish this trivial bit of work.

    Repositories

    Repositories are not explicit using the AR method of persisting entities, as shown above. Persistence concerns are directly associated with the entity (via Find(), FindAll(), etc). Ayende has an Active Record repository implementation in his Rhino Commons library. The SVN url is https://svn.sourceforge.net/svnroot/rhino-tools/trunk/ and the code can be found under rhino-commons/Rhino.Commons/Repositories.

    Ayende’s ARRepository implementation appears to be a way around inheriting from ActiveRecordBase, but I’m not sure. Does anybody else know?

    Stories in TargetProcess


    TargetProcess is fantastic tool for managing projects using Agile techniques. Gone are the days of 6 month Gantt charts, not truly knowing where you’re at on a daily basis and less effective communication of status.

    Stories and task breakdowns are the essential elements in this tool, but TargetProcess is incredibly flexible in how you can use it – from usage by one man shops to enterprise level teams.

    I use TargetProcess on my personal projects by first creating stories, the equivalent of requirements, and having conversations with the end user to hash out the details of the story and beginnings of a domain model:

     

    The beauty of stories is that they are simple to create and track. What is difficult is thinking about going back to utilizing other requirement methods because stories provide significant value by just being so easy to use. 

    With stories, you do not try to get it all right upfront, but you do try to write as many stories as possible. From here, you can break down stories further, come up with tasks, and/or roughly estimate the story.

    Simple NHibernate Example, Part 5: Repository Testing


    Bill McCafferty has released his updates to his NHibernate Best Practices article. The article is fantastic. It is listed as an advanced topic, but if you follow every link and study the topics in his article, the article makes for as great a comprehensive introduction as any… Go read it now.

    In the last few posts, we explored very simple concepts from the point of view of DDD and BDD/TDD. In this post, I will put together a very simple testing harness to show how to roll together these concepts in the context of NHibernate.

    Begin by reviewing the interface created in Part 3. In that example, the Repository interface is placed at the domain level (Foo.Domain.Repositories.DealerRepository – and no, I don’t use the I in my interfaces). Again, the idea is that a Repository is a domain level concept for managing the lifecycle of Entities. However, the implementation of the persistence mechanism is abstracted from the domain’s point of view.

    We will create an implementation of the repository interface for NHibernate. I know there are many ways to implement repositories, but I’m keeping it easy for this example.

    One other side note – this example is copying over the same specs/tests from Part 3 for my NHibernate tests, but you could imagine reusing them somehow by injecting in mock or NHibernate implementations of the interface. I really haven’t explored an idea like this, but if somebody has, let me know.

    Everything needed for this repository example is in the sample zip file. Go download the zip file now. You will want to focus on the Foo.Data.NHibernate and Tests.Foo.Data.NHibernate projects to see how it all works. Post your questions here.

     

    Supporting Items

    The database schema is included in the solution if you would like to run that to build the FooDomain database and Dealer table.

    This Dealer domain object to table mapping file is located in the Foo.Data.NHibernate project under the DomainMappings folder. The file also contains a component mapping for a phone number Va_lue_ object (the fax number and contact phone number). Let’s just say that it is important for us to store the phone number broken down into its constituent parts (area code, exchange and SLID) for this example.

    In the mapping file, I use of the access=nosetter.camelcase access attribute. This tells NHibernate to set the field values directly rather than through the properties (since no setter is available, as we would like to keep IDs and value object references immutable, as you will see in a moment).

    I use a customized version of nDbUnit to allow for rollbacks of transactions rather than explicit delete. My customized version is included in the project file (which is what makes the zip file pretty big) and could use some cleanup if you want to take that on. 🙂 The project, Tests.Foo.Data.NHibernate contains the xsd used by nDbUnit and you can review the tests to see how it is read into.

    My customizations also reset the identity value on the Dealer table

    after a DeleteAll is called.

    I did not explicitly call flush after the NHibernate save() calls, which

    would immediately write the data to the database. The flush is done automatically since

    NHibernate has to go to the database to pull the identity value and set

    it on the domain object. This is an important point since NDbUnit needs

    to have some data to read from the database. 🙂

    Finally, the tests show how to extract the

    NHibernate transaction and pass it to NDbUnit in the GetTransaction()

    method.
     

    Bill McCafferty’s NHibernate Best Practices


    Bill McCafferty is coming out with an update to his article NHibernate Best Practices. In it, he corrects the problem with HttpContext vs CallContext.

    Simple NHibernate Example, Part 4 : Session Management


    Before continuing with the implementation, let’s talk briefly about NHibernate session management. This will be a short discussion, as you can get more details from chapter 8 of the book, Hibernate In Action. Also, please review the article, NHibernate Best Practices, for further explanation of the Session In View.

    One of the potential problems with using NHibernate is the intermingling of managing the NHibernate sessions and transactions with the basic interactions between the domain and NHibernate. Ideally, we’d like the client using NHibernate to manage transactions outside of the context of the business workflow, committing or rolling back entire transactions depending on the results of the workflow (this is the unit of work pattern).

    We’d like to be able to manage a single session across a single persistence context (or transaction context). We’ll utilize the thread safe CallContext object in an NHibernate SessionManager class. An upcoming post will show how this session manager works. In the meantime, here is the code (borrowed from NHibernate Best Practices):

    using System.Configuration;

    using System.Runtime.Remoting.Messaging;

    using System.Web;

    using NHibernate;

    using NHibernate.Cache;

    using Configuration=NHibernate.Cfg.Configuration;

    using NHibernateConfiguration = NHibernate.Cfg;

     

    namespace DealerMatrix.Data.NHibernate.Session

    {

        ///

    </span> </p>

        /// Handles creation and management of sessions and transactions.  It is a singleton because

        /// building the initial session factory is very expensive. Inspiration for this class came

        /// from Chapter 8 of Hibernate in Action by Bauer and King.  Although it is a sealed singleton

        /// you can use TypeMock (http://www.typemock.com) for more flexible testing.

        /// </summary>

        public sealed class NHibernateSessionManager

        {

            private ISessionFactory sessionFactory;

     

            #region Thread-safe, lazy Singleton

     

            ///

    </span> </p>

            /// This is a thread-safe, lazy singleton.  See http://www.yoda.arachsys.com/csharp/singleton.html

            /// for more details about its implementation.

            /// </summary>

            public static NHibernateSessionManager Instance

            {

                get { return Nested.nHibernateNHibernateSessionManager; }

            }

     

            ///

    </span> </p>

            /// Initializes the NHibernate session factory upon instantiation.

            /// </summary>

            private NHibernateSessionManager()

            {

                InitSessionFactory();

            }

     

            ///

    </span> </p>

            /// Assists with ensuring thread-safe, lazy singleton

            /// </summary>

            private class Nested

            {

                static Nested()

                {

                }

     

                internal static readonly NHibernateSessionManager nHibernateNHibernateSessionManager = new NHibernateSessionManager();

            }

     

            #endregion

     

            private void InitSessionFactory()

            {

                Configuration cfg = new Configuration();

     

                // The following makes sure the the web.config contains a declaration for the HBM_ASSEMBLY appSetting

                if (ConfigurationManager.AppSettings[“HBM_ASSEMBLY”] == null ||

                    ConfigurationManager.AppSettings[“HBM_ASSEMBLY”] == “”)

                {

                    throw new ConfigurationErrorsException(“NHibernateManager.InitSessionFactory: “HBM_ASSEMBLY” must be “ +

                                                           “provided as an appSetting within your config file. “HBM_ASSEMBLY” informs NHibernate which assembly “ +

                                                           “contains the HBM files. It is assumed that the HBM files are embedded resources. An example config “ +

                                                           “declaration is <add key=”HBM_ASSEMBLY” value=”MyProject.Core” />”);

                }

     

                cfg.AddAssembly(ConfigurationManager.AppSettings[“HBM_ASSEMBLY”]);

                sessionFactory = cfg.BuildSessionFactory();

            }

     

            ///

    </span> </p>

            /// Allows you to register an interceptor on a new session.  This may not be called if there is already

            /// an open session attached to the HttpContext.  If you have an interceptor to be used, modify

            /// the HttpModule to call this before calling BeginTransaction().

            /// </summary>

            public void RegisterInterceptor(IInterceptor interceptor)

            {

                ISession session = threadSession;

     

                if (session != null && session.IsOpen)

                {

                    throw new CacheException(“You cannot register an interceptor once a session has already been opened”);

                }

     

                GetSession(interceptor);

            }

     

            public ISession GetSession()

            {

                return GetSession(null);

            }

     

            ///

    </span> </p>

            /// Gets a session with or without an interceptor.  This method is not called directly; instead,

            /// it gets invoked from other public methods.

            /// </summary>

            private ISession GetSession(IInterceptor interceptor)

            {

                ISession session = threadSession;

     

                if (session == null)

                {

                    if (interceptor != null)

                    {

                        session = sessionFactory.OpenSession(interceptor);

                    }

                    else

                    {

                        session = sessionFactory.OpenSession();

                    }

     

                    threadSession = session;

                }

     

                return session;

            }

     

            public void CloseSession()

            {

                ISession session = threadSession;

                threadSession = null;

     

                if (session != null && session.IsOpen)

                {

                    session.Close();

                }

            }

     

            public void BeginTransaction()

            {

                ITransaction transaction = threadTransaction;

     

                if (transaction == null)

                {

                    transaction = GetSession().BeginTransaction();

                    threadTransaction = transaction;

                }

            }

     

            public void CommitTransaction()

            {

                ITransaction transaction = threadTransaction;

     

                try

                {

                    if (transaction != null && !transaction.WasCommitted && !transaction.WasRolledBack)

                    {

                        transaction.Commit();

                        threadTransaction = null;

                    }

                }

                catch (HibernateException ex)

                {

                    RollbackTransaction();

                    throw ex;

                }

            }

     

            public void RollbackTransaction()

            {

                ITransaction transaction = threadTransaction;

     

                try

                {

                    threadTransaction = null;

     

                    if (transaction != null && !transaction.WasCommitted && !transaction.WasRolledBack)

                    {

                        transaction.Rollback();

                    }

                }

                catch (HibernateException ex)

                {

                    throw ex;

                }

                finally

                {

                    CloseSession();

                }

            }

     

            ///

    </span> </p>

            /// If within a web context, this uses <see cref=”HttpContext” /> instead of the WinForms

            /// specific <see cref=”CallContext” />.  Discussion concerning this found at

            /// http://forum.springframework.net/showthread.php?t=572.

            /// </summary>

            private ITransaction ThreadTransaction

            {

                get

                {

                    if (IsInWebContext())

                    {

                        return (ITransaction) HttpContext.Current.Items[TRANSACTION_KEY];

                    }

                    else

                    {

                        return (ITransaction) CallContext.GetData(TRANSACTION_KEY);

                    }

                }

                set

                {

                    if (IsInWebContext())

                    {

                        HttpContext.Current.Items[TRANSACTION_KEY] = value;

                    }

                    else

                    {

                        CallContext.SetData(TRANSACTION_KEY, value);

                    }

                }

            }

     

            ///

    </span> </p>

            /// If within a web context, this uses <see cref=”HttpContext” /> instead of the WinForms

            /// specific <see cref=”CallContext” />.  Discussion concerning this found at

            /// http://forum.springframework.net/showthread.php?t=572.

            /// </summary>

            private ISession ThreadSession

            {

                get

                {

                    if (IsInWebContext())

                    {

                        return (ISession) HttpContext.Current.Items[SESSION_KEY];

                    }

                    else

                    {

                        return (ISession) CallContext.GetData(SESSION_KEY);

                    }

                }

                set

                {

                    if (IsInWebContext())

                    {

                        HttpContext.Current.Items[SESSION_KEY] = value;

                    }

                    else

                    {

                        CallContext.SetData(SESSION_KEY, value);

                    }

                }

            }

     

            private static bool IsInWebContext()

            {

                return HttpContext.Current != null;

            }

     

            private const string TRANSACTION_KEY = “CONTEXT_TRANSACTION”;

            private const string SESSION_KEY = “CONTEXT_SESSION”;

        }

    }

    </div> * Have a look at the last two properties to see how the CallContext is used. These maintain the transaction and session across a single request thread. * InitSessionFactory() requires that HBM_ASSEMBLY be defined in the config file. * Finally, be sure to call CommitTransaction(), RollbackTransaction() and/or CloseSession() at the end of the request to clear out the CallContext at the end of the thread request. Again, I will show you how to put all these pieces together in an upcoming post.

subscribe via RSS