The Inquisitive Coder – Davy Brion’s Blog

Trying to walk that thin line between intelligence and ignorance

Archive for the 'NHibernate' Category

Avoid Using NHibernate With NUnit 2.4.6

Posted by Davy Brion on 24th June 2009

We just spent about 2 hours trying to find out why our NHibernate tests were about 10x slower on our build server than they were on our local machines. I had noticed lately that the build for one of our projects was taking longer and longer but i hadn’t really timed the difference. This project has about 1200 tests that use NHibernate and they run in about 45-60 seconds on my local machine. It turns out they took around 15 minutes on the buildserver when running them through TeamCity.

I logged into the buildserver and ran the tests manually using nunit’s console runner (with an NUnit-2.4.7 build that i happened to have installed somewhere on the machine) and they only took about 45 seconds. After a lot of guesswork and screwing around, it turned out that we never modified our base build script (why yes, i do believe in build script inheritance) to use a newer version of NUnit. We set up the buildserver about 1 year ago, and at that time, the latest stable NUnit version that TeamCity supported was NUnit 2.4.6. Our base build script was still referring to NUnit 2.4.6, which apparently sets log4net to use debug level logging. Now, NHibernate logs a huge amount of information at the debug level, so this turned out to slow down all of our builds that had NHibernate tests.

We changed the the 2.4.6 version in our script to 2.4.7 and the build time of this particular project decreased from around 50 minutes to about 35 minutes. Yes, that’s still a lot but this is a huge project with a lot of legacy tests and the entire build process is pretty complex. Other projects went from build times from around 7 minutes to about 2 minutes.

That’s a pretty nice improvement for simply changing a “6″ to a “7″ ;)

  • Share/Save/Bookmark

Posted in NHibernate, Performance, Test Driven Development | 3 Comments »

Using The Guid.Comb Identifier Strategy

Posted by Davy Brion on 21st May 2009

As you may have read by now, it’s a good idea to avoid identity-style identifier strategies with ORM’s. One of the better alternatives that i kinda like is the guid.comb strategy. Using regular guids as a primary key value leads to fragmented indexes (due to the randomness of the guid’s value) which leads to bad performance. This is a problem that the guid.comb strategy can solve quite easily for you.

If you want to learn how the guid.comb strategy really works, be sure to check out Jimmy Nilsson’s article on it. Basically, this strategy generates sequential guids which solves the fragmented index issue. You can generate these sequential guids in your database, but the downside of that is that your ORM would still need to insert each record seperately and fetch the generated primary key value each time. NHibernate includes the guid.comb strategy which will generate the sequential guids before actually inserting the records in your database.

This obviously has some great benefits:

  • you don’t have to hit the database immediately whenever a record needs to be inserted
  • you don’t need to retrieve a generated primary key value when a record was inserted
  • you can batch your insert statements

Let’s see how we can use this with NHibernate. First of all, you need to map the identifier of your entity like this:

    <id name="Id" column="Id" type="guid" >

      <generator class="guid.comb" />

    </id>

And that’s actually all you have to do. You don’t have to assign the primary key values or anything like that. You don’t need to worry about them at all.

Take a look at the following test:

        [Test]

        public void InsertsAreOnlyExecutedAtTransactionCommit()

        {

            var insertCountBefore = sessionFactory.Statistics.EntityInsertCount;

 

            using (var session = sessionFactory.OpenSession())

            using (var transaction = session.BeginTransaction())

            {

                for (int i = 0; i < 50; i++)

                {

                    var category = new ProductCategory(string.Format("category {0}", i + 1));

                    // at this point, the entity doesn't have an ID value yet

                    Assert.AreEqual(Guid.Empty, category.Id);

                    session.Save(category);

                    // now the entity has an ID value, but we still haven't hit the database yet

                    Assert.AreNotEqual(Guid.Empty, category.Id);

                }

 

                // just verifying that we haven't hit the database yet to insert the new categories

                Assert.AreEqual(insertCountBefore, sessionFactory.Statistics.EntityInsertCount);

                transaction.Commit();

                // only now have the recors been inserted

                Assert.AreEqual(insertCountBefore + 50, sessionFactory.Statistics.EntityInsertCount);

            }

        }

