Chapter 8. Developing NHibernate applications – NHibernate in Action

Chapter 8. Developing NHibernate applications

This chapter covers

  • Implementing layered applications
  • Solving issues when setting up .NET applications using NHibernate
  • Achieving design goals
  • Solving debugging and performance problems
  • Using integrating services like audit logging

At this point, you may be thinking, “I know all about NHibernate features, but how do I fit them together to build a full NHibernate application?” It’s time for us to answer that question and to show you how to apply the knowledge you’ve gained to implement applications as part of a real-world development process.

We discussed the architecture of an NHibernate application in section 1.1.5. This provided the bird’s-eye view, but you need to get from that point to working with executable code.

We discuss the development process layer by layer, showing the internals of each layer and how each should handle its designated responsibilities. We also discuss how layers should communicate with each other.

Because this book focuses on NHibernate, the domain model and persistence layers draw most of our attention. However, using NHibernate requires design decisions throughout all the application layers, so we provide details where we feel they will help.

The complexity of defining and building a layered application depends on the complexity of the problem at hand. For instance, the “Hello World” application in chapter 2 is trivial, but building an application as complex as the CaveatEmptor example is more challenging. By following the advice given in this chapter, you should be able to find your way through any difficult patches.

This chapter begins by focusing on the implementation of an NHibernate application. First, we rediscover the classic architecture of an NHibernate application. We talk about its layers and their purposes and briefly about their implementations. After that, we discuss some issues relating specifically to .NET applications, including web applications, security, and remoting. Finally, we explore approaches to troubleshooting and bug-fixing in your NHibernate applications. This part also serves as a map for the next two chapters; it will provide references to upcoming sections for more details.

The last part of this chapter is about services: how to integrate loosely coupled components with an NHibernate application. You’ll learn how to use the IInterceptor interface to efficiently integrate services like audit logging. We also discuss some other alternatives.

Let’s start with the architecture of an NHibernate application and its implementation.

8.1. Inside the layers of an NHibernate application

Chapter 1 presented an overview of the layered architecture of an NHibernate application. In this chapter, we look at the code that belongs in these layers.

If you recall, disciplined layering is important because it helps you achieve separation of concerns, making code more readable and maintainable by grouping code that does similar things. Layering also carries a price: each extra layer increases the amount of code required to implement a piece of functionality—and more code makes the functionality more difficult to change.

We don’t try to form any conclusions about the correct 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 it increases the complexity and costs of development.

In this section, we go through the layers introduced in chapter 1, explaining their roles and discussing their implementations. That way, you’ll progressively learn how to develop an NHibernate application. These layers are as follow:

  • The business layer (with the domain model)
  • The persistence layer
  • The presentation layer

Only two of these layers matter to the end user: the business layer matters because it embodies the problem the application is supposed to solve, and the presentation layer matters because it lets the user issue commands to the application and see the results.

The user doesn’t care about the persistence layer directly, but it’s obviously essential to most applications. It’s useful to remember that persistence is a service that the application uses (like those presented in section 8.4), and is there to permit loading and saving of an application’s data. Keeping this viewpoint in mind will help you achieve a good separation of concerns.

If you think carefully about the implementation of these layers, you’ll see that they all exist to augment the domain model in some way. This is why we say that the development process of an NHibernate application is domain-centric; this approach is called domain-driven development (DDD). Because testing is part of the development process, we also discuss testing these layers and see how you can apply test-driven development (TDD).

Before we go any further, an introduction to patterns, DDD, and TDD is required.

8.1.1. Using patterns and methodologies

The breadth of this chapter requires us to mention many patterns and practices related to software development. Explaining each in depth is beyond the scope of this book, but we give a brief introduction to each pattern as we encounter it. If you’re in unfamiliar territory, we encourage you to follow any references we give to get a deeper understanding of the topic.

If you haven’t studied patterns before, here’s the general idea. Many problems, although different in their formulation, are solved in similar ways. Thus it’s possible to formulate a general solution that applies to many similar problems. In software, many professionals have documented the most useful patterns they’ve observed in the field, allowing them to communicate and share their knowledge with others. Hundreds of software-design patterns have been documented over the years, and throughout this section we give references to books and articles we consider important.

Let’s quickly look at one popular and famous pattern: the Singleton pattern. You may have heard of it. The Singleton pattern ensures that a class has only one instance in an application and provides a global point of access to that instance. If you don’t want people creating more than one instance of your Shopping Basket class, you can use the Singleton pattern to prevent them from doing so. This pattern was well documented in the book Design Patterns: Elements of Reusable Object-Oriented Software, published more than 10 years ago [Gamma et al 1995]. Because the Singleton pattern has proven so useful, it continues to be used daily in many open source and commercial .NET projects, including the .NET framework itself.

