Chapter 10. Architectural patterns for persistence – NHibernate in Action

Chapter 10. Architectural patterns for persistence

This chapter covers

  • Designing the persistence layer
  • Implementing reusable Data Access Objects
  • Implementing conversations
  • Supporting Enterprise Services transactions

And so, you’ve finally arrived at the last chapter. We’ve touched on many topics along the way, and you should now feel comfortable about using NHibernate to implement persistence in your applications. You should also be roughly familiar with the breadth of features available in NHibernate and understand the flexibility they give you. We’ve also discussed layered architecture, which will help you build maintainable applications where concerns are neatly separated.

With all this knowledge, you should be able to create the domain model, map it to the database, and implement the business layer and the presentation layer. We’ve discussed domain models, but so far we haven’t addressed the persistence layer in much depth. In chapter 2, you may recall using simple function calls to load, save, and update your entities. These types of examples are great for quickly explaining concepts; but in a real-world application, you’ll benefit from something more structured and coordinated.

This chapter starts with the presentation of the Data Access Object (DAO) pattern. It’s a popular pattern that deals with the organization of the persistence layer. We’ll take this pattern and demonstrate how you can build a neat, structured persistence layer that is both generalized and reusable.

You’ll also learn the basics of session management, which is an important (and somewhat challenging) aspect of working with NHibernate. Following that, we’ll return to the interesting topic of conversations (introduced in chapter 5) and show practical examples of the various ways you can implement conversations with NHibernate.

In the real world, you may be using NHibernate as part of a larger system. This chapter will end with a discussion of distributed applications. It will explain how to make an NHibernate application participate to a distributed transaction.

10.1. Designing the persistence layer

NHibernate is intended to be used in just about any architectural scenario imaginable, as long as the application is based on .NET (or Mono). It may run in an ASP.NET application, WPF, WCF, a Windows Forms, or a Console application. It may even be used inside a web service or Windows service.

These environments are similar as far as NHibernate is concerned; only a few changes are required to port an NHibernate application from one environment to another, as long as the application is correctly layered.

We don’t expect your application design to exactly match the scenario we show here, and we don’t expect you to integrate NHibernate using exactly the code that we use. Rather, we’ll demonstrate some common patterns and let you adapt them to your own needs and goals. For this reason, our examples are written in plain C#, using no third-party frameworks.

We emphasized the importance of disciplined application layering in chapter 1. Layering helps you achieve separation of concerns, making code more readable and maintainable by grouping functionality that does similar things. On the other hand, layering carries a price: each extra layer increases the amount of code it takes to implement a simple piece of functionality—and more code makes the functionality more difficult to change.

We won’t try to form any conclusions about the right number of layers to use (and certainly not about what those layers should be) because the “best” design varies from application to application, and a complete discussion of application architecture is well outside the scope of this book. We merely observe that, in our opinion, a layer should exist only if it’s required, because layers increase the complexity and cost of development. But we agree that a dedicated persistence layer is a sensible choice for most applications and that persistence-related code shouldn’t be mixed with business logic or presentation.

In this section, we’ll show you how to separate NHibernate-related code from your business and presentation layers. The example is based on a console application, but it will be easy to reuse this persistence layer in another (web or Windows) application.

We’ll use the simple “place bid” use case from the CaveatEmptor application to demonstrate our ideas. This use case states that when a user places a bid on an item, CaveatEmptor must perform the following tasks, all in a single request:

  1. Check that when the user enters the bid, the amount is greater than any other bids for the item (you can’t bid lower than someone else!).
  2. Check that the auction hasn’t yet ended.
  3. Create a new bid for the item.

If either of the first two checks fails, the user should be informed of the reason for the failure; if both checks are successful, the user should be informed that the new bid has been made. These checks are the business rules. We also have a nonfunctional requirement: if a failure occurs while accessing the database, the user should be informed that the system is currently unavailable (this is an infrastructure concern).

Let’s see how you can implement this functionality, starting with an overly simple approach.

10.1.1. Implementing a simple persistence layer

In the “Hello World” application of chapter 2, the example program contained simple functions for everything related to persistence. This design doesn’t scale well, and using it in larger applications would result in a sprawling, disorganized mess of functions for creating, reading, updating, and deleting entities.

In this section, we’ll suggest a tidier approach, where you split the persistence layer into a number of classes, each responsible for a specific concern. The first thing we want to tackle is finding a way for the application to obtain new ISession instances. For this, you’ll write a simple helper (or utility) class to handle configuration and initialization of the ISessionFactory (see chapter 3) and also to provide easy access to new ISessions. The full code for this class is shown in listing 10.1.

Listing 10.1. A simple NHibernate helper class
public class NHibernateHelper {
public static readonly ISessionFactory SessionFactory;
static NHibernateHelper() {
try {
Configuration cfg = new Configuration();
SessionFactory = cfg.Configure().BuildSessionFactory();
} catch (Exception ex) {
Console.Error.WriteLine(ex);
throw new Exception("NHibernate initialization failed", ex);
}
}
public static ISession OpenSession() {
return SessionFactory.OpenSession();
}
}

The ISessionFactory is bound to a static (and readonly) variable . All your threads can share this one constant, because the ISessionFactory implementation is thread-safe. This session factory is created in a static constructor , and this constructor is executed the first time this helper class is accessed.

The ISessionFactory is built from a Configuration ; this is the same process we’ve demonstrated throughout the book. You catch and log exceptions , but of course you should use your own logging mechanism rather than Console.Error. The utility class has one public method: a factory method for new ISessions . It’s a convenient method to shorten the code required for the most common usage of this class: opening new sessions.

This (trivial) implementation stores the ISessionFactory in a static variable. Note that this design is completely cluster-safe. The ISessionFactory implementation is essentially stateless (it keeps no state relative to running transactions), except for the second-level cache. It’s the responsibility of the cache provider to maintain cache consistency across a cluster. Thus you can safely have as many ISessionFactory instances as you like. Despite this freedom, in practice you want as few as possible, because the ISessionFactory consumes significant resources and is expensive to initialize.

Now that we’ve solved the problem of where to put the ISessionFactory instance (a common question that arises with NHibernate newcomers), we’ll continue with the use-case implementation.

Performing all the Operations Inside the Same Method

In this section, you’ll write the code that implements the “place bid” use case in a single PlaceBidForItem() method, shown in listing 10.2. This code can live in an ASP.NET code-behind or a function in a console application. It doesn’t matter; let’s assume that wherever it is, the containing program will get some user input and pass it to this method.

It’s worth noting that this code sample isn’t considered a good implementation, but we’ll get to that shortly. It does give us a nice starting point for demonstrating the varying degrees of separation you can introduce into your applications.