Interesting, no? The entities have an ID value after they have been ’saved’ by NHibernate. But they haven’t actually been saved to the database yet though. NHibernate always tries to wait as long as possible to hit the database, and in this case it only needs to hit the database when the transaction is committed. If you’ve enabled batching of DML statements, you could severly reduce the number of times you need to hit the database in this scenario.

And in case you’re wondering, the generated guids look like this:

81cdb935-d371-4285-9dcb-9bdb0122f25f
a44baf99-58e9-4ad7-9a59-9bdb0122f25f
a88300c2-6d64-4ae3-a55b-9bdb0122f25f
032c7884-da2f-4568-b505-9bdb0122f25f
….
70d7713c-b38d-4341-953d-9bdb0122f25f

Notice the last part of the guids… this is what prevents the index fragmentation.

Obviously, this particular test is not a realistic scenario but i’m sure you understand how much of an improvement this identifier strategy could provide throughout an entire application. The only downside (IMO) is that guid’s aren’t really human readable so if that is important to you, you should probably look into other identifier strategies. The HiLo strategy would be particularly interesting in that case, but we’ll cover that in a later post ;)

  • Share/Save/Bookmark

Posted in NHibernate, Performance | 11 Comments »

Using SQL Functions in Criteria Restrictions

Posted by Davy Brion on 30th April 2009

A coworker needed to use a SQL function in the where clause of a query that he was creating with NHibernate’s ICriteria API. Most examples of this on the web use HQL instead of the ICriteria API and since we primarily use the ICriteria API we looked into how to do this.

Turns out it is pretty simple to do, though the syntax isn’t really straightforward. Suppose you want to query all of your employees who are born in a specific year. You could mess around with some DateTime parameters, but most databases have SQL functions to get the year from a date. Using the ICriteria API, this would look like this:

            var employeesBornIn81 = session.CreateCriteria<Employee>()

                .Add(Restrictions.Eq(Projections.SqlFunction("year", NHibernateUtil.DateTime, Projections.Property("BirthDate")), 1981))

                .List<Employee>();

which adds the following where clause to the SQL statement (on SQL Server 2005):

WHERE datepart(year, this_.BirthDate) = @p0;

  • Share/Save/Bookmark

Posted in NHibernate | 8 Comments »

NHibernate’s Future Queries And Their Fallback Behavior

Posted by Davy Brion on 13th April 2009

I’ve blogged about NHibernate’s Future queries a couple of times already. But as you know, NHibernate aims to offer you a way to write your code completely independent of the actual database you’re using. So what happens if you run your code, which is using the Future and FutureValue features, on a database that doesn’t support batched queries? Previously, this would fail with a NotSupportedException being thrown.

As of today, (revision 4177 if you want to be specific) this is no longer the case. If you use the Future or FutureValue methods of either ICriteria or IQuery, and the database doesn’t support batching queries, NHibernate will fall back to simply executing the queries immediately, as the following tests show:

        [Test]

        public void FutureOfCriteriaFallsBackToListImplementationWhenQueryBatchingIsNotSupported()

        {

            using (var session = sessions.OpenSession())

            {

                var results = session.CreateCriteria<Person>().Future<Person>();

                results.GetEnumerator().MoveNext();

            }

        }

        [Test]

        public void FutureValueOfCriteriaCanGetSingleEntityWhenQueryBatchingIsNotSupported()

        {

            int personId = CreatePerson();

 

            using (var session = sessions.OpenSession())

            {

                var futurePerson = session.CreateCriteria<Person>()

                    .Add(Restrictions.Eq("Id", personId))

                    .FutureValue<Person>();

                Assert.IsNotNull(futurePerson.Value);

            }

        }