Learning patterns offers many benefits. They’re well tested, time-proven methods that let you leverage the experience of other professionals. They give you a common vocabulary for efficiently communicating aspects of your software with others. You can learn more about patterns in the books Design Patterns and Patterns of Enterprise Application Architecture [Fowler 2003] and Head First Design Patterns [Eric Freeman et al 2004]. In addition, you may want to look up the many books and papers related to the Pattern Language of Programs (PLoP) conferences (http://hillside.net/plop/2008/).

Domain-Driven Development

The practice of DDD is complementary to NHibernate development. DDD is an increasingly popular approach to building software; the development process is heavily focused on the domain model. Establishing a good domain model is a fine art, and DDD asks that developers and business folk find common terminology to use in both conversation and code. In a DDD solution, the domain model should fully express the problem in terms that both developers and business people understand.

NHibernate applications are well suited to this domain-model-centric approach. We encourage you to learn more about DDD: see Domain-Driven Design [Evans 2004] and Applying Domain-Driven Design and Patterns [Nilsson 2006].

Test-Driven Development

TDD is another practice that has gained much popularity over the last decade. Often, developers think that TDD must be about testing code that has already been written, but that’s not entirely true. TDD is a practice that encourages developers to write tests before—or at least at the same time as—they write their domain logic and other code. This means thinking of a solution to a problem, writing the tests for this solution, and then implementing the solution to make the tests pass. If you’ve not tried TDD before, it may be hard for you to instantly understand the magic of this approach. We strongly recommend that you try it; once you’re used to the process, the benefits become clear.

Unit testing is a key part of TDD and is different from classic application testing. Traditionally, testing involved having a test team exhaustively test applications by navigating screens and prodding buttons. Unit testing is different because it’s repeatable and automated, and it doesn’t involve user interaction. Thus unit testing can be done frequently.

When you use unit testing, you write tests to test a single unit of code at a time, such as a class or function. Each test is usually small and only needs to interact with the unit of code, rather than an entire application. This means you can run thousands of tests by clicking the “run tests” button and sipping your coffee while the computer does the hard work.

The .NET framework has some popular testing libraries to support TDD and unit testing. The most popular is NUnit (http://nunit.org/). Later in this chapter, we briefly demonstrate how you can unit test a domain model and the business layer using NUnit.

Like DDD, TDD is an agile software development practice (http://en.wikipedia.org/wiki/agile_software_development). For more details about NUnit and TDD, read Test-Driven Development in Microsoft .NET [Newkirk et al 2004]. You can also look at tools like ReSharper (http://www.jetbrains.com/resharper/) which is a Visual Studio plug-in that can help greatly with both TDD and DDD.

With all these methodologies and patterns in mind, let’s see how you can use them to develop the layers of an application.

8.1.2. Building and testing the layers

In the following sections we demonstrate how to combine these good practices to build an NHibernate application. A clear separation of concerns allows you to develop the layers of an application almost independently. Some practitioners like to start with the GUI layer, others like to start with the domain model layer, and some developers build all the layers simultaneously. For the purposes of simplicity, we start with the domain model and work our way up to the presentation layer. For each layer, we look at two activities: writing the core classes and writing the unit tests.

8.1.3. The domain model

The domain model is commonly considered part of the business layer. But we define a slight separation: the domain model is the heart of the business. It indicates what the business is doing (the domain) and represents the entities manipulated in the domain by the business (the model).

It’s conceptually independent of any other layer, even the business layer, because it doesn’t use any class that isn’t itself part of the domain model. Without realizing it, you’re familiar with the domain model because it primarily includes entities, which you’ve been implementing since chapter 2!

You can see a more complex example of a domain model in the CaveatEmptor application illustrated in chapter 4, figure 4.2. Its implementation requires nine classes for the entities and even more for the other components.

Implementation

In the context of enterprise application development, implementing a domain model may not be as simple as it looks. This section enumerates the steps of this process along with some of the problems you may encounter; we cover this topic more thoroughly in chapter 9.

There are two ways to build your domain model entities. You can either write them from scratch, or generate them from the mapping files, a database schema, or some other intermediate format. Either way, building the entities is usually easy enough; it’s a matter of creating a set of classes with appropriate constructors and properties.

Implementing the entities’ behavior and rules (the business logic) can be less straightforward. You must be careful when choosing how and where to implement business rules to prevent things from getting too complicated.

A domain model doesn’t exist in isolation, so the other layers may have an influence on how it is designed. For example, they may require the domain model to not be completely persistence ignorant, or that entities expose properties to assist in GUI data binding.

Finally, you may have to communicate with other components/services/applications that aren’t able to manipulate your domain model directly. In .NET applications, it’s common to have a specific format for sending information to and from other applications. This might be XML, json, DataSets or even Data Transfer Objects (DTOs). In all these cases, you must find a way of converting data between these formats, which isn’t always easy.

All these problems require consideration and experimentation. Chapter 9 provides a wide range of options to help you solve them.

Testing

If your previous applications didn’t include tests for your domain model, you should reconsider your position. Remember, your domain model is the heart of your software; but unlike the presentation layer, a broken domain model may not be easily visible.

Note that when you’re unit testing your domain model, the units are the entities.

Globally, you can use two kinds of tests: data-integrity tests and logic tests. Let’s take a simple example of each and see how you can use NUnit. To make it even more fun, we apply TDD.

Data-integrity tests are simple tests to make sure the entities’ content is valid (according to the business logic) and remains valid, no matter what happens. For example, suppose you have a User entity in your domain model, and its name is mandatory; and because it’s stored in the database, this name must not exceed a certain number of characters (let’s say 64).

Listing 8.1 shows three tests to codify this specification.

Listing 8.1. Unit testing an entity

Tests are grouped in public classes called fixtures. NUnit uses attributes to identify them; this is why this class is decorated with the attribute [TestFixture]. Tests are methods marked using [Test]. Note that these methods must be public and must take no parameters.

The first test makes sure there is a property called Name in the class User and that it keeps the value you assign to it. You use a random value to prove that the name is stored correctly. The actual test is done using Assert.AreEqual(...); the Assert class belongs to NUnit and provides a wide range of methods for testing.

The two other tests use a different approach. They try to set invalid values to the property Name and expect it to throw a BusinessException. If it doesn’t, the test fails. As explained in section 9.4.2, you may allow invalid values and validate changed entities before saving them.

Remember, it’s good practice to keep test fixture classes in a separate test library.

Now, we can move on to the implementation of the part of the User class that makes these three tests pass. First, you create the User class and its Name property; then, you use a _name field to keep the value assigned to this property. At this point, the first test passes.

After that, you add a validation to make the two other tests pass. Here is the final result:

public class User {
private string _name;
public string Name {
get { return _name; }
set {
if( string.IsNullOrEmpty(value) || value.Length>64 )
throw new BusinessException("Invalid name");
_name = value;
}
}
}

This code is easy to understand: in the setter of the Name property you throw en exception if the name that is about to be set is invalid (null, empty, or too long); you keep this value in the _name field.

The second kind of test for an entity is the logic test, which tests any behavior in the entity. For example, when changing her password, the user must provide the old password, and then enter the new password twice.

Here are some tests for this method (they assume that a newly created user has a blank password and that there is no encryption):

[Test]
public void WorkingPassword() {
string random = new Random().Next().ToString();
User u = new User();
u.ChangePassword("", random, random);
Assert.AreEqual(random, u.Password);
}
[Test, ExpectedException( typeof(BusinessException) )]
public void BadOldPassword() {
User u = new User();
u.ChangePassword("?", "", "");
}
[Test, ExpectedException( typeof(BusinessException) )]
public void DifferentNewPasswords() {
User u = new User();
u.ChangePassword("", "x", "y");
}

There is little to explain. The first test makes sure you can successfully change the password, and the other two make sure you can’t change the password with an invalid old password or a new password that’s different than the confirmation password.

Here is an implementation of the ChangePassword() method that makes these tests pass:

public void ChangePassword(string oldPwd, string newPwd,
string confirmPwd) {
if( (_password != oldPwd) || (newPwd != confirmPwd) )
throw new BusinessException("...");
_password = newPwd;
}

Note that you should probably separate the two validations to provide meaningful messages in the thrown exception. And, in the real world, you should move these validations to the business layer, as you’ll do in the next section.

Remember that a domain model can (and should) be efficiently tested. Its autonomy makes it easy not only to implement, but also to test.

Let’s continue with the other classes that form the business aspect of the application.

8.1.4. The business layer

The business layer acts as a gateway that upper layers (such as the presentation layer) must use to manipulate entities. It’s common to see .NET applications (mainly those using DataSet) directly access the database. You already know that doing so is wrong, and why (if you’ve forgotten, see section 1.2).

The business layer plays several roles: It’s a layer on top of the persistence layer. It performs high-level business logic (that can’t be performed by the entities themselves). It may also integrate services like security and audit logging.

The controller (from the model-view-controller [MVC] pattern) can also be considered part of this layer. It pilots the flow of information between the end user (through the view) and the model. But it’s a good practice to keep the controllers as a thin layer on top of the core of the business layer.

Implementation

Depending on the coupling between the entities, you can write one business class to manage each entity or one for many entities. You may also have business classes for some use cases or scenarios.

When you’re implementing CRUD-like operations, try not to mimic the persistence layer. At this level, save and update aren’t business words; but it’s easy to find the right words when you consider the problem from a business perspective. For example, when you place a bid on an item, although you’re saving this bid in the database (technically speaking), you shouldn’t call the method performing this operation SaveBid(); in this case, PlaceBid() is more expressive. Obviously, PlaceBid() (in the business layer) will send the bid for persistence using a method from the persistence layer that can be called Save() (or MakePersistent() as explained in section 10.1).

Let’s change the previous example to illustrate a piece of the business layer. What if you want to allow administrators to change users’ passwords (without knowing their current ones)?

In this case, the class User can’t perform this logic because it doesn’t know which user is currently logged in, unless you make this information available using, for example, the Singleton pattern (but this may not be a good idea).

Here is how you can implement the ChangePassword() method (this method belongs to a class in the business layer):

public void ChangePassword( User u,
string oldPwd, string newPwd, string confirmPwd ) {
if( u != null )
throw new ArgumentNullException("u");
if( LoggedUser == null )
throw new BusinessException("Must be logged");
if( ( ! LoggedUser.IsAdministrator && u.Password != oldPwd )
|| ( newPwd != confirmPwd ) )
throw new BusinessException("...");
u.Password = newPwd;
UserPersister.Save(u); // Persistence layer
}

This method includes many validations, but note that you don’t validate the new password; this is up to the User.Password property. You can make the setter of this property internal (provided the domain model and the business layer are in the same library) to make sure the presentation layer can’t change the password directly.

Note that instead of receiving an instance of the User class, this method can receive the user’s identifier and load the user internally. One advantage is that the business layer won’t have to check if the user instance has been modified. Another advantage is that the presentation layer can more easily provide an identifier rather than a complete instance. This is especially the case with ASP.NET applications, where you’re more likely to have an identifier rather than the entire user instance. Note that it’s considered less object-oriented to pass identifiers rather than instances, but this is a worthwhile trade-off in some cases.

Unless you’re writing a heavily customizable search engine UI, you should provide methods for all the kinds of queries that the end user can run. Don’t let the presentation layer build arbitrary queries; letting it do so makes the presentation layer aware of the persistence layer, which may become a security issue, and it also makes the application less testable. If you have many queries to run, you might consider a more extensible approach that avoids having to create too many query methods.

You may also include some code for audit logging to keep track of what happened and who did it (invaluable when you’re debugging an application in production). But in section 8.4, you’ll learn another way to deal with this kind of service.

We discuss implementing the business logic in section 9.4. We can’t discuss the general implementation because it depends heavily on the application. Just make sure you cleanly separate the business logic from the other layers. You may, for example, put the business classes in a Business namespace and the controllers in a Controllers namespace. We also can’t provide much detail about the implementation of controllers, because they tend to be platform-dependent.

Testing

As you may guess, testing the business layer is crucial. It’s similar to testing the domain model. If you understand unit testing as illustrated in listing 8.1, you should be able to easily test the previous method, ChangePassword().

But because this layer uses the persistence layer, it may become troublesome. Some complex business logic may require a specific persistence strategy. These borderline scenarios require a compromise between the separation of concerns and the ease of implementation and testing.

Tests for the business layer should only be related to the business layer itself. We look at testing the persistence layer in the next section to see what must not be tested here—even tests for the domain model’s entities should be separated.

8.1.5. The persistence layer

The persistence layer provides CRUD methods for entities. Thanks to NHibernate, it can be implemented as a service (that is, non-intrusively) for the domain model.

But some libraries merge the persistence layer into the domain model. We generally see that as a bad practice, but the reason behind this design choice is simplicity: when you’re writing a new application that isn’t complex (in terms of layer coupling and integration issues), having a persistence-ignorant domain model isn’t necessarily a requirement.

Regardless, we recommend that you build the persistence layer separately and that you hide it behind the business layer. But we agree that it depends on your programming style.

Implementation

The general practice, when implementing the persistence layer, is to write one persistence class per entity, commonly called EntityNameDAO. (DAO stands for Data Access Object.) It’s a well-known pattern to implement persistence layers. In the previous code snippet, UserPersister can be called UserDAO.

Persistence classes have methods for simple CRUD operations and can let the business layer execute custom queries. We explain this approach in section 10.1.2.

The presentation layer (and other upper layers) shouldn’t be able to access this persistence layer unless the application is simple, in which case the business layer isn’t required (but beware of simple applications evolving and becoming complex). If this layer is in the same library as the business layer, its classes can be made internal. Otherwise, avoid adding a reference in the presentation library to the persistence library.

Also note that the persistence sits between the business layer and the database, so the layer should hide any database semantics. This leads to a more maintainable business layer that isn’t concerned with low-level database and persistence issues.

Testing

As with the domain model, you can test the persistence layer two ways. You can test the correctness of the mapping between the domain model and the database, and you can also test the persistence logic.

Testing the mapping means making sure the NHibernate mapping is correctly written and those entities are correctly loaded and saved. It involves saving an entity with random content, loading it, and making sure the content hasn’t changed.

When you test the persistence logic, the logic is represented by any code customizing the way NHibernate works, such as the queries (the where clause, the ordering, and so on). The idea is to make sure you get the data as intended.

But you should avoid testing NHibernate itself. There are specific NHibernate tests for that. Let’s take this test as an example:

session1.Save( entity );
Assert.IsNotNull( session2.Get<Entity>(id) );

This test is useless because if Save() succeeds, then the entity was saved. You’ll have a hard time if you don’t trust NHibernate and all the libraries to do their jobs. On the other hand, you can make sure the proper lazy-loading and cascading options are enabled.

Notice that you use two different sessions (session1 and session2) in this example because session1.Get<Entity>(id) doesn’t hit the database; it uses its (first-level) cache instead. If creating two sessions in a test is too costly for you, you can either use session1.Clear() or provide your own database connection by calling sessionFactory.OpenSession(yourDbConnection). For more details about the caches, see section 6.3.

Persistence tests are slower than other tests because of the cost of using a database. You can speed them up using in-memory-capable RDBMSs like SQLite. It’s also possible to mock the database; but the tests may become less meaningful. (Mocking is a technique that lets you fake a component in order to avoid its dependencies. For details, see http://en.wikipedia.org/wiki/Mock_object.)

Because the persistence layer is hidden behind the business layer, you can test it through the business layer. Although doing so clutters the business layer tests with unrelated tests, it may be acceptable for simple cases.

8.1.6. The presentation layer

The presentation layer is basically the user interface. In ASP.NET it’s made up of the ASPX, ASCX, and various code-behind files. It serves as a bridge between the end user and the business layer. Its primary function is to format and display information (the entities); it also receives commands and information from the user to send them to the business layer (or the Controller).

Implementation

Implementing the presentation layer is largely outside the scope of this book, but using NHibernate may have some effect on it. Section 8.2 discusses deployment issues of .NET applications that use NHibernate.

From the domain model point of view, the biggest issue is displaying and retrieving entities. .NET provides some powerful data-binding mechanisms that may be hard to leverage with a domain model. In section 9.5, we show you many alternatives to data-bind entities.

To see a typical command-handling method, let’s implement a method that can be called when the user clicks the button to change a password:

private void btnChangePassword_Click(object sender, EventArgs e) {
try {
Business.ChangePassword( editedUser,
editOldPwd.Text, editNewPwd.Text, editConfirmPwd.Text );
MessageBox.Show("Success!");
// Or go to the success page
}
catch(Exception ex) {
MessageBox.Show("Failed: " + ex.Message);
// Or go to the failed page
}
}

This method sends the information to the business layer and then displays a message. The method plays the role of the Controller; strictly speaking, it should call the Controller, which will then do exactly what is in this method. This is an example of a situation where the code-behind is used to implement the Controller logic; the Controller isn’t part of the business layer but is merged in the presentation layer.

We use the generic name Business for the business class because its name can vary widely depending on the way you organize your business layer.

Note that you should always output errors to a log file. Doing so may save you a lot of work when you’re trying to figure out what happened in an application in production.

Testing

The presentation layer is the most difficult to test automatically, because it’s inherently visual. The most common way to test it is to run the application and see if it works.

On the other hand, if you write your application as we’ve described in this chapter, the persistence layer should consist of the design code (HTML for web applications) and a thin code-behind for formatting and data binding. This code can be easily tested visually: it isn’t complex, and it’s harder to break.

A number of techniques and libraries are available to test the presentation layer, but we don’t cover them here. For more information, start by reading http://en.wikipedia.org/wiki/List_of_GUI_testing_tools.

When you’re implementing these layers, you may encounter issues when you try to make your NHibernate application work with some .NET features. Let’s see how you can solve them.

8.2. Solving issues related to .NET features

Applications using NHibernate tend to use some .NET features that can be troublesome because of constraints in the way they work or because of security restrictions. In this section, we talk about two specific features: working in a web environment and using .NET remoting.

8.2.1. Working with web applications

Web applications are much more restricted than Windows applications. They have a specific structure, and they must follow many rules. This section aims to give you guidelines for how to develop an application with a web interface.

Quick Start

You shouldn’t have major issues starting an application using web pages as the presentation layer. To achieve a good separation of concerns, the other layers must not be in the web application or web site; practically speaking, the code-behind of the pages and App_Code section must contain only the presentation layer logic (formatting, displaying, retrieving information, and calling the other layers). The other layers can reside in one or more separate libraries. This is also true for Windows Forms applications, where GUI code is separated from other business and persistence code.

You may encounter two common issues when implementing the presentation layer of an NHibernate application: data-binding the entities (and their collections) and managing the session to efficiently persist these entities. We discuss data binding in section 9.5 and session management in chapter 10.

Code Access Security

The .NET framework has a powerful security policy. It allows, for example, web administrators to limit the permissions given to assemblies based on their provenance. This is required because web applications are generally accessible by a lot of uncontrollable persons; hence they must be carefully configured to avoid security issues.

Web servers are often configured for medium trust. For this reason, if you intend to deploy your application in a public hosting service, you may have some issues getting it to work. The medium-trust policy enforces some restrictions that affect NHibernate applications: you can’t use reflection to access private members of a class, and you can’t use proxies because they can’t be generated due to tightened security.

You must map your database to public fields/properties, you must turn off lazy loading by setting Lazy=false in the mapping of each class, and you must turn off the reflection optimizer. Read sections 3.4.3 and 3.4.4 for more details.

If your NHibernate configuration properties are in Web.config, you must add an extra attribute to the section declaration:

<section name="nhibernate" type="..." requirePermission="false" />

Setting the requirePermission attribute to false lets NHibernate read this section when loading the configuration.

8.2.2. .NET remoting

Many NHibernate applications use .NET remoting to make the business (or persistence) layer accessible across a network. This is mainly the case for Windows applications. In this scenario, most layers can be executed on the client’s computer—except the persistence layer, which is executed on the server. That way, database access can be restricted so that only the server knows how to access the database; the client communicates with the server using, for example, a layer of marshal-by-reference objects.

The domain model must be serializable. Because NHibernate proxies aren’t serializable, you must also return entities with fully initialized associations. In some edge cases, you may consider using Data Transfer Objects (DTOs). For more details, see section 10.3.1.

Once the application is finished, you may ask yourself whether you achieved your initial goals. And now, the maintenance starts (fixing bugs, improving performance, and so on). Let’s see how we can help you with these boring and stressful tasks.

8.3. Achieving goals and solving problems

Now that we’ve looked at the development process of an NHibernate application, let’s take a step back and think again about the design of this application. Even with a good implementation and set of tests, a poorly designed application would be useless. This is why you should have some design goals and ways to measure how much they’re achieved.

A good understanding of the development process is required to fully take advantage of this section. But don’t make the mistake of delaying this task when developing an application. You should realize that the costs to change the design of an application increase rapidly as the application is implemented. And in the middle of development, don’t hesitate to take a break and look back at your initial goals and how far you are from them.

Assessing an application’s status isn’t always easy. Developers can also have a hard time understanding what is wrong with their application. Having some problem-solving skills is definitively a plus when you’re dealing with complex frameworks like NHibernate.

Finally, remember that a single tool can’t do every job. NHibernate is a good framework to solve the ORM mismatch; but it’s also complex. We advise you to carefully test NHibernate and your competence before you begin to use it in a mission-critical environment. And learn to choose wisely: use NHibernate when it’s the best option, and fall back to other alternatives for other situations.

By the end of this section, you’ll have a better understanding of how to deal with these tasks. Let’s start with the first one in the development process: achieving your design goals.

8.3.1. Design goals applied to an NHibernate application

An NHibernate application is a .NET application using NHibernate. But because of the central role played by NHibernate, you must take into consideration some implications when you’re designing an NHibernate application.

The MSDN defines six design goals: availability, reliability, manageability, securability, performance, and scalability. We look at each of them with NHibernate in mind.

Availability and Reliability

Availability and reliability relate to an application’s ability to be present and ready for use. Basically, you should aim to make your application bug-free.

NHibernate has an impact on the way your application is tested. Because it isn’t intrusive, the business logic in your domain model and business layer can be fully tested outside NHibernate’s scope. Note that you must still test the way you use NHibernate; see the section on performance and scalability.

Extensive testing is the first recommendation to achieve the goal of availability and reliability. You should test the internals of your application and its interactions with the outside world. If your application interacts with external services, verify that a failure in one of these services won’t cause your application to crash.

Finally, if your application provides services to external systems, verify that they can’t crash your application by sending invalid information or using your services in a specific way. The end user can be considered part of these external systems. The most common technique is to carefully validate all inputs you receive from these systems.

Manageability and Securability

Manageability and securability describe the application’s ability to be administered and to protect its resources and its users. The purpose of manageability is to ease the (re)configuration and the maintenance of the application.

NHibernate encourages the separation of concerns in your application (layered architecture), which increases its manageability. NHibernate is also configurable using XML files, thus allowing production-time changes.

Security wasn’t a major concern a few years ago, but it’s gaining considerably more attention as systems become more open to the outside world. You should keep your connection string in a safe place (not plain text and not in an assembly); for example, you can keep it encrypted in Web.config. Our general advice is that you implement your application with security in mind and encourage the use of minimal privileges by default.

Performance and Scalability

Performance is the measure of an application’s operation under load. Developers tend to consider this goal the most important. It’s also the most misunderstood (for example, it’s commonly confused with scalability).

NHibernate is a layer on top of ADO.NET; do some tests to make sure your application performs well, identify bottlenecks, and make sure NHibernate is efficiently used (lazy/eager fetching, caching, and so on). As a last resort, NHibernate lets you fall back to classic ADO.NET (the underlying connection is accessible through the ISession. Connection property); we aren’t against stored procedures for batch processing. Note that NHibernate 1.2.0 is faster than hand-coded data access for classic operations on SQL Server because it uses an unexposed batching feature of the .NET framework.

The next section gives you some tips that can help improve your application’s performance. We think performance should be considered throughout a project, rather than leaving it until the end. Of course, we don’t recommend you spend time prematurely optimizing your systems, but it always helps to at least have an understanding of the performance implications of the code you’re writing.

Scalability refers to an application’s ability to match increasing demand with a proportional increase in resources and cost. You can use many techniques to achieve this goal, including asynchronous programming (wrapping expensive calls using asynchronous delegates). You should also use the ADO.NET connection pool (and maybe increase its size); in this case, avoid using a connection string per user.

Another best practice is to open an NHibernate session as late as possible and to close it as soon as possible. You may also consider using a distributed cache (see section 6.3).

Final Note

You may certainly aim at fulfilling all these design goals; but a design choice that has a positive effect on one goal may at the same time have a negative effect on another.

Remember that it’s important to have strict rules to make sure these goals are kept in mind throughout the development process. It’s also important to balance the amount of effort you put into optimization with the outcome of the work.

Don’t work on a feature more than it’s worth, and don’t optimize blindly—that is, without any way to know whether the optimization is needed and to measure the improvement that results from it.

8.3.2. Identifying and solving problems

Nothing is more frustrating than getting an error and not understanding where it comes from. But a strict debugging process helps easily fix most errors. Errors in .NET applications are generally thrown exceptions.

Users may also complain when they’re using a bug-free application, because the application is too slow. This problem can be frustrating for users, and you can be certain they’ll pester you about it.

Bug-Solving Process

Before you think about fixing a bug, make sure you have an infrastructure to catch and log it and that the user receives a useful message. This is important in a production environment.

The first step to fix a bug revealed through an exception is to read the content of this exception—not only the message, but also the stack trace, inner exceptions, and so on (everything returned by exception.ToString()).

Then try to understand the meaning of this exception (refer to the documentation), and begin investigating its origin. A good technique at this stage is to isolate the problem until its origin and solution become obvious. Practically speaking, this means removing processes until you locate the culprit. If you have a hard time understanding an NHibernate exception, appendix B tells where you can ask for help.

Once you’ve fully identified the problem, TDD recommends that you write tests that reproduce this problem; these tests will help prevent the problem from coming back unnoticed later. You should also take some time to think about the design of your application, to see if the problem is the symptom of a bigger issue.

Finally, you can fix the bug, making the tests you wrote pass.

Improving Performance

Suppose you’ve received a performance complaint. Here are some common mistakes you may have made and tips to help dramatically improve your application’s performance (and make your users happier).

To begin with, you should consider writing performance tests at an early stage, rather than waiting for a user to tell you that your application is too slow. And don’t forget to optimize your database (adding the correct indexes, and so on).

Sometimes, developers new to NHibernate create the session factory more often than is required. Remember, creating the session factory is an expensive process, and for most applications, it needs to be done only once: at application startup. Try to give your session factory a lifespan equal to that of your application (by keeping it in a static variable, for example).

Another common mistake, related to the fact that NHibernate makes it so easy to load entities, is to load more information than you need (without knowing it). For example, associations and collections are fully initialized when lazy loading isn’t enabled. Even when you’re loading a single entity, you may end up fetching an entire object graph. Our general advice is to always enable lazy loading and to write your queries carefully.

A related issue arises when you enable lazy loading: the n+1 select problem. For details, read section 8.6.1. You can spot this issue early by measuring the number of queries executed per page; you can easily achieve that by writing a tool to watch logs from NHibernate.SQL at the DEBUG level. If this level is higher than a certain limit, you have a problem to solve; do it immediately, before you forget what is going on in this page. You can also measure other performance-killer operations (like the number of remote calls per page) and global performance information (such as the time it takes to process each page).

You should also try to load the information you need using the minimum number of queries (but avoid expensive queries like those involving Cartesian products). Note that it’s generally more important to minimize the number of entities loaded (row count) than the number of fields loaded for each entity (column count). Chapter 8 describes many features that can help you write optimized queries.

Now, let’s talk about a less-well-known issue, related to the way NHibernate works. When you load entities, the NHibernate session keeps a number of pieces of information about them (for transparent persistence, dirty checking, and so on). During committing/flushing, the session uses this information to perform the required operations.

In one specific situation, this process can be a performance bottleneck: when you load a lot of entities but update only few of them, this process is slower than it should be. The session checks all the entities to find those that must be updated. You should avoid this waste by evicting unchanged entities or using another session to save changed entities.

As a last resort, consider using the second-level cache (and the query cache) to hit the database less often and reuse previous results. See section 6.3 for more details about the pros and cons of this feature.

Throwing Exceptions

It’s common in other environments (like C++) to write code like this:

if( something goes wrong ) return -1;

But .NET guidelines recommend using exceptions. They’re much more powerful and harder to miss. It’s important to understand this concept because NHibernate relies on exceptions to provide error reports, and your application should do the same. Although you should already be familiar with this concept, we briefly review it and explain how to handle NHibernate exceptions.

Your first step should be to create your own exceptions to provide better information (and thus make you able to better handle them). Use .NET built-in exceptions only when they’re meaningful (for example, ArgumentNullException when reporting a null argument).

This book uses BusinessException when a business rule is broken. You can add more specific exceptions if you need to.

Another good practice is to not let exceptions from external libraries reach the presentation layer (or any facade like WebService) untouched. You should wrap them in your own exceptions.

Here is an example showing the implementation of a simple Save() method in a class of the persistence layer (this class can be called UserDAO):

public void Save( User user ) {
try {
session.SaveOrUpdate( user );
}
catch( HibernateException ex ) {
log.Error( "Error while saving a user.", ex );
throw new PersistenceException( "Error while saving a user.", ex );
}
}

HibernateException is the (base) exception thrown by NHibernate. Obviously, the upper layer must close the session if an exception is thrown. Note that you can move this exception handling to the business layer (to provide a business-friendly message to the user).

If you come across a performance problem when using NHibernate, and you find it difficult to solve, step back and ask yourself whether NHibernate was the right tool for the process.

8.3.3. Use the right tool for the right job

As we explained in chapter 1, ORM and NHibernate are powerful tools. But we take great care not to make NHibernate appear to be a silver bullet. It isn’t a solution that will make all your database problems go away magically.

Writing database applications is one of the more challenging tasks in software development. NHibernate’s job is to reduce the amount of code you have to write for the most common 90 percent of use cases (common CRUD and reporting).

The next 5 percent of use cases are more difficult; queries become complex, transaction semantics are unclear at first, and performance bottlenecks are hidden. You can solve these problems with NHibernate elegantly and keep your application portable, but you’ll also need some experience to get it right.

NHibernate’s learning curve is high at first. In our experience, a developer needs at least two to four weeks to learn the basics. Don’t jump on NHibernate one week before your project deadline—it won’t save you. Be prepared to invest more time than you would need for another web application framework or simple utility.

Finally, use SQL and ADO.NET for the 5 percent of use cases you can’t implement with NHibernate, such as mass data manipulation or complex reporting queries with vendor-specific SQL functions. Many tasks aren’t inherently object-oriented. Forcing ORM can easily cripple the performance of your application.

Once again: Use the right tool for the right job.

There are other places where you must carefully think about the right approach. For example, you can add services using many techniques, each of which has pros and cons. Let’s study the case of audit logging.

8.4. Integrating services: the case of audit logging

So far, we’ve talked about the development of an NHibernate application without taking into account many aspects and features that may be hard to plug into a layered architecture. In this section, we talk about the integration of these services.

In the context of this book, a service is a clearly separated subsystem (set of classes) that is integrated into the main application to add functionality. Note that talking about independent services (for example, using COM) is largely out of the scope of this book.

The most common services are audit logging and security. It’s also common to have business components implemented as services. For example, in a messaging platform, a service may analyze messages to detect misbehaving users or filter strong language.

We call them services because they should be loosely coupled with the business logic (although it isn’t that important for simple applications). An important property of services is that, due to their loose coupling, they can evolve and be configured independently; it’s also easy to disable them without significantly affecting the main application. This is why they’re often part of the nonfunctional requirements.

Audit logging is the process of recording changes made to data (occurring events, in general). An audit log is a database table that contains information about changes made to other data—specifically, about the event that results in the change. For example, you may record information about creation and update events for auction Items. The information that’s recorded usually includes the user, the date and time of the event, what type of event occurred, and the item that was changed.

NHibernate has special facilities for implementing audit logs (and other similar aspects that require a persistence event mechanism). In this section, we use the IInterceptor interface to implement audit logging. But first, we briefly discuss the hard way (doing it manually), so you can grasp the problem’s level of difficulty. Then we show you how IInterceptor makes it much easier.

8.4.1. Doing it the hard way

The hard way (the manual way) requires continuous effort through the development process. In the case of audit logging, it means calling the audit logging service each time it’s needed.


Note

Can’t I use database triggers?—Audit logs are often handled using database triggers, and we think this is an excellent approach. But it’s sometimes better for the application to take responsibility, especially if complex processing is used or if portability between different databases is required.


Practically speaking, you can have an implementation similar to the following:

public void Save( User user ) {
try {
session.SaveOrUpdate( user );
AuditLog.LogEvent( LogType.Update, user );
}
catch { ... }
}

The call to the method LogEvent() of the class AuditLog generates and saves a log about this change. LogType is an enumeration; it’s better than using a string. Note that you may use the Observer pattern to remove the dependency on this service; see section 9.3.2 for details about this pattern.

The main advantage of this approach is that a better message can be generated for each operation, because you know exactly what you’re doing each time you call this service. The disadvantages are that this approach is verbose (it clutters the code) and, more important, can be forgotten or bypassed. This is unacceptable when you’re building a trustworthy audit logging service.

This approach may work much better for other services; so don’t discard it completely.

8.4.2. Doing it the NHibernate way

Let’s see how NHibernate allows you to automate audit logging. The advantages of this approach are the disadvantages of the hard way, and vice versa.

You need to perform several steps to implement this approach:

1.  

Mark the persistent classes for which you want to enable logging.

2.  

Define the information that should be logged: user, date, time, type of modification, and so on.

3.  

Tie it all together with an NHibernate IInterceptor that automatically creates the audit trail for you.

Creating the Marker Attribute

You first create a marker attribute, AuditableAttribute. You use this attribute to mark all persistent classes that should be automatically audited:

[AttributeUsage( AttributeTargets.Class, AllowMultiple=false )]
[Serializable]
public class AuditableAttribute : Attribute {
}

This attribute can be applied once on classes; you can add properties to customize the logging per class (for example, using a localized name instead of the class name). Enabling audit logging for a particular persistent class is now trivial; you add it to the class declaration. Here’s an example, for Item:

[Auditable]
public class Item {
//...
}

Note that using an attribute implies relying on entity.ToString() to obtain logging details. The audit-logging service has no other means to extract them, unless you use a big switch statement to cast the object (which is feasible if this service is aware of the domain model).

Instead of an attribute, you can create an IAuditable interface. That way, the entities can actively participate to the logging process. You can also do both and choose the best one for each entity.

Creating and Mapping the Log Record

Now you create a new persistent class, AuditLogRecord. This class represents the information you want to log in the audit database table:

public class AuditLogRecord {
public long Id;
public string Message;
public long EntityId;
public Type EntityType;
public long UserId;
public DateTime Created;
internal AuditLogRecord() {}
public AuditLogRecord(string message,
long entityId,
Type entityType,
long userId) {
this.Message = message;
this.EntityId = entityId;
this.EntityType = entityType;
this.UserId = userId;
this.Created = DateTime.Now;
}
}

You shouldn’t consider this class part of your domain model. Hence you don’t need to be as cautious about exposing public fields. The AuditLogRecord is part of your persistence layer and possibly shares the same assembly with other persistence-related classes, such as your custom mapping types.

Next, you map this class to the AUDIT_LOG database table:

<hibernate-mapping default-access="field">
<class name="NHibernate.Auction.Persistence.Audit.AuditLogRecord,
NHibernate.Auction.Persistence"
table="AUDIT_LOG"
mutable="false">
<id name="Id" column="AUDIT_LOG_ID">
<generator class="native"/>
</id>
<property name="Message" column="MESSAGE"/>
<property name="EntityId" column="ENTITY_ID"/>
<property name="EntityType" column="ENTITY_CLASS"/>
<property name="UserId" column="USER_ID"/>
<property name="Created" column="CREATED"/>
</class>
</hibernate-mapping>

You mark the class mutable="false" because AuditLogRecords are immutable. NHibernate will no longer update the record, even if you try to.

The audit-logging concern is somewhat orthogonal to the business logic that causes the log-able event. It’s possible to mix logic for audit logging with the business logic; but in many applications it’s preferable for audit logging to be handled in a central piece of code, transparently to the business logic. You wouldn’t manually create a new AuditLogRecord and save it whenever an Item was modified.

NHibernate offers an extension point so you can plug in an audit-log routine or any similar event listener. This extension is known as an NHibernate IInterceptor.

Writing an Iinterceptor

You’d prefer that a LogEvent() method be called automatically when you call Save(). The best way to do this with NHibernate is to implement the IInterceptor interface, as shown in listing 8.2.

Listing 8.2. IInterceptor implementation for audit logging

Instead of directly implementing the IInterceptor interface, you inherit from EmptyInterceptor, which allows you to ignore the methods of this interface that you don’t need.

This particular interceptor has two interesting aspects. First, the session and user-Id are fields this interceptor needs to do its work, so a client using this interceptor must set both properties when enabling the interceptor. The other interesting aspect is the audit-log routine in OnSave() and OnFlushDirty(), where you add new and updated entities to collections. The OnSave() interceptor method is called whenever NHibernate saves an entity; the OnFlushDirty() method is called whenever NHibernate detects a dirty object. The audit logging is done in the PostFlush() method, which NHibernate calls after executing the synchronization SQL.

Note that entity.GetType().GetCustomAttributes() performs badly (compared to using IAuditable), but you can optimize this code by caching all the decorated types.

You use the static call AuditLog.LogEvent() (a class and method we discuss next) to log the event. Note that you can’t log events in OnSave(), because the identifier value of a new entity may not be known at this point. NHibernate is guaranteed to have set all entity identifiers after flushing, so PostFlush() is a good place to perform audit logging.

Also note how you use the session: you pass the ADO.NET connection of a given session to the static call to AuditLog.LogEvent(). There is a good reason for doing this, as we discuss in more detail. Let’s first tie it all together and see how you enable the new interceptor.

Enabling the Interceptor

You need to assign the IInterceptor to an NHibernate ISession when you first open the session:

AuditLogInterceptor interceptor = new AuditLogInterceptor();
using( ISession session =
sessionFactory.OpenSession(interceptor) ) {
interceptor.Session = session;
interceptor.UserId = currentUser.Id;
using( session.BeginTransaction() ) {
session.Save(newItem); // Triggers OnSave() of the interceptor
session.Transaction.Commit(); // Triggers PostFlush()
}
}

You should move the session opening to a helper method to avoid doing this work each time.

Let’s get back to that interesting session-handling code inside the interceptor and find out why you pass the Connection of the current ISession to AuditLog. LogEvent().

Using a Temporary Session

It should be clear why you require an ISession instance inside the AuditLogInterceptor. The interceptor has to create and persist AuditLogRecord objects, so a first attempt for the OnSave() method can have been the following routine:

if ( entity.GetType().GetCustomAttributes(
typeof(AuditableAttribute), false).Length > 0 ) {
try {
object entityId = session.GetIdentifier(entity);
AuditLogRecord logRecord = new AuditLogRecord( ... );
// ... set the log information
session.Save(logRecord);
} catch (HibernateException ex) {
throw new CallbackException(ex);
}
}

You use session.GetIdentifier(entity) to easily get the identifier. This implementation seems straightforward: create a new AuditLogRecord instance and save it, using the current session. But it doesn’t work.

It’s illegal to invoke the original NHibernate ISession from an IInterceptor callback. The session is in a fragile state during interceptor calls. A nice trick that avoids this issue is to open a new ISession for the sole purpose of saving a single AuditLogRecord object. To keep this as fast as possible, you reuse the ADO.NET connection from the original ISession. This temporary session handling is encapsulated in the AuditLog helper class, as shown in listing 8.3.

Listing 8.3. Temporary session pattern

Note that this method never commits or starts any database transactions; all it does is execute additional INSERT statements on an existing ADO.NET connection and inside the current database transaction. Using a temporary ISession for some operations on the same ADO.NET connection and transaction is a handy technique you may also find useful in other scenarios.

The NHibernate way is powerful, simple, and easier to integrate. But there are some kinds of operations that can’t work using it. In the case of audit logging, the NHibernate way only logs operations per entity; you can’t log an operation affecting many entities or unrelated to persistence. The bottom line is that you’ll probably use both approaches.

We encourage you to experiment and try different interceptor patterns. The NHibernate website also has examples that use nested interceptors and log a complete history (including updated property and collection information) for an entity.

8.4.3. Other ways of integrating services

The approaches we’ve covered are common and simple to implement. But more complex applications may require a more loosely coupled approach. In this section, we introduce the Inversion of Control and Dependency Injection patterns. We also give you a hint about how you can use a logging library to merge your logs with NHibernate logs.

Inversion of Control and Dependency Injection

It’s outside the scope of this book to cover these patterns in detail; but if you don’t know about them, a brief introduction will be helpful. These patterns are designed to avoid high coupling between services that address different concerns.

Let’s take an example. In the auction application, ending an auction involves updating the database, sending a notification to the winner, collecting the payment, and dispatching the item. These steps require that you communicate with different services. A high coupling between them may cripple the application’s manageability and flexibility. Configuring and changing these services can become difficult.

The Inversion of Control pattern solves this issue by externalizing the binding between the application and the services. You define interfaces (contracts) to communicate with the services, and you use a configuration file (generally written in XML) to specify the service to use for each interface. That way, you can change the service by editing the configuration file.

In the case of audit logging, you can create an interface called IAuditLog and specify in the configuration file that the class (service) to use is the AuditLog class defined in listing 8.3.

Many libraries provide these features, and each has pros and cons. Two of the most popular are Castle Windsor (http://www.castleproject.org/container/) and Spring. NET (http://www.springframework.net/). There are many more, including Structure Map, NInject and Unity. For more information, see http://en.wikipedia.org/wiki/Dependency_injection.

Integrating NHibernate Logging

You may decide to use a logging library instead of or in addition to saving logs using NHibernate. This kind of logging is generally used for maintenance rather than auditing.

It’s easy to merge your logs with NHibernate logs using log4net. This library provides various destinations for the logs; it’s even possible to send them through email or to save them in a database. But be aware of the performance costs.

If you can’t use log4net, you have another option: wrap the log4net library to redirect method calls, so you can switch from log4net to another solution like the Enterprise Library or the System.Diagnostics API.

Although logging is a nonfunctional requirement that few users care about, logs are invaluable when you’re debugging applications in production. Think twice before deciding you don’t need to implement logging.

8.5. Summary

This chapter focused on application development and the integration issues you may encounter when writing NHibernate applications. We first considered the practical implementation of a layered application. We discussed how the domain model and the business layer should be implemented and tested. We then discussed the persistence layer and ended with the presentation layer.

The next objective of this chapter was to help you integrate NHibernate applications into production environments. We talked about the medium-trust issue you may encounter when developing web applications.

After that, we summarized how NHibernate can help you achieve the standard design goals of a .NET application. NHibernate has an impact on the way you design an application, and careful use of its features can greatly improve the quality of your application. We also gave you a few tips that can help you identify and solve bugs and performance issues.

In the last section of the chapter, we considered integrating services in an NHibernate application. We discussed the pros and cons of the hard way and the NHibernate way. We also talked about a few other alternatives.

We implemented audit logging for persistent entities with an implementation of the NHibernate IInterceptor interface. The custom interceptor uses a temporary ISession trick to track modification events in an audit history table.

You’re now ready to dig into the details of implementing the two layers directly related to NHibernate: the domain-model layer and the persistence layer. These topics are covered in the next two chapters.