Listing 10.2. Implementing a simple use case in one method
public void PlaceBidForItem(long itemId, long userId, double bidAmount) {
try {
using(ISession session = NHibernateHelper.OpenSession())
using(session.BeginTransaction()) {
Item item = session.Load<Item>(itemId, LockMode.Upgrade);

if ( item.EndDate < DateTime.Now ) {
throw new BusinessException("Auction already ended.");
IQuery q =
session.CreateQuery(@"select max(b.Amount)
from Bid b where b.Item = :item");
q.SetEntity("item", item);
double maxBidAmount = (double) q.UniqueResult();
if (maxBidAmount > bidAmount) {
throw new BusinessException("Bid too low.");
User bidder = session.Load<User>(userId);
Bid newBid = new Bid(bidAmount, item, bidder);
item.AddBid(newBid);
session.Transaction.Commit();
}
} catch (HibernateException ex) {
throw new InfrastructureException(
"Error while accessing the database", ex );
}
}

You first get a new ISession using your utility class . You then start a database transaction. The session and transaction will be closed automatically due to the using() statement. If you don’t commit the transaction, or if this commit fails for some reason, the transaction will be automatically rolled back.

You load the Item from the database using its identifier value and also ask for a pessimistic lock so the database won’t allow another transaction to modify the record while you’re working on it. This prevents two simultaneous bids for the same item.

If the end date of the auction is earlier than the current date , you throw an exception so that the persistence layer displays an error message. Usually, you’ll want more sophisticated error handling for this exception, with a qualified error message.

Using an HQL query , you check whether the database contains a higher bid for the current item. If a higher bid exists, you display an error message. Otherwise, if all checks are successful, you place the new bid by adding it to the item . Note that you don’t have to save it manually by calling Save(); it’s saved using NHibernate’s transitive persistence (cascading from the Item to Bid).

Committing the database transaction flushes the current state of the ISession to the database. A try-catch block is responsible for exceptions thrown when rolling back the transaction or closing the session; it’s wrapped to abstract NHibernate details.

As we mentioned, this implementation of the PlaceBidForItem() method isn’t necessarily a good one; it does too much of the hard work itself and takes on the responsibilities of implementing domain logic, enforcing business rules, and carrying out persistence functionality. Essentially, it does the work of a persistence layer, business layer, and domain model combined.

Let’s now see if you can improve things by pushing some of that hard work into the domain model.

Creating a “Smart” Domain Model

The current PlaceBidForItem() method contains code that implements business logic. Let’s move that code to its right place: the Item entity.

To do this, give your Item entity a PlaceBid()method :

public Bid PlaceBid(User bidder, double bidAmount, double maxBidAmount) {
if ( this.EndDate < DateTime.Now )
throw new BusinessException("Auction already ended.");
if (maxBidAmount > bidAmount) {
throw new BusinessException("Bid too low.");

Bid newBid = new Bid(bidAmount, this, bidder);

this.AddBid(newBid);
return newBid;
}

This code enforces business rules that constrain the state of your business objects, but it doesn’t hold any data-access functionality. The motivation here is to encapsulate business logic in the classes of the domain model without worrying about loading and saving data. Hence, you have a separation of concerns.

You may be surprised to see that this new PlaceBid() method has a maxBidAmount parameter—surely the Item entity can find this information for itself. This is a matter of taste, and we’d rather pass this data in from the upper layer than ask the domain object to run queries that may require accessing the persistence layer.

Now that your domain model has taken some responsibility for itself, you can simplify the original PlaceBidForItem() method as follows:

Obviously, you were able to reduce this method because some of the work is now delegated to the domain model (item.PlaceBid). But this code still contains functionality that is relevant to both the persistence layer and the business layer; it’s dealing with both saving and loading entities, and deciding how to construct queries to coordinate the placing of a bid.

To take things a step further, let’s attempt to clearly separate these responsibilities. Remember, as your programs get bigger, combining lots of responsibilities into a single class can lead to software that is difficult to maintain and evolve. Separating responsibilities will make life much easier for you.

Many patterns are available to help you address this separation of concerns. Let’s examine one of the most popular.

Introducing the Data Access Object Pattern

Mixing data access code (the responsibility of the persistence layer) with control logic (part of the business layer) violates our emphasis on separation of concerns. For all but the simplest applications, it makes sense to hide NHibernate API calls behind a façade with higher-level business semantics. There is more than one way to design this façade—some small applications may use a single class for all persistence operations; some may use a class for each operation—but we prefer the Data Access Object (DAO) pattern.

A DAO defines an interface to persistence operations (CRUD and finder methods) relating to a particular persistent entity. It advises you to group code that relates to persistence of that entity. Another common name for this pattern is Gateway (although they have slightly different meanings). If you’ve read Domain-Driven Design[Evans 2004], you may realize that the DAO pattern is similar to the Repository pattern described there. DAO tends to be slightly more fine-grained, having one DAO class per entity. We like both these patterns, but for this example it’s simpler to explain DAO rather than the ins and outs of domain-driven development (DDD).

To begin our explanation of the DAO pattern, let’s create an ItemDAO class, which will eventually implement all persistence code related to Items. For now, it contains only the FindById() method, along with GetMaxBidAmount() and a method to save items. The full code of the DAO implementation is shown in listing 10.3.

Listing 10.3. DAO abstracting item-related persistence operations
public class ItemDAO {
public static Item FindById(long id) {
using (ISession session = NHibernateHelper.OpenSession())
return session.Load<Item>(id);
}
public static double GetMaxBidAmount(long itemId) {
string query = @"select max(b.Amount)
from Bid b where b.Item = :item";
using (ISession session = NHibernateHelper.OpenSession()) {
IQuery q = session.CreateQuery(query);
q.SetInt64("itemId", itemId);
return (double) q.UniqueResult();
}
}
public static Item MakePersistent(Item entity) {
using (ISession session = NHibernateHelper.OpenSession())
session.SaveOrUpdate(entity);
return entity;
}
}

This class provides two static methods to perform the operations needed by your PlaceBid() method. The FindById() method loads items. Note that pessimistic locking isn’t an option here because you’re opening and closing sessions in several places, and you may want the lock to span all these operations (something we haven’t allowed for here, but we’ll get to that). To retrieve the highest bid amount, you can use the GetMaxBidAmount() method . The MakePersistent() method can be used to save items.

Whether GetMaxBidAmount() belongs on an ItemDAO or a BidDAO is a matter of taste; but because the argument is an Item identifier, it seems to naturally belong here. When you’re designing any class interfaces, we encourage you not to be troubled by such decisions because modern refactoring tools make it easy to move responsibilities around if you change your mind.

Your UserDAO also needs a FindUserById() method. You should be able to figure out how to implement it (replace Item with User in listing 10.3).

As a result of all this separation, you reap the rewards of a much cleaner PlaceBidForItem() method:

public void PlaceBidForItem(long itemId, long userId, double bidAmount) {
try {
Item item = ItemDAO.FindById(itemId);
double maxBidAmount = ItemDAO.GetMaxBidAmount(itemId);
User bidder = UserDAO.FindById(userId);
item.PlaceBid(bidder, bidAmount, maxBidAmount);
ItemDAO.MakePersistent(item);
}
} catch (HibernateException ex) {
throw new InfrastructureException(
"Error while accessing the database", ex );
}
}

Notice how much more self-documenting this code is than the first implementation. Someone who knows nothing about NHibernate can understand immediately what this method does, without the need for code comments. You’ve also achieved a clear separation of concerns.

You may be satisfied with this improved implementation, but let’s look at some of its drawbacks. First, it makes transparent persistence impossible. This is why you need to explicitly save the item at the end. The implementation also opens four sessions where a single would be enough. Finally, the implementation of several similar DAOs violates the Don’t Repeat Yourself (DRY) principle, giving you redundancy for the basic CRUD operations.

These problems can all be solved by abstracting the common basic operations and by figuring out a way to make these DAOs share the same session.

Let’s jump to the right solution.

10.1.2. Implementing a generic persistence layer

You learned in the previous section that although it’s easy to implement a simple DAO, a number of key issues require a smarter solution. An ideal solution would allow all DAOs to share the same session and would minimize the amount of code repetition.

Let’s examine a great solution to the first issue of shared sessions, using a feature introduced in NHibernate 1.2.

Using ISessionFactory.GetCurrentSession()

The idea behind this feature is that, for a specific action, you generally need a single session. Because this session can be reused for all the operations (even if they’re unrelated), it’s logical to make this session available to the entire application (that is, to its persistence layer).

Your first impulse may be to create a static session like the session factory defined in listing 10.1. But this won’t work for ASP.NET applications because each HTTP context should have its own NHibernate session (using a static session results in a single session for the whole web application, which is bad because sessions aren’t thread safe).

It’s also possible to open a session and send it to each DAO. In this case, the DAOs would no longer have static methods. You’d instantiate these DAOs and provide the session as a parameter in their constructors. This solution could work, but it’s tedious having to pass the NHibernate session everywhere it may be needed.

Instead of solving this problem yourself, you can leverage the ISessionFactory.GetCurrentSession() method. This method returns the session instance associated with the current persistence context, similar to the ASP.NET notion of an HTTP request context. Any components called in the same context share the same session.

When you use this feature, the specific context of your application is abstracted. Your persistence layer works whether the context is defined by a web or Windows context.

The first step to enable this feature is to set the context. You do so using the configuration property current_session_context_class:

<property name="current_session_context_class">
web
</property>

This example sets the context to web, which is the short name of an implementation included in NHibernate that uses HttpContext to track the current session. It’s therefore appropriate for ASP.NET applications.

NHibernate 1.2.1 comes with a number of built-in current session context implementations, listed in table 10.1.

Table 10.1. NHibernate’s built-in current session-context implementations

Short name

Description

Managed_web

This context was the only one available in NHibernate 1.2.0. It’s now deprecated: use web instead.

Call

This context uses the CallContext API to store the current session. Note that although it works in any kind of application, it isn’t recommended for ASP.NET 2.0 applications.

thread_static

When using this context, sessions are stored in a static field marked with [ThreadStaticAttribute]. Each thread has its own session.

Web

This context uses the HttpContext API to store the current session. It’s recommended for web applications (and only works with them).

It’s obviously possible to implement your own contexts. You have to write a class implementing the extension interface NHibernate.Context.ICurrentSessionContext and set it in the mentioned property. For more details, see this extension’s documentation and the available implementations in the namespace NHibernate.Context. Depending on the implementation of the context, you may have additional work to do. For example, these contexts don’t take care of opening and closing the session. You have to do it yourself and bind the session to the context (using the class CurrentSessionContext).

Let’s see how to use this feature. First, add the following method to the class NHibernateHelper:

public static ISession GetCurrentSession() {
return SessionFactory.GetCurrentSession();
}

Here’s what the ItemDAO class looks like now:

public class ItemDAO {
public static Item FindById(long id) {
return NHibernateHelper.GetCurrentSession().Load<Item>(id);
}
public static double GetMaxBidAmount(long itemId) {
string query = @"select max(b.Amount)
from Bid b where b.Item = :item";
IQuery q = NHibernateHelper.GetCurrentSession().CreateQuery(query);
q.SetInt64("itemId", itemId);
return (double) q.UniqueResult();
}
public static Item MakePersistent(Item entity) {
NHibernateHelper.GetCurrentSession().SaveOrUpdate(entity);
return entity;
}
}

The DAO is no longer responsible for opening the NHibernate session. This is done at an upper level. In the example, it can be done in the PlaceBidForItem() method; see listing 10.4.

Listing 10.4. Session management using the current session API
using NHibernate.Context;
public void PlaceBidForItem(long itemId, long userId, double bidAmount) {
try {
using(ISession session = NHibernateHelper.OpenSession())
using(session.BeginTransaction()) {
CurrentSessionContext.Bind(session);
Item item = ItemDAO.FindByIdAndLock(itemId);
double maxBidAmount = ItemDAO.GetMaxBidAmount(itemId);
User bidder = UserDAO.FindById(userId);
item.PlaceBid(bidder, bidAmount, maxBidAmount);
session.Transaction.Commit();
}
} catch (HibernateException ex) {
throw new InfrastructureException(
"Error while accessing the database", ex );
}
finally {
CurrentSessionContext.Unbind(NHibernateHelper.SessionFactory);
}
}

Once the session is opened, you attach it to the current context so it’s available to any object executed in this context . After that, you can execute the logic as before . Note that a pessimistic lock can now be used. At the end of the operation, you must detach the (closed) session from the current context .

With this implementation, DAOs can access the same session in a neat and transparent way. It’s time to address the other issue in the implementation of a persistence layer: minimizing redundancy.

Designing DAOs Using Generics

Whenever you find yourself repeating a similar code repeatedly, it’s time to think composition, inheritance, and generics. Note that this section assumes you have a good understanding of .NET 2.0 generics. If you’re still using .NET 1.1, it’s possible to adapt the following idea, but the result won’t be as clean.

As you saw when implementing the method FindById() for the ItemDAO and UserDAO classes, only the name of the entity changes for basic CRUD operations. Therefore, it’s possible to use generics to abstract this operation.

The new DAOs may look like this:

public abstract class GenericNHibernateDAO<T, ID> {
public T FindById(ID id) {
try {
return NHibernateHelper.GetCurrentSession().Load<T>(id);
}
catch(HibernateException ex) {
throw new Exceptions.InfrastructureException(ex);
}
}
//...
}
public class UserDAO : GenericNHibernateDAO<User, long> {
}

The GenericNHibernateDAO class only needs the types of the entity T and its identifier ID to implement the FindById() method. After that, implementing the UserDAO class means inheriting from GenericNHibernateDAO and providing these types.

Many other methods can be implemented like that. Before you fill the GenericNHibernateDAO class with them, let’s take a step back and think about the final design.

As far as the business layer is concerned, the persistence layer should provide a set of interfaces to perform all the operations that are needed. This means the underlying implementation doesn’t matter and can be changed as long as the interfaces don’t change.

In your design, you’ll have a GenericDAO interface with operations common to all entities and DAO interfaces, inheriting from the GenericDAO interface for each entity. These interfaces will all have implementations using NHibernate. Figure 10.1 illustrates this design.

Figure 10.1. Generic DAO interfaces with a separated NHibernate implementation

Our experience tells us that even though some interfaces may not have any methods, it’s still important to create them because they’re likely candidates for future extension.

You’ll use the Abstract Factory pattern as a façade to provide the implementations of the interfaces. This means the NHibernate classes will be completely hidden from the other layers. It will even be possible to switch the persistence mechanism at runtime.

Enough theory. Let’s look at the GenericDAO interface:

public interface GenericDAO<T, ID> {
T FindById(ID id);
T FindByIdAndLock(ID id);
IList<T> FindAll();
T MakePersistent(T entity);
void MakeTransient(T entity);
}

This interface defines methods to load (the Find...() methods), save (using MakePersistent()), and delete (using MakeTransient()) entities.


Why MakePersistent() and MakeTransient() instead of Save() and Delete()?

It’s simpler to explain persistence operations using verbs like save and delete. But NHibernate is a state-oriented framework. This notion was introduced in section 5.1.

For example, when you delete an entity, that entity becomes transient. Its row in the database is eventually deleted, but the entity doesn’t cease to exist (and it can even be persisted again). Because these methods are created for the business layer, their names should reflect what happens at that level.


The interfaces inheriting from the GenericDAO interface look like this:

public interface ItemDAO : GenericDAO<Item, long> {
Bid GetMaxBid(long itemId);
Bid GetMinBid(long itemId);
}

Here, you avoid defining a too-specific method like GetMaxBidAmount(). Now, let’s implement these interfaces. First, GenericNHibernateDAO:

public abstract class GenericNHibernateDAO<T, ID>: GenericDAO<T, ID> {
private ISession session;
public ISession Session {
get {
if (session == null)
session = NHibernateHelper.GetCurrentSession();
return session;
}
set {
session = value;
}
}
public T FindById(ID id) {
return Session.Load<T>(id);
}
public T FindByIdAndLock(ID id) {
return Session.Load<T>(id, LockMode.Upgrade);
}
public IList<T> FindAll() {
return Session.CreateCriteria(typeof(T)).List<T>();
}
public T MakePersistent(T entity) {
Session.SaveOrUpdate(entity);
return entity;
}
public void MakeTransient(T entity) {
Session.Delete(entity);
}
}

In this implementation, you add the Session property so the DAOs can work without needing a session bound to the current context. But in this case, you must manually provide this session. You can write another class in the persistence layer to take care of that.

The implementation of the other classes is straightforward. Here’s the implementation of the ItemDAO interface:

public class ItemDAOImpl : GenericNHibernateDAO<Model.Item, long>,
ItemDAO {
public virtual Model.Bid GetMinBid(long itemId) {
IQuery q = Session.GetNamedQuery("MinBid");
q.SetInt64("itemId", itemId);
return q.UniqueResult<Model.Bid>();
}
public virtual Model.Bid GetMaxBid(long itemId) {
IQuery q = Session.GetNamedQuery("MaxBid");
q.SetInt64("itemId", itemId);
return q.UniqueResult<Model.Bid>();
}
}

Although it has nothing to do with this design, we decided to follow another good practice and use named queries. Note that you should wrap all these methods in a try/catch statement in case an exception is thrown:

try {
//...
}
catch (HibernateException ex) {
throw new Exceptions.InfrastructureException(ex);
}

Using this new persistence layer isn’t much different than what is done in listing 10.4. The main difference is that these DAOs must be instantiated. You add another interface to take care of that concern without introducing a dependency to the NHibernate implementation:

public abstract class DAOFactory {
public abstract UserDAO GetUserDAO();
public abstract ItemDAO GetItemDAO();
}

Its implementation is as follows:

public class NHibernateDAOFactory : DAOFactory {
public override UserDAO GetUserDAO() {
return new UserDAOImpl();
}
public override ItemDAO GetItemDAO() {
return new ItemDAOImpl();
}
}

The last step is to instantiate this class at the initialization of the application:

DAOFactory daoFactory = new NHibernateDAOFactory();

Everything is ready for the new PlaceBidForItem() method. Here’s the interesting part of that method:

ItemDAO itemDAO = daoFactory.GetItemDAO();
Item item = itemDAO.FindByIdAndLock(itemId);
double maxBidAmount = itemDAO.GetMaxBid(itemId).Amount;
User bidder = daoFactory.GetUserDAO().FindById(userId);
item.PlaceBid(bidder, bidAmount, maxBidAmount);

The rest of the method remains as in listing 10.4. Interestingly, this code represents exactly what you want the PlaceBidForItem() method to look like (the rest is plumbing).

You need to clean up one other issue: the PlaceBidForItem() method still takes care of creating sessions and dealing with NHibernate. If you agree that managing the NHibernate session is a persistence-layer concern, it shouldn’t appear in this businesslayer method. You’ll do one last refactoring to take care of this issue by using session management. We’ll start by explaining session management for ASP.NET applications, because this is likely to be the most common scenario.

Session Management for Web Applications

When you process a complex request, many classes and methods of the business layer may be involved. The latest implementation of the PlaceBidForItem() method is currently responsible for opening and closing a session; but what if the program needs to call a similar method called UpdateItemPopularity() after calling the PlaceBidForItem() method? Following the current strategy, you’d have to open another session in that function, too.

Aside from the obvious performance issue, this means the business logic must involve one database transaction for each method that opens and closes a session. In turn, this implies that if UpdateItemPopularity() fails, it won’t be possible to fall back to the previous call to PlaceBidForItem(), so you risk leaving your database in an inconsistent state.

Another issue is that after executing the business logic in each of these functions, the entities they manipulate become detached from their session as it’s closed. Lazy loading is subsequently disabled, thus preventing other layers (like the presentation layer) from transparently lazy loading collections of these entities.


Why can’t NHibernate open a new connection (or session) if it has to lazy load associations?

First, we think it’s a better solution to fully initialize all required objects for a specific use case using eager fetching (this approach is less vulnerable to the n+1 selects problem). Furthermore, opening new database connections (and ad hoc database transactions!) implicitly and transparently to the developer exposes the application to transaction-isolation issues. When do you close the session and end the ad hoc transaction—after each lazy association is loaded?

We strongly prefer transactions to be clearly and explicitly demarcated by the application developer. If you want to enable lazy fetching for a detached instance, you can use Lock() to attach it to a new session.


Thankfully, you can easily solve these problems by using a single session that is used for all the high-level functions, and which stays open for the entire request. Rather than make each function in the business layer responsible for session management, you move that responsibility somewhere else. In this section, we’ll take the example of an ASP.NET application, where you want to execute a bunch of operations during a single web request.

Your applications may do lots of things during a single web request—they render web pages, load data in the persistence layer, carry out business functions, and so on. In the approach we’ll describe, you can use the same single session throughout the entire request. This session is opened at the start of the request and closed at the end. This approach provides several benefits: any un-initialized associations or collections are successfully initialized when accessed at any point during the request, and a session is always available for saving and loading entities. Furthermore, entities aren’t detached during the request as their session is not closed.

Despite the benefits of this session-per-request approach, don’t be tempted to get lazy and let NHibernate always load data on demand; it may eventually kill your application’s performance. Always eagerly load the data you know you’ll need. For more details, read section 7.7.1.

A simple way of implementing session-per-request is to open and attach an NHibernate session at the beginning of the web request. ASP.NET lets you implement the IHttpModule interface in order to execute code at the beginning and end of a web request. Listing 10.5 shows how to leverage this feature.

Listing 10.5. Web module managing NHibernate sessions
using NHibernate.Context;
public class NHibernateCurrentSessionWebModule : IHttpModule {
public void Init(HttpApplication context) {
context.BeginRequest += new EventHandler(Application_BeginRequest);
context.EndRequest += new EventHandler(Application_EndRequest);
}
public void Dispose() {
}
private void Application_BeginRequest(object sender, EventArgs e) {
ISession session = NHibernateHelper.OpenSession();
session.BeginTransaction();
CurrentSessionContext.Bind(session);
}
private void Application_EndRequest(object sender, EventArgs e) {
ISession session = CurrentSessionContext.Unbind(
NHibernateHelper.SessionFactory );
if (session != null)
try {
session.Transaction.Commit();
}
catch(Exception ex) {
session.Transaction.Rollback();
Server.Transfer("...", true);
}
finally {
session.Close();
}
}
}

At the beginning of a request , you open and attach a session. At its end , you detach and close the session. You also commit the changes that happen thorough the request’s lifetime.

The following code must be added to the Web.config file:

<configuration>
<system.web>
<httpModules>
<add name="NHibernateCurrentSessionWebModule"
type="NHiA.NHibernateCurrentSessionWebModule" />
</httpModules>
</system.web>
</configuration>

This code is required to register your web module so ASP.NET uses it.

Now, the PlaceBidForItem() method is how you want it to be:

public void PlaceBidForItem(long itemId, long userId, double bidAmount) {
try {
ItemDAO itemDAO = daoFactory.GetItemDAO();
Item item = itemDAO.FindByIdAndLock(itemId);
double maxBidAmount = itemDAO.GetMaxBid(itemId).Amount;
User bidder = daoFactory.GetUserDAO().FindById(userId);
item.PlaceBid(bidder, bidAmount, maxBidAmount);
} catch (Exception ex) {
throw new BusinessException("Placing the bid failed.", ex);
}
}

If you want to call some other method (such as UpdateItemPopularity()) after this, you can do so using the same session and transaction. You have a session that lives as long as the web request.


Do I really have to write all this infrastructure code?

In this chapter we explain how you can write your own data access objects, HTTP modules for ASP.NET session management, and various other useful infrastructure-related components.

Understanding how to do this is great, but you don’t have to write this code yourself. Many people have done this for you already! Existing NHibernate libraries take care of much of the hard work, and include the best practices described here. After reading this section, we encourage you to research some of those libraries, including NHibernate Burrow, Castle ActiveRecord, Rhino Tools, and S#arp Architecture.

Even if you decide not to use any of them, you can learn a great deal by browsing the code of these libraries.


You can build almost any application using this approach, although sometimes you may feel you need something more. For example, what if you have a long operation that is completed over several web requests? Is there a better way to handle those longer use cases? We’ll look at this next.

10.2. Implementing conversations

You’ve implemented your persistence layer, and you may think that you’re finished. Unfortunately, not quite! There is a common real-world scenario that your persistence layer hasn’t accommodated: long-running conversations.

We discussed the notion of conversations in section 5.2. Despite discussing the mechanics of these features, we didn’t explain how they’re used in the context of real NHibernate applications; we now return to this essential subject.

When you’re using a command-oriented framework (for example, ADO.NET), each API call is meant to retrieve or change data: it adds/updates/deletes rows in a database. But NHibernate’s API takes a different approach: it’s state oriented. Rather than execute a command to cause a direct result in the database, each API call is meant to change the state of an entity (as illustrated in figure 5.1).

Changing the state of an entity may lead to the execution of a SQL command immediately, or it may lead to execution some time later when the session is flushed. Alternatively, it may never lead to the execution of SQL, such as when you’re using FlushMode.Never, and may decide to cancel a set of updates. It’s important to be aware of this difference when you’re implementing operations that span many user requests (conversations).

In an application that uses NHibernate, you can implement conversations three ways: using a long session, using detached objects, and loading objects on each request. We’ll start with the latter; it’s by far the simplest and is particularly well suited to web applications. First, you need a use case with which to illustrate these ideas.

10.2.1. Approving a new auction

Your auction has an approval cycle. A new item is created in the Draft state. The user who created the auction may place the item in the Pending state when the user is satisfied with the item details. System administrators may then approve the auction, placing the item in the Active state and beginning the auction. At any time before the auction is approved, the user or any administrator may edit the item details. Once the auction is approved, no user or administrator may edit the item. It’s essential that the approving administrator sees the most recent revision of the item details before approving the auction and that an auction can’t be approved twice. Figure 10.2 shows the item-approval cycle.

Figure 10.2. State chart of the item-approval cycle in CaveatEmptor

The conversation is auction approval, which spans two user requests (and possibly many web requests over many days). First, the administrator selects a pending item to view its details; second, the administrator approves the auction, moving the item to the Active state. The second request must perform a version check to verify that the item hasn’t been updated or approved since it was retrieved for display.

As usual, the business logic for approving an auction should be implemented by the domain model. In this case, you add an Approve() method to the Item class:

public void Approve(User byUser) {
if ( !byUser.IsAdmin )
throw new PermissionException("Not an administrator.");
if ( state.equals != ItemState.Pending )
throw new BusinessException("Item not pending.");
state = ItemState.Active;
approvedBy = byUser;
approvalDatetime = DateTime.Now;
}


Are conversations really transactions?

Most books define transaction in terms of the ACID properties: atomicity, consistency, isolation, and durability. Is a conversation a transaction by that definition? Consistency and durability don’t seem to be a problem, but what about atomicity and isolation? Our example is both atomic and isolated, because all update operations occur in the last request/response cycle (that is, the last database transaction). But our definition of a conversation permits update operations to occur in any request/ response cycle. If a conversation performs an update operation in any but the final database transaction, it isn’t atomic and may not even be isolated. Nevertheless, we feel that the term transaction is appropriate, because systems with this kind of conversation usually have functionality or a business process that lets the user compensate for the lack of atomicity (allowing the user to roll back steps of the conversation manually, for example).


This code should give you a feel for what the use case is about. Now we can look at how to implement the conversation that realizes this use case. As we said, you can choose from three approaches when implementing conversations, and we’ll start by demonstrating the simplest one.

10.2.2. Loading objects on each request

A simple way to implement conversations is to load all persistent instances at the beginning of a request and discard them at the end (web request or otherwise). If you’ve worked with ASP.NET applications, you may be used to doing this using Linq to SQL, DataSets, or DataReaders.

We like this approach because it’s simple to manage and implement, but it also has a few caveats. Let’s use our use case to explain them.

The longer the administrator spends deciding whether to approve the auction, the greater the risk that some other user will edit the auction details, thus making the displayed Item out of date (the page is showing stale data). Suppose your first request executed the following code to retrieve the auction details:

public Item ViewItem(long itemId) {
return itemDAO.FindById(itemId);
}

In a typical web request, you’d load the Item, display it on screen, and then discard it. You’d need to store the identifier value somewhere so you could retrieve the same Item again for use in the next request, after the administrator clicked the Approve button to approve the Item. It seems superficially reasonable that the newly loaded Item would hold nonstale data for the duration of the second database transaction, so all is well.

Not so! This notion has one problem: the data might have gone out of date while the administrator was looking at it (someone else could have updated it). It’s possible that the administrator based the decision to approve the Item on false information. Reloading the Item to approve it in another request wouldn’t help at all, because the reloaded state wouldn’t be shown on screen and wouldn’t be used for anything—at least, it couldn’t be used in deciding whether the auction should be approved, which is the important thing.

To ensure that the entity’s state at the time of approval is the same as the entity’s state at the time of viewing, you need to perform an explicit manual version check. The following code demonstrates how this can be implemented by the business layer:

public void ApproveAuction(long itemId,
int itemVersion,
long adminId) {
Item item = itemDAO.FindById(itemId);
if ( itemVersion != item.Version ) )
throw new StaleItemException();
User admin = userDAO.FindById(adminId);
item.Approve(admin);
}

In this case, the manual version check isn’t difficult to implement. You take note of the version of the record that was loaded in the first request, and then you make sure it’s the same when you decide to approve it in the second request.

Despite its simplicity, this load-objects-on-each-request approach doesn’t always fit the bill; you may consider one of the other approaches that we tackle in the next section. For example, this approach may not work in a more complex use case that has many relationships and related objects. It would be tedious to perform all the version checks manually for all objects that are to be updated. These manual version checks should be considered noise—they implement a purely systemic concern not expressed in the business problem.

Some would argue that the previous code snippet contains other unnecessary noise, too; you already retrieved the Item and User in previous requests. Is it necessary to reload them in each request? It should be possible to simplify the control code to the following:

public ApproveAuction(Item item, User admin) {
item.Approve(admin);
}

Doing so not only saves three lines of code but is also arguably more object oriented—the system is working mainly with domain model instances instead of passing around identifier values. This code is also quicker, because it saves two SQL SELECT queries that uselessly reload data. How can you achieve this simplification using NHibernate?

10.2.3. Using detached persistent objects

Suppose you kept the Item as a detached instance (this approach is common in Windows applications). You could reuse it in the second database transaction by reassociating it with the new NHibernate session using either Lock() or Update(). Let’s see what these two options look like.

In the case of Lock(), you adjust the ApproveAuction() method to look like this:

public void ApproveAuction(Item item, User admin) {
try {
NHibernateHelper.GetCurrentSession()
.Lock(item, LockMode.None);
} catch (HibernateException ex) {
throw new InfrastructureException(ex);
}
item.Approve(admin);
}

The call to ISession.Lock() reassociates the item with the new NHibernate session and ensures that any subsequent change to the state of the item is propagated to the database when the session is flushed (for a discussion of the different LockModes, see section 5.1.8).

Because Item is versioned (if you map a <version> property), NHibernate checks the version number when synchronizing with the database, using the mechanism described in section 5.2.1. You don’t have to use a pessimistic lock, as long as concurrent transactions are allowed to read the item in question while the approval routine runs.

Of course, it would be better to hide NHibernate code in a new DAO method, so you add a new Lock() method to the ItemDAO. Doing so lets you simplify the ApproveAuction() method as follows:

Alternatively, you can use Update(). For the example, the only real difference is that Update() can be called after the state of the item has been modified, which would be the case if the administrator made changes before approving the auction:

public ApproveAuction(Item item, User admin) {
item.Approve(admin);
itemDAO.MakePersistent(item);
}

Again, NHibernate performs a version check when updating the item.

Is this implementation, using detached objects, any simpler than the load-objects-on-every-request approach? You still need an explicit call to the ItemDAO, so the point is arguable. In a more complex example involving associations, you’ll see more benefit, because the call to Lock() or Update() may cascade to associated instances. And let’s not forget that this implementation is more efficient, avoiding the unnecessary SELECTs.

Nevertheless, we’re not satisfied. Is there a way to avoid the need for explicit reassociation with a new session? One way is to use the same NHibernate session for both database transactions, a pattern we described in chapter 5 as session-per-conversation or long session.

10.2.4. Using the session-per-conversation pattern

A long session is an NHibernate session that spans a whole conversation, allowing reuse of persistent instances across multiple database transactions. This approach avoids the need to reassociate detached instances created or retrieved in previous database transactions.

A session contains two important kinds of state: a cache of persistent instances and an ADO.NET IDbConnection. We’ve already stressed the importance of not holding database resources open across multiple requests. The session needs to release its connection between requests if you intend to reuse it in the many web requests that may span the conversation.

As explained in section 5.1.4, NHibernate 1.2 keeps the connection open from the first time it’s needed to the moment the transaction is committed. Committing the transaction at the end of each request is enough to close the connection. You don’t want that, because a closed session is useless in subsequent transactions.

Listing 10.5 used the web context to store the session. This strategy uses HttpContext, and the session lives only as long as the request. How do you make your session usable across multiple requests?

The simplest solution is to keep the NHibernate session in the ASP.NET session state, so that it’s available from one request to another. You’ll use this approach in the following example.

Let’s write a new web module called NHibernateConversationWebModule (see listing 10.6). It will store the NHibernate session between requests instead of discarding it. It will also handle the reattaching of the session to the new context.

Listing 10.6. NHibernateConversationWebModule for conversations
public class NHibernateConversationWebModule : IHttpModule {
const string NHibernateSessionKey =
"NHiA.NHibernateSession";
const string EndOfConversationKey = "NHiA.EndOfConversation";
public static void
EndConversationAtTheEndOfThisRequest() {
HttpContext.Current.Items[EndOfConversationKey] = true;
}
public void Init(HttpApplication context) {
context.PreRequestHandlerExecute +=
new EventHandler(OnRequestBeginning);
context.PostRequestHandlerExecute +=
new EventHandler(OnRequestEnding);
}
public void Dispose() {
}
private void OnRequestBeginning(object sender,
EventArgs e) {
ISession currentSession =
(ISession)HttpContext.Current.Session[NHibernateSessionKey];
if (currentSession == null) {
currentSession = NHibernateHelper.OpenSession();
currentSession.FlushMode = FlushMode.Never;
}
CurrentSessionContext.Bind(currentSession);
currentSession.BeginTransaction();
]
private void OnRequestEnding(object sender,
EventArgs e) {
ISession currentSession =
CurrentSessionContext.Unbind(NHibernateHelper.SessionFactory);

if (HttpContext.Current.Items[EndOfConversationKey]
!= null) {
currentSession.Flush();
currentSession.Transaction.Commit();
currentSession.Close();
HttpContext.Current.Session[NHibernateSessionKey] = null;
}
else {
currentSession.Transaction.Commit();
HttpContext.Current.Session[NHibernateSessionKey] =
currentSession;
}
}
}

Because you’ll be storing the NHibernate session in a map, you need a key to define its location. Because the business logic is responsible for ending the conversation, it needs to call the EndConversationAtTheEndOfTheRequest() method to set a value in the current context that will be used at the end of the request. The initialization of the module registers events for the beginning and the end of requests. Note that you aren’t using the same events as in listing 11.5 because these let you access the ASP.NET session state.

When beginning a new request , if a conversation is already running, you extract its detached NHibernate session from the ASP.NET session state . Otherwise , you start a new conversation by opening a new session. We’ll explain why you set its flush mode to never in the next section. Once you have the NHibernate session of the running conversation, you bind it to the current context and begin a new transaction. At this point, the conversation is ready to be used anywhere in this web request.

When the time comes to end the request , you detach its NHibernate session from the current context . If the value to end the conversation was set , you manually flush the session to process all the changes made in the conversation, you commit these changes, you close the session, and you then remove the NHibernate session from ASP.NET session state. If the conversation is suspended , you commit the transaction to close its database connection, and you store the session in the ASP.NET session state. This conversation will resume when the next request starts.

This implementation isn’t complete because the exception-handling part is missing. Refer to the CaveatEmptor source code for an example. Basically, these methods should be inside a try/catch statement. When catching an exception, you should roll back the current transaction and detach and then close the current session. There is also another issue that we’ll cover in the next section.

Starting, Continuing, and Ending a Conversation

Now let’s see how you can use this conversation module (don’t forget to register it). If you recall, in the example the administrator is viewing and then approving an auction. You’ll have a conversation that spans two web requests: the first to view the action and the second to approve it.

The following ASP.NET web page displays the auction’s item when loading (first request), and it provides a button to approve this auction (second request):

In this example, you store the item in the ASP.NET session state between the requests (don’t forget to enable the session).

In the case of a Windows application, the implementation of a conversation is simpler because you can store everything locally in memory. It’s also possible to mimic this example by using the CallContext class.

Cancelling a Conversation

You can also support canceling a conversation. All you have to do is replace the EndConversationAtTheEndOfTheRequest() method with two methods: one to accept the changes and another one to cancel them. Then you must distinguish these values when ending the conversation. You cancel the conversation by closing the session without flushing it.

There is an exception to this solution. To help you understand this problem, we need to explain some theory behind the implementation of conversations. Then we’ll describe how to deal with the problem.

Guaranteeing Atomicity and Compensating for Changes

A conversation is supposed to behave like a database transaction, so one of its requirements is to be atomic. In order to guarantee the atomicity of a conversation, all the changes made by the requests should be committed only when ending the conversation.

But by default, committing a transaction makes NHibernate commit the detected changes to the database. The session is flushed at that moment, collecting all the changes made since it was opened and executing the corresponding SQL commands.

The solution to change this behavior is to set the NHibernate session to FlushMode. Never, as we’ve done in listing 10.6.

To propagate any changes to the database, you must explicitly flush the session when you’re ready (at the end of the conversation). Until then, all changes are tracked inside the NHibernate session, and no database commands are issued. Note that any queries involving the database aren’t aware of these unflushed changes, so they may return stale data.

Now the exception: When you ask the session to Save() an entity whose identifier is generated by the database, perhaps using an identity column, NHibernate must save this entity immediately in order to retrieve its identifier. This can cause side effects if you have triggers, constraints, and keys set up, for example.

Another related consideration is that if the conversation is then canceled, it will be necessary to revert these changes in the database by deleting the stub record or undoing any database triggers that were fired. If the NHibernate session throws an exception during the conversation; you should perform a similar cleanup, end the conversation, and close the session.

You can look at the IInterceptor API (used in section 9.4) to keep track of these permanent changes and revert them if necessary. Alternatively, you can avoid using auto-generated identifiers in your database.

Sometimes, you may want conversations to persist changes at each request. This lets you ensure a recovery mechanism in case of a system failure. In this case, you again have to provide compensation actions whenever a conversation is cancelled, to clean up any database debris.

There’s one final potential complication in guaranteeing atomicity with the long session approach: NHibernate’s ISession implementation isn’t thread-safe. If an environment allows multiple requests from the same user to be processed concurrently, it’s possible that these concurrent requests can obtain the same NHibernate ISession instance. This will result in unpredictable behavior. This problem also affects the previous approach, which uses detached objects, because detached objects also aren’t thread-safe. This problem affects any application that keeps mutable state in a non-thread-safe cache.

Because this isn’t a generic problem, we’ll leave it to you to find an appropriate solution if you’re confronted with it. It’s worth mentioning that the NHibernate. Burrow project may offer some valuable insights, because it’s specifically designed to help ASP.NET work with the session-per-conversation pattern. Another good solution is to reject any new request if a request is already being processed for the same user. Other applications may need to serialize requests from the same user. Note that this isn’t a problem for web applications that use the ASP.NET session state: concurrent requests (from the same user) are automatically serialized, so the second request waits until the first completes.

Now that we’ve covered three different ways to deal with conversations, you may be confused when trying to choose one for your application. When are each of the three conversation approaches we’ve discussed relevant?

10.2.5. Choosing an approach to conversations

Our default recommendation for NHibernate web applications is to use the load-objects-on-every-request approach, where you create one NHibernate session per request. It’s easy to understand and in many cases easier to implement than the other approaches. It’s particularly well suited to architectures in which you’re unable to keep state associated with the user because you’re using a stateless framework. This approach will especially appeal to those who prefer to avoid using the ASP.NET session. Another good fit for this approach is in architectures where the web tier should never access the domain model directly, and so the domain model is completely hidden from the presentation layer behind an intermediate Data Transfer Object (DTO) abstraction layer.

The second most popular approach in NHibernate applications uses detached objects, with a new session per database transaction. In particular, this is the method of choice for an application where business logic and data access execute in the data access layer but the domain model is also used in the presentation tier, avoiding the need for tedious DTOs. This approach is even being used successfully in Windows applications. But we think that in many cases, it isn’t the best approach.

More complex cases may be suited to the long-session approach, especially Windows Forms applications. So far, we’ve found this approach difficult to explain, and it isn’t well understood in the NHibernate community. We suppose this is because the notion of a conversation isn’t widely understood, and most developers aren’t used to thinking about problems in terms of conversations. We hope this situation changes soon, because this idea is useful even if you don’t use the long-session approach.

The next step is to see how you can take this code and adapt it to run in an Enterprise Services application. Obviously, you’d like to change as little as possible. We’ve been arguing all along that one advantage of POCOs and transparent persistence is portability between different runtime environments. If you now have to rewrite all the code for placing a bid, we’re going to look silly.

10.3. Using NHibernate in an Enterprise Services application

By Enterprise Services application, we mean an application that takes advantage of the distributed transaction service of .NET Enterprise Services. Chapter 5 contains a brief explanation of the steps required to make an NHibernate application participate in a distributed transaction. Now you’ll implement this approach.

First, we must cover an issue linked to interprocess requests. Whenever you have physically separated components/tiers, you must minimize the communication between them. This is important because latency is added by every interprocess request, increasing the application response time and reducing concurrency due to the need for either more database transactions or longer transactions. These issues can strongly influence the scalability of your application.

It’s essential that all data access related to a single user request occur within a single request to the persistence layer. This means you can’t use a lazy approach, where the presentation layer pulls data as needed. Instead, the business layer must accept responsibility for fetching all data that will be needed subsequently by the presentation layer.

The ubiquitous DTO pattern provides a way of packaging the data the presentation layer will need. A DTO is a class that holds the state of a particular entity; you can think of a DTO as a POCO without any business methods. But because DTOs tend to duplicate entities, we naturally find ourselves questioning the need for DTOs.

10.3.1. Rethinking DTOs

DTOs are commonly used to totally separate the presentation tier from the domain model. Certain reasonable arguments can be made in favor of this approach, but you shouldn’t mistake these arguments for the real reason why DTOs are useful.

The idea behind the DTO pattern is that fine-grained remote access is slow and unscalable. It’s also useful when the domain model can’t be made serializable. In this case, another object must be used to package and carry the state of the business objects between tiers.

There are now twin justifications for the use of DTOs: first, DTOs implement externalization of data between tiers; second, DTOs enforce separation of the presentation tier from the business-logic tier. Only the second justification applies to you, and the benefit of this separation is questionable when weighed against its cost. We won’t tell you never to use DTOs (sometimes we’re less reticent). Instead, we’ll list some arguments for and against use of the DTO pattern in an application that uses NHibernate and ask you to carefully weigh these arguments in the context of your own application.

It’s true that the DTO removes the direct dependency of the presentation tier on the domain model. If your project partitions the roles of .NET developer and web designer, this may be of some value. In particular, the DTO lets you flatten domain-model associations, transforming the data into a format that is perhaps more convenient for presentation purposes (it may greatly ease data binding). But in our experience, it’s normal for all layers of the application to be highly coupled to the domain model, with or without the use of DTOs. We don’t see anything wrong with that, and we suggest that it may be possible to embrace the fact.

The first clue that something is wrong with DTOs is that, contrary to their title, they aren’t objects. DTOs define state without behavior. This is immediately suspect in the context of object-oriented development. Even worse, the state defined by the DTO is often identical to the state defined in the business objects of the domain model—the supposed separation achieved by the DTO pattern can also be viewed as mere duplication.

The DTO pattern exhibits two of the code smells described in Refactoring: Improving the Design of Existing Code [Fowler 1999]. The first is the shotgun-change smell, where a small change to a system requirement requires changes to multiple classes. The second is the parallel class hierarchies smell, where two different class hierarchies contain similar classes in a one-to-one correspondence. The parallel class hierarchy is evident in this case—systems that use the DTO pattern have Item and ItemDTO, User and UserDTO, and so on. The shotgun-change smell manifests itself when you add a new property to Item: you must change not only the presentation tier and the Item class, but also the ItemDTO and the code that assembles the ItemDTO instance from the properties of an Item (this last piece of code is especially tedious and fragile).

Of course, DTOs aren’t all bad. The code we just referred to as “tedious and fragile”—the assembler—has value even in the context of NHibernate. DTO assembly provides a convenient point at which to ensure that all data the presentation tier needs is fully fetched before returning control to the presentation tier. If you find yourself wrestling with NHibernate LazyInitializationExceptions in the presentation tier, one possible solution is to try the DTO pattern, which imposes extra discipline by requiring that all needed data is copied explicitly from the business objects (we don’t find that we need this discipline, but your experience may vary).

Finally, DTOs may have a place in data transfer between loosely coupled applications (our discussion has focused on their use in data transfer between tiers of the same application). But typed DataSets seem better adapted to this problem.

You can consider a typed DataSet as a special kind of DTO. There are definitively good reasons to use DataSets: an extensive toolset is available, and many existing libraries use DataSets. But writing custom classes as DTOs gives you better control over the design of your application even if the code is tedious to write.

You won’t use DTOs in the CaveatEmptor application. Now that we’ve covered the potential issues that may occur when you’re dealing with physically separated tiers, we can go back to distributed transactions.

10.3.2. Enabling distributed transactions for NHibernateHelper

You must apply a few changes to the previous NHibernateHelper class to enable distributed transactions. First, you must add a reference to the System.EnterpriseServices assembly and change the class definition like this:

[Transaction(TransactionOption.Supported)]
public class NHibernateHelper : ServicedComponent {
//...
}

You must also add the following methods to the management of the transaction: BeginTransaction(), CommitTransaction(), and RollbackTransaction(). They’re used to create, commit/roll back, and close the distributed transaction. Here are the two first methods:

public static void BeginTransaction() {
ServiceConfig sc = new ServiceConfig();
sc.Transaction = TransactionOption.RequiresNew;
ServiceDomain.Enter(sc);
}
public static void CommitTransaction() {
try {
ContextUtil.SetComplete();
ServiceDomain.Leave();
}
catch(HibernateException ex) {
throw new InfrastructureException(ex);
}
}

In order to take part in the distributed transaction, the NHibernate session’s transaction must be enlisted. Note that you have to use .NET reflection here because the method used isn’t part of the IDbConnection interface:

private static void TryEnlistDistributedTransaction(ISession session) {
if (ContextUtil.IsInTransaction) {
IDbConnection conn = session.Connection;
MethodInfo mi = conn.GetType().GetMethod(
"EnlistDistributedTransaction",
BindingFlags.Public | BindingFlags.Instance );
if (mi != null)
mi.Invoke( conn,
new object[] {
(System.EnterpriseServices.ITransaction)
ContextUtil.Transaction } );
}
}

If the EnlistDistributedTransaction() method isn’t available, this code silently fails to enlist the transaction. If it represents an error in your use case, you should throw an exception. You can even avoid reflection if you know which type of database connection is used.

Assuming that the application must always try to enlist the distributed transaction, you can change the implementation of the OpenSession() method:

public static ISession OpenSession() {
ISession session = SessionFactory.OpenSession();
TryEnlistDistributedTransaction(session);
return session;
}

All that’s left to do is update the code to use these new methods. Don’t forget to register the resulting COM+ assembly (which must be signed) before you use it. To do so, you use the command-line executable RegSvcs.

10.4. Summary

This chapter focused on the design of the persistence layer. We first introduced the NHibernateHelper class, which is useful to abstract the initialization of NHibernate. We also showed you how to move from a monolithic method that mixes all the concerns to a neat architecture with a clear separation between the layers.

We illustrated a smart domain model by implementing business logic in the CaveatEmptor Item class. This was the first step of a series of refactorings.

You used the DAO pattern to create a façade for the persistence layer, hiding NHibernate’s internals from the other layers. We also introduced the ISessionFactory.GetCurrentSession() API and the notion of context. You used this feature to significantly improve your DAOs by making them share the same session without having to pass the session as a parameter and without using a global static session.

After that, you leveraged the .NET 2.0 generics to reduce the redundancies in the persistence layer. You also designed the persistence layer so the other layers were unaware of the persistence framework that was used. You even made it possible to switch from one implementation to another by changing a single line of code.

We explained how to make a session live for an entire web request. This is useful to guarantee that a single session is used for all processing and that lazy loading always works transparently.

Chapter 5 introduced the notion of a conversation (also called application/business transaction). In this chapter, we provided three ways of implementing conversations: the load-objects-on-each-request approach, which is simple and suits many web applications; the approach that uses detached persistent objects, which is useful in stateless environments; and the approach that uses long-living sessions. The last approach is relatively unknown; but it’s powerful, especially in a rich environment.

This chapter ended by improving the NHibernateHelper class to support Enterprise Services transactions. We discussed the potential latency issue and the DTO pattern. Although this pattern can be useful when decoupling the domain model is important, we agree that such a situation is rare and that the cost of maintaining the DTO is too high.

At this point, you should have all the technical knowledge required to leverage the features of NHibernate. It’s a powerful tool, but it requires a deep understanding of its behavior to be correctly used.

NHibernate has a growing, enthusiastic community. We hope that you’ll enjoy being part of it. The Google nhusers forum is frequented by many of the NHibernate developers, so feel free to drop by to find answers to your questions and join in the discussions. Also, we recommend http://nhforge.org for its wealth of blog posts and articles. It’s also a great place to keep up with new NHibernate developments. Happy NHibernating!