There are more tests obviously, but you get the point. The interesting part about these tests is how i disabled query batching support. I only have Sql Server and MySQL running on this machine, and they both support query batching. I didn’t really feel like installing a database that doesn’t support it, so i just took advantage of NHibernate’s extensibility. Since most of us run the NHibernate tests on Sql Server, i inherited from the Sql Server Driver and made sure that it would report to NHibernate that it didn’t support query batching:

    public class TestDriverThatDoesntSupportQueryBatching : SqlClientDriver

    {

        public override bool SupportsMultipleQueries

        {

            get { return false; }

        }

    }

Easy huh? Then i just inherited from the TestCase class we have in the NHibernate.Tests project which offers a virtual method where you can modify the NHibernate configuration for the current fixture:

        protected override void Configure(Configuration configuration)

        {

            configuration.Properties[Environment.ConnectionDriver] =

                "NHibernate.Test.NHSpecificTest.Futures.TestDriverThatDoesntSupportQueryBatching, NHibernate.Test";

            base.Configure(configuration);

        }

Now NHibernate thinks that query batching isn’t supported, yet the above tests still work. Mission accomplished :)

  • Share/Save/Bookmark

Posted in NHibernate | 1 Comment »

Assigning Foreign Keys In NHibernate

Posted by Davy Brion on 3rd April 2009

This post from the Entity Framework team recently caught my attention. It discusses the ability to add actual foreign key values to your entities instead of just references to the referred entities. One of the benefits of this ability is that you can assign foreign key values to an entity’s properties without having to actually retrieve the entity you are referring to. While i am no fan of this approach, i do want to point out that you can do this with NHibernate too, especially because some people don’t know about this.

Take a look at the following code:

                product.Category = session.Get<ProductCategory>(categoryId);

                session.SaveOrUpdate(product);

This code changes the product’s Category property, and to do that it retrieves the actual ProductCategory instance through the id value of the category. This causes 2 database hits. One to retrieve the ProductCategory, and one to persist the Product.

You could do this instead:

                product.Category = session.Load<ProductCategory>(categoryId);

                // this verifies that the product.Category is an uninitialized proxy

                // which means that we did not fetch the product category from the database

                Assert.IsFalse(NHibernateUtil.IsInitialized(product.Category));

                // we were able to save the product without having loaded the product category

                session.Save(product);

Notice how we use ISession’s Load method here, instead of the Get method to ‘retrieve’ the ProductCategory. The Get method actually fetches the entity from the database if it’s not already in the session cache. The Load method however will return an uninitialized proxy to the ProductCategory entity if it’s not present in the session cache. The NHibernateUtil.IsInitialized() method will return false, because this proxy is indeed uninitialized. It does not hit the database until you try to access any of the properties of the ProductCategory proxy, except for its identifier property. So accessing product.Category.Id would not hit the database, but product.Category.Name or product.Category.Description would.

If you want to avoid hitting the database to assign foreign keys, using a proxy might be an interesting alternative for you.

  • Share/Save/Bookmark

Posted in NHibernate | 5 Comments »

Transparent Query Batching Through Your Repository

Posted by Davy Brion on 1st April 2009

All of our projects that use NHibernate (which is all of them except those where the customer explicitly doesn’t want us to use it or where it wouldn’t make sense to use it) use the same Repository implementation. After the Future and FutureValue queries were added to NHibernate, i modified the implementation of that Repository class.

Two of the FindAll methods now look like this:

        public virtual IEnumerable<T> FindAll()

        {

            return Session.CreateCriteria<T>().Future<T>();

        }

 

        public virtual IEnumerable<T> FindAll(DetachedCriteria criteria)

        {

            return criteria.GetExecutableCriteria(Session).Future<T>();

        }

The only thing i changed in those methods is calling the Future method, instead of the List method. That’s it. All of our specific Find-methods (those that execute specific queries) pass through the FindAll(DetachedCriteria criteria) method so they all benefit from this change.

That means that all of our queries are suddenly batched transparently whenever possible, without impacting any of the calling code. And that is pretty nice if you ask me. Batching queries can offer a substantial performance benefit, and we didn’t even have to change any of the calling code to achieve it.

