in

 

Whoa, Nellie.NET!

  • 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<T>). 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.
     

    Posted Oct 11 2007, 07:17 AM by Nelson Montalvo with no comments
    Filed under:
  • 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<T>, 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(@"..\..\Schemas\DealerSchema.xsd");

                sqlDbUnit.ReadXml(@"..\..\TestData\TestDataForADealerListWithOneDealer.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<T>, 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 Value 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

    {

        /// <summary>

        /// 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

     

            /// <summary>

            /// 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; }

            }

     

            /// <summary>

            /// Initializes the NHibernate session factory upon instantiation.

            /// </summary>

            private NHibernateSessionManager()

            {

                InitSessionFactory();

            }

     

            /// <summary>

            /// 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();

            }

     

            /// <summary>

            /// 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);

            }

     

            /// <summary>

            /// 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();

                }

            }

     

            /// <summary>

            /// 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);

                    }

                }

            }

     

            /// <summary>

            /// 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";

        }

    }

    • 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.

  • Simple NHibernate Example, Part 3: Initial Repository Implementation

    Repositories are our central access point to pre-existing domain objects. Repositories simplify the management of the object's life cycle and decouple the domain from data access technologies and strategies. Finally, repositories are domain level, communicating to other domain objects how certain objects can be accessed.

    I will begin my initial repository implementation by exploring how to save (register) a NEW domain object with our repository, thus giving that object Identity within the context of our domain.

    Let's say that we have a new auto Dealer object. The auto dealer's information has been entered into the domain object and we are not ready to persist that information into the domain.

    Our initial specifications for the repository are the following. For this initial implementation, I am going to use a mocked interface representing the repository. This will allow us to explore the interactions between the domain object and the Repository interface. Eventually, we will replace this mocked interface code with the real NHibernate implementation.

        1 using Foo.Domain;
        2 using Foo.Domain.Repositories;
        3 using NUnit.Framework;
        4 using Rhino.Mocks;
        5 
        6 namespace Specifications.Foo.Domain
        7 {
        8     [TestFixture]
        9     public class WhenSavingANewDealerToTheDealerRepository
       10     {
       11         private const int MOCK_DB_ID = 1;
       12         private Dealer dealer;
       13         private MockRepository mocks;
       14         private DealerRepository dealerRepository;
       15 
       16         [SetUp]
       17         public void SetUpContext()
       18         {
       19             dealer = new Dealer();
       20             mocks = new MockRepository();
       21             dealerRepository = mocks.CreateMock<DealerRepository>();
       22         }
       23 
       24         [Test]
       25         public void ShouldPopulateAnIdForTheDealerAfterSaving()
       26         {
       27             SetupMockExpectations();
       28 
       29             Assert.AreEqual(0, dealer.Id, "The dealer's ID should not be set before the save.");
       30             Dealer savedDealer = dealerRepository.Save(dealer);
       31 
       32             Assert.IsNotNull(savedDealer, "A saved dealer reference should be returned from the Repository.");
       33             Assert.AreNotEqual(dealer, savedDealer, "The saved dealer reference should be different from the one passed into the Save.");
       34             Assert.AreEqual(MOCK_DB_ID, savedDealer.Id, "A system ID should be generated for the newly saved dealer.");
       35             mocks.VerifyAll();
       36         }
       37 
       38         private void SetupMockExpectations()
       39         {
       40             Dealer dealerToReturnInExpectedCall = SetupExpectationThatACallToSaveWillReturnANewDealerReference();
       41             SetupExpectationThatTheIdOnTheNewDealerReferenceWillBeSet(dealerToReturnInExpectedCall);
       42             mocks.ReplayAll();
       43         }
       44 
       45         private static void SetupExpectationThatTheIdOnTheNewDealerReferenceWillBeSet(Dealer dealerToReturnInExpectedCall)
       46         {
       47             Expect.Call(dealerToReturnInExpectedCall.Id).Return(MOCK_DB_ID);
       48         }
       49 
       50         private Dealer SetupExpectationThatACallToSaveWillReturnANewDealerReference()
       51         {
       52             Dealer dealerToReturnInExpectedCall = mocks.PartialMock<Dealer>();
       53             Expect.Call(dealerRepository.Save(dealer)).Return(dealerToReturnInExpectedCall);
       54             return dealerToReturnInExpectedCall;
       55         }
       56     }
       57 }


    As an explanation: 



    • I am going to make a technical implementation decision (let me know if this is wrong) and return a new Dealer object reference from the save call (line 30). Simliar to NHibernate, I believe, this will allow for the return of the proxied reference, which is  managed by NHibernate (mocked on lines 52 and 53). This is a significant point and I hope to have some time to verify this in another post.


    • The Dealer.Id property cannot be set externally (please review my previous post on Identity), so on line 47, I used Rhino mocks to partially mock the interface and return a value for that property.


    • On line 33, the expectation is meant to show that the original reference to the Dealer domain object is not the same as the new "proxied" version.

    This defines the basic specification for the interface:


    namespace Foo.Domain.Repositories
    {
        public interface DealerRepository
        {
            Dealer Save(Dealer dealer);
        }
    }
     

    Time to begin implementation of the repository interface with NHibernate. I'll create an NHibernate mapping file to begin with.


    Assume that our database (in Sql Server 2005) and Dealer table with a DealerId column are in place. Also, this example uses NHibernate 1.2.0CR1 released on 2/22/07. I downloaded the latest version for some of the newest functionality, including generics and null types.


    The table definition:



    The simplest NHibernate mapping file to match our specifications will look like this:


    <?xml version="1.0" encoding="utf-8" ?>
    <
    hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
        <
    class name="Foo.Domain.Dealer, Foo.Domain" table="Dealer">
            <
    id name="Id" column="DealerId">
                <
    generator class="assigned" />
            </
    id>
        </
    class>
    </
    hibernate-mapping>


    Basically, all this says is to map the Dealer domain property, Id, to the column DealerId in the database table Dealer. It also says to utilize the database table's  identity capabilities to generate the unique ID for the domain object.


    Here is how I've structured the project. The Dealer mapping file is called Dealer.hbm.xml. There are references to the project containing the domain and the repository interface. There is also a reference to the NHibernate.dll file. The DealerRepository listed below is implementing the DealerRepository interface from the domain (code coming up shortly):



    And be sure to include the Dealer.hbm.xml as an embedded resource:



    I've jumped the gun a bit, so in the next posting, I'll begin with writing NHibernate unit tests that interface with the database. The process is somewhat involved for the initial implementation, but once setup, it should be reusable for all database unit tests going forward.

  • Using NHibernate 1.2 with Monorail

    The current Castle distribution seems to be linked to an older version of NHibernate. To use Castle with NHibernate 1.2, I had to work from the development trunk and not from the compiled distributions.

    So get the latest code from the Castle Subversion repository, compile it and link your Monorail project to those newly compiled libraries.

    Seems to work as of today... :) Let me know if there are any alternatives. Thanks.
  • Setting up Apache with SSL on Windows (for Subversion)

    Well, I finally decided to begin using Subversion rather than my usual choice, SourceGear Vault, for source code control. Vault is great, but lately I've been finding the need to use Subversion and well... it's free (but certainly not without its hassles).

    I decided to go the Apache route so as to be able to securely connect to my Subversion server via the Internet. The basic windows MSI installer of Subversion (1.4.3 as of this posting) comes with a server application that runs as a service and it's easy to setup... Tortoise can even connect to it using secure SSH, but I wanted to give Apache a shot. Looking back, I might stick to the basic install next time. :)
    1. Use this link to get started. Be sure to grab Apache 2.0.59.

    2. Use this link to get setup the SSL version of Apache, located here. Download version 2.0.59 and simply extract the zipped files over the version of Apache downloaded in step 1.

      Follow the instructions through Step 2 and you will now have the SSL capabilities to generate key files and certificates.

    3. Purchase a cert to make this official. I got mine from GoDaddy and used these instructions to generate a CSR for Apache 2.x (using the windows version of OpenSSL, of course).

      Otherwise, generate the test certificate.

    4. The only issues I had were:

      • Pay careful attention to the error.log file in the Apache logs directory. It'll help you to work out any issues.

      • The Windows version of Apache is retarded. Be sure to decrypt the key file prior to running Apache with SSL using the command: openssl rsa -in originalEncryptedKeyFile.key -out newDecryptedKeyFile.key

      • The SSLMutex parameter in the httpd.conf file is different. Set the SSLMutex parameter to 'default'.

      • The SSLLog parameters in the httpd.conf file don't seem to work anymore. Remove any reference to them.

      • In your virtual host entry in the httpd.conf file, set the ServerName to whatever your Canonical Name was in the certificate (foo.bar.com).
    It's late, I haven't tested Subversion out yet. I'll let you know tomorrow.

    By the way, I'm running my version of Apache side-by-side with IIS, so I had to set the SSL port to 444. You can set that up in the http.conf file by setting up the ListenOn port to 444 and including the port number in your VirtualHost entry.
  • AnkhSVN (Visual Studio 2005 AddIn for connecting to Subversion)

    I was in refactoring mode, preparing for my repository post, and had to move files from one location to another.

    Now, moving files to other directories can easily be done within the Tortoise repository browser, but the updates are not reflected in the Visual Studio project using this method. You have to go into the project, delete the files in the old location and add the files in the new one. Tedious! :)

    I wanted an easy way to do moves - and just about anything else against Subversion - within Visual Studio, so I took a look at AnkhSVN. Ankh is a Visual Studio AddIn (not an SCC provider) that gives you the ability to interface with your Subversion repository from within Visual Studio. The 1.0 version was just released in late January and appears stable.

    So far, so good. I was able to make the moves and the repository was updated automatically upon commit. The only caveat is that you have to commit any other changes that you may have in progress. Not a big deal.