Obviously, this only works for the queries that return IEnumerables (in our case, that’s every query that doesn’t return a single value). I also added a few more methods to enable query batching for queries that return a single entity, or a scalar value (i kept the original methods in this code snippet as well so you can see the difference):

        public virtual T FindOne(DetachedCriteria criteria)

        {

            return criteria.GetExecutableCriteria(Session).UniqueResult<T>();

        }

 

        public virtual IFutureValue<T> FindFutureOne(DetachedCriteria criteria)

        {

            return criteria.GetExecutableCriteria(Session).FutureValue<T>();

        }

 

        public virtual K GetScalar<K>(DetachedCriteria criteria)

        {

            return (K)criteria.GetExecutableCriteria(Session).UniqueResult();

        }

 

        public virtual IFutureValue<K> GetFutureScalar<K>(DetachedCriteria criteria)

        {

            return criteria.GetExecutableCriteria(Session).FutureValue<K>();

        }

 

        public virtual int Count(DetachedCriteria criteria)

        {

            return Convert.ToInt32(QueryCount(criteria).GetExecutableCriteria(Session).UniqueResult());

        }

 

        public virtual IFutureValue<int> FutureCount(DetachedCriteria criteria)

        {

            return QueryCount(criteria).GetExecutableCriteria(Session).FutureValue<int>();

        }

 

So let’s recap. Queries that return IEnumerables are all batched transparently whenever it’s possible to do so. No calling code had to be modified to get this benefit. Queries that return single values (an entity instance or a scalar value) that still use the ‘old’ FindOne, GetScalar and Count methods obviously couldn’t benefit from the transparent batching without breaking backwards compatibility, but the new methods that were introduced do enable transparent batching for these queries from now on.

Does all of this sound too good to be true? I’d be skeptic too if i were you but i made these changes a few months ago actually and we have been using this stuff on a couple of projects with zero problems.

Obviously, you need NHibernate 2.1 Alpha 1 (or later) for this or the current trunk, both of which i would recommend over NH 2.0 at this point.

  • Share/Save/Bookmark

Posted in NHibernate, Performance | 10 Comments »

Must Everything Be Virtual With NHibernate?

Posted by Davy Brion on 30th March 2009

If you’ve ever used NHibernate 2.0 or later, you will have undoubtedly run into the following runtime exception a couple of times:

NHibernate.InvalidProxyTypeException: The following types may not be used as proxies:
NHibernateExamples.Entities.OrderLine: method get_UnitPrice should be ‘public/protected virtual’ or ‘protected internal virtual’
NHibernateExamples.Entities.OrderLine: method set_UnitPrice should be ‘public/protected virtual’ or ‘protected internal virtual’

Oops… we forgot to make the UnitPrice property on the OrderLine entity virtual. But why does it need to be virtual in the first place? That’s a question that many people who are new to NHibernate have.

The quick answer to that question is: because we need members to be virtual in order to do our lazy loading magic/voodoo.

The longer answer is more interesting though. An important feature that any real ORM must have is transparent Lazy Loading. If you retrieve an object through an ORM, you don’t want it to automatically pull in an entire object graph (not by default anyway), yet you don’t want to litter your code with checks to see if certain associations have been loaded yet, and then loading them if necessary. This is the ORM’s responsibility. Ideally, you want to be able to access properties and have the ORM load the necessary data upon first access of those properties if the data hasn’t been retrieved yet.

NHibernate has this ability, yet it doesn’t require you to inherit from some kind of NHibernate base class or implement any interfaces or anything like that. So how does it work? Well, NHibernate uses proxies of your classes at runtime whenever lazy loading is required. Ok, so what exactly is a proxy? In this case, an NHibernate proxy is a type which is generated dynamically when NHibernate is initialized for your application (this only happens once upon application startup). A proxy type will be generated for each of your entities that hasn’t explicitly been mapped to avoid lazy loading (more on this later). A proxy type for one of your entities will actually inherit from your entity, and will then intercept each possible call you can perform on that type.

Let’s discuss a small example that might make things clearer. Suppose you have an Order class. The Order class has properties such as Employee and Customer, among others. But when you load Order instances, you might not always want the Employee property to already contain the real Employee entity instance. Same thing goes for the Customer property. By default, NHibernate considers each entity type as eligible for lazy loading unless it’s been explicitly configured not to (again, more on this later). So when NHibernate is initialized, it will know that it needs to dynamically generate proxy types for Customer and Employee. Let’s just assume these types will be named CustomerProxyType and EmployeeProxyType (they wouldn’t be called like that btw, but it doesn’t matter). Now suppose that you are retrieving an Order instance (or a bunch of them, doesn’t really matter) and you don’t instruct NHibernate to already fetch the Customer or Employee data. You haven’t requested the Customer or Employee data, so it shouldn’t be there, right? But it shouldn’t be null either, right? So NHibernate assigns an instance of the CustomerProxyType class to the Customer property, and an instance of EmployeeProxyType and initializes both proxies so that they contain their identifier value, which you already have in memory anyway after selecting the order record.

You can safely use the Order instance(s) and you can even access the Employee and Customer instances and nothing will happen. But, whenever you access any of the non-identifier members (that means properties _and_ methods) of a proxy instance, NHibernate needs to make sure that the data of either the Customer or the Employee (depending on which one you’re using) needs to be fetched from the database. So how does NHibernate do that? The proxies will override all of your properties and methods and when one of them is accessed, NHibernate will either fetch the data of the entity if it’s not present yet and then proceed with the original implementation of the property or the method, or it will immediately call the original implementation if the data was already present.

This is basic OO… your entities are base classes to NHibernate’s proxies, and those proxies need to add a little bit of behavior to your entities’ behavior. In order to do that, NHibernate needs to override every public member to make sure that this extra behavior is triggered at the appropriate time. Now, there are quite a few people who dislike this requirement. First of all, there is a minor performance cost to calling virtual members as opposed to calling non virtual members. However, this performance cost is really extremely small and in practically every situation it’s completely negligible. This extra cost certainly doesn’t even compare to some real world performance costs, like hitting the database more often than you should or retrieving more data than you really need. Another reason why some people don’t like this is because they don’t like to enable derived classes to override whatever member they want to. In some cases, this is a valid objection. In most cases however, it’s pure Intellectual Masturbation which offers no real value at all. There are other ORM’s that don’t require you to make your members virtual and they are still able to offer lazy loading features. But those ORM’s usually require you to either inherit from a specified base class, or to implement one or more interfaces that the ORM will use. In both cases, i’d argue that this pollutes your entities far more than virtual members do, but that’s just my opinion.

But for those cases where you really do not want to make members virtual, and don’t mind forgoing on the lazy-loading features of NHibernate, you can simply map those entities to not enable lazy loading at all. You could just map an entity like this:

  <class name="OrderLine" table="OrderLine" lazy="false" >

Setting the lazy attribute to false will ensure that NHibernate will not create a proxy type of your entity type, and that you will always be dealing with instances of the actual type of your entity instead of a possible proxy type. It also means that you will never be able to use any kind of lazy loading when it comes to retrieving instances of these entity types.

  • Share/Save/Bookmark

Posted in NHibernate | 31 Comments »

Avoiding MySQL’s MAX_JOIN_SIZE Limit With NHibernate

Posted by Davy Brion on 30th March 2009

We’re working on an application which has to use a legacy MySQL database. So far, we haven’t really had any problems with MySQL (apart from the dreadful legacy schema but that’s another issue) but today, i started getting an exception with the following message:

The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET SQL_MAX_JOIN_SIZE=# if the SELECT is okay

There is a certain complex view in our application which apparently hits this default limit set by MySQL once there is a certain amount of data present. The view itself is a costly one… it really does need to do a whole lot of joins and rewriting it to use less joins would probably take a long time.

So i figured the better option in this case would be to set the SQL_BIG_SELECTS option to 1. The only problem was: how? It’s not a setting that you can pass through the connection string (or at least, i did not find a way to do so) and NHibernate is taking care of all of the database communication.

I remembered a trick i had used earlier, which is to extend NHibernate’s DriverConnectionProvider. It could then set the setting with the appropriate value whenever the connection is opened, like this:

    public class CustomConnectionProvider : NHibernate.Connection.DriverConnectionProvider

    {

        public override IDbConnection GetConnection()

        {

            IDbConnection connection = base.GetConnection();

            EnableBigSelects(connection);

            return connection;

        }

 

        private void EnableBigSelects(IDbConnection connection)

        {

            using (var command = connection.CreateCommand())

            {

                command.CommandText = "SET SQL_BIG_SELECTS=1";

                command.ExecuteNonQuery();

            }

        }

    }

After that, you just set the DriverConnectionProvider to use in your hibernate.cfg.xml file like this:

    <property name="connection.provider">MyProject.Infrastructure.NHibernate.CustomConnectionProvider, MyProject</property>

And all is well.

  • Share/Save/Bookmark

Posted in NHibernate | 2 Comments »

Implementing A Value Object With NHibernate

Posted by Davy Brion on 25th March 2009

I’ve covered implementing a Value Object before, but this post is about using Value Objects with NHibernate. First, a little recap of what a Value Object is for those who don’t know yet.

A Value Object (also known as Immutable Object) is basically an object without a conceptual identity. A Value Object is defined through its inner values, and not an identity like Entities. This means that a Value Object’s inner values can not be changed after object creation, hence the term Immutable Object. Should you need to change the inner values of the Value Object, you should actually create a new Value Object.

For some of you, this might seem odd. But you’ve actually used Value Objects on many occasions already. In .NET, strings are Value Objects. So are DateTime instances. If you create a string, you can’t modify its inner value. If you do, a new string is actually created. Same thing with a DateTime. The DateTime class provides methods to add days, hours, seconds, whatever… but those methods never modify the instance’s inner value. Instead, they return a new DateTime object because each DateTime instance is immutable.

This has interesting consequences on object equality. Two Value Objects holding the same data should be considered identical objects, even though they are 2 different instances. A Value Object should therefore properly implement the Equals and GetHashCode methods.

For this example, we’ll define a Name class, which is a Value Object consisting of 2 values: FirstName and LastName. If 2 people have the same first and last name, you could say that they have the same ‘name’, right? We’ll just ignore middle names here… The combination of a FirstName and LastName would make for a good Value Object. We need to make sure that once an instance of the Name class has been created, none of its values can be modified. We also need to make sure that multiple instances of the Name class that have the same values can be safely considered equal to each other. The code of the Name class looks like this:

    public class Name : IEquatable<Name>

    {

        private readonly string firstName;

        private readonly string lastName;

 

        public Name(string firstName, string lastName)

        {

            this.firstName = firstName;

            this.lastName = lastName;

        }

 

        // the default constructor is only here for NH (private is sufficient, it doesn't need to be public)

        private Name() : this(string.Empty, string.Empty) {}

 

        public string LastName

        {

            get { return lastName; }

        }

 

        public string FirstName

        {

            get { return firstName; }

        }

 

        public bool Equals(Name other)

        {

            if (ReferenceEquals(null, other)) return false;

            if (ReferenceEquals(this, other)) return true;

            return Equals(other.firstName, firstName) && Equals(other.lastName, lastName);

        }

 

        public override bool Equals(object obj)

        {

            if (ReferenceEquals(null, obj)) return false;

            if (ReferenceEquals(this, obj)) return true;

            if (obj.GetType() != typeof(Name)) return false;

            return Equals((Name)obj);

        }

 

        public override int GetHashCode()

        {

            unchecked

            {

                return (firstName.GetHashCode() * 397) ^ lastName.GetHashCode();

            }

        }

 

        public static bool operator ==(Name left, Name right)

        {

            return Equals(left, right);

        }

 

        public static bool operator !=(Name left, Name right)

        {

            return !Equals(left, right);

        }

    }

Take a close look at the constructors. The public constructor takes both required values (firstName and lastName), and assigns them to the private fields. The default constructor (which we’ve made private) merely calls the public constructor and passes String.Empty to the public constructor’s parameters. As you can gather from the comments on the private constructor, it’s only reason for existence is because NHibernate requires classes to have a default constructor. Well actually, that’s not entirely accurate since it is possible to use classes without a default constructor but it’s not trivial do so.

Creating a private default constructor seems to be a reasonable alternative. Developers can’t create invalid Name instances (unless they cheat with reflection), and NHibernate can use the private constructor so it can create the instances before it fills the fields with the values from the database.

Note: NHibernate allows a private default constructor for Value Objects, but for Entities you will need a default public or protected constructor as private is not sufficient.

We can now use this Value Object in every entity we want by simply adding a property to the entity like this:

        public virtual Name Name { get; set; }

The mapping of the Value Object must be added to the mapping of the entity like this:

    <component name="Name" class="NHibernateExamples.Values.Name" insert="true" update="true">

      <property name="FirstName" column="FirstName" type="string" length="50" not-null="true" access="nosetter.camelcase" />

      <property name="LastName" column="LastName" type="string" length="50" not-null="true" access="nosetter.camelcase" />

    </component>

Notice the value of the access attribute. It’s set to nosetter.camelcase. That means that NHibernate will use the get property when reading the values, but it will use a camelcase private field to set the values when it’s creating the object with values from the database.

  • Share/Save/Bookmark

Posted in NHibernate | 10 Comments »

Entities: Required Properties And Properties That Shouldn’t Be Modified

Posted by Davy Brion on 24th March 2009

How often do you see entities mapped with getters and setters for every property, and only a default constructor (either added implicitly by the compiler or explicitly by a developer)? It’s not really the best way to map entities, so i just wanted to show a better way of doing this.

Consider the OrderLine entity. It has 4 required properties: Order, Product, UnitPrice and Quantity. It also has one optional property called DiscountPercentage. The Order and Product properties should never be changed after the OrderLine was created. It also has a database Id property which should never be changed either.

This is how the code of the OrderLine class would look like:

    public class OrderLine : IIdentifiable<int>

    {

        public OrderLine(Order order, Product product, decimal unitPrice, int quantity)

        {

            if (order == null) throw new ArgumentNullException("order");

            if (product == null) throw new ArgumentNullException("product");

 

            this.order = order;

            this.product = product;

            UnitPrice = unitPrice;

            Quantity = quantity;

        }

 

        // required for NH

        protected OrderLine() {}

 

        private int id;

 

        public virtual int Id

        {

            get { return id; }

        }

 

        private Order order;

 

        public virtual Order Order

        {

            get { return order; }

        }

 

        private Product product;

 

        public virtual Product Product

        {

            get { return product; }

        }

 

        public virtual decimal UnitPrice { get; set; }

        public virtual int Quantity { get; set; }

        public virtual double? DiscountPercentage { get; set; }

    }

There is only one public constructor, which takes all of the required properties as parameters. The protected constructor is only there because NHibernate needs it to create run-time proxies (which enable all of the lazy-loading magic). In theory, you can’t create instances of the OrderLine entity without its required data.

Also, notice how the Id, Order and Product properties only have a getter, and no setter. These values can no longer be changed by developers once the object is constructed. The UnitPrice and Quantity properties do have setters, because these values can be modified after the entity is created.

The mapping for this class looks like this:

  <class name="OrderLine" table="OrderLine" >

    <id name="Id" column="Id" type="int" access="nosetter.camelcase" >

      <generator class="identity" />

    </id>

 

    <many-to-one name="Order" column="OrderId" class="Order" not-null="true" access="nosetter.camelcase" />

    <many-to-one name="Product" column="ProductId" class="Product" not-null="true" access="nosetter.camelcase" />

    <property name="UnitPrice" column="UnitPrice" type="Decimal" not-null="true" />

    <property name="Quantity" column="Quantity" type="int" not-null="true" />

    <property name="DiscountPercentage" column="DiscountPercentage" type="double" />

  </class>

It’s pretty easy… each property that shouldn’t be changed after creation is mapped with the nosetter.camelcase access strategy. That means NHibernate uses the private field to set the value directly after creation, but will use the getters whenever it needs to read the data from the entity.

As you can see, without too much trouble you can make sure that your entities always have their required data, and that properties that shouldn’t change after creation can’t be modified either.

  • Share/Save/Bookmark

Posted in NHibernate | 19 Comments »