Logo

NHibernate

The object-relational mapper for .NET

Nhibernate and WPF: Models concept

Part I: Introducing Nhibernate and WPF
Part II: Chinook Media Manager: The Core

Introduction

For this post I will use the concept of Fabio Maulo called “Conversation-per-BussinesTransaction”. If you have not read, what are you waiting for?

Definition

This definition is extracted from Fabio’s post and is very important that you understood it:

When you read something about NHibernate-session-management is because we want abstract/”aspect” NH-session’s stuff from the code are using it. In general, or I hope so, you are using the session in something like a DAO/Repository and, in a use-case, you are calling more than one DAO/Repository. Open a new session in each DAO/Repository is an anti-pattern called session-per-call (or very closer to it). What we are searching is something to manage the session in an high-layer but without wire that layer with NHibernate. In a WEB environment it is pretty easy because we can manage the session using an implementation of a IHttpModule, or in the HttpApplication, “intercepting” the request (two patterns mentioned here). In a winForm, or WPF, aplication it is not so clear where the abstraction have place. In this post I will use an “artifact” named Model to represent the “abstraction-point” (in my mind the Model is the business-object that are using DAOs/Repository and domain-entities); in an MVC based application, probably, the abstraction have place in the Controller.

Implementation

We start defining an interface for our Use-case Model class as follows:

public interface IAlbumManagerModel
    {

        /// <summary>
        /// Search all albums from a given artist.
        /// </summary>
        /// <param name="artist"></param>
        /// <returns></returns>
        IEnumerable<Album> GetAlbumsByArtist(Artist artist);

        /// <summary>
        /// Persist an album.
        /// </summary>
        /// <param name="album"></param>
        void SaveAlbum(Album album);

        /// <summary>
        /// Revert changes of a given album to his current persisted state.
        /// </summary>
        /// <param name="album"></param>
        void CancelAlbum(Album album);

        /// <summary>
        /// Flush all changes to the database.
        /// </summary>
        void SaveAll();

        /// <summary>
        /// Cancel all changes.
        /// </summary>
        void CancelAll();
    }

As I say in my previous post this interface is defined in the “ChinookMediaManager.Domain”.
The concrete implementation is defined in ChinookMediaManager.DomainImpl as follows:

[PersistenceConversational(MethodsIncludeMode = MethodsIncludeMode.Implicit)]
    public class AlbumManagerModel : IAlbumManagerModel
    {

        private readonly IAlbumRepository albumRepository;

        public AlbumManagerModel(IAlbumRepository albumRepository)
        {
            this.albumRepository = albumRepository;
        }

        /// <summary>
        /// Search all the albums from a given artist.
        /// </summary>
        /// <param name="artist"></param>
        /// <returns></returns>
        public IEnumerable<Album> GetAlbumsByArtist(Artist artist)
        {
            return albumRepository.GetByArtist(artist);
        }

        /// <summary>
        /// Persist an album.
        /// </summary>
        /// <param name="album"></param>
        public void SaveAlbum(Album album)
        {
            albumRepository.MakePersistent(album);
        }

        /// <summary>
        /// Revert changes of a given album to his original state.
        /// </summary>
        /// <param name="album"></param>
        public void CancelAlbum(Album album)
        {
            albumRepository.Refresh(album);
        }

        /// <summary>
        /// Flush all changes to the database.
        /// </summary>
        [PersistenceConversation(ConversationEndMode = EndMode.End)]
        public void SaveAll()
        { }

        /// <summary>
        /// Cancel all changes.
        /// </summary>
        [PersistenceConversation(ConversationEndMode = EndMode.Abort)]
        public void CancelAll()
        {
        }
    }



Well, this is the non-invasive AOP way of doing with CpBT from unhaddins.
The PersistenceCovnersational attribute tell us two things:
- All methods are involved in the conversation if not explicitly excluded.
– All methods ends with “Continue” if not explicitly changed.
SaveAll end the conversation with EndMode.End, this means that the UoW will be flushed.
For the other hand CancelAll end with Abort, this means that all changes are evicted and the conversation is not flushed.

Testing the model

Testing is very simple, all you need to do is to mock your repositories as follows:

[Test]
public void can_cancel_album()
{
    var repository = new Mock<IAlbumRepository>();
    var album = new Album();
    repository.Setup(rep => rep.Refresh(It.IsAny<Album>()))
              .AtMostOnce();

    var albumManagerModel = new AlbumManagerModel(repository.Object);

    albumManagerModel.CancelAlbum(album);

    repository.Verify(rep => rep.Refresh(album));
}

In this test I’m using the Moq library.
The other thing that we need to test is the conversation configuration, my first approach was to test the attributes, but owned by the fact that we can configure the CpBT in many ways (such as XML), this is my test:

[TestFixtureSetUp]
public void FixtureSetUp()
{
    conversationalMetaInfoStore.Add(typeof (AlbumManagerModel));
    metaInfo = conversationalMetaInfoStore.GetMetadataFor(typeof(AlbumManagerModel));
}

[Test]
public void model_represents_conversation()
{
    metaInfo.Should().Not.Be.Null();
    metaInfo.Setting.DefaultEndMode.Should().Be.EqualTo(EndMode.Continue);
    metaInfo.Setting.MethodsIncludeMode.Should().Be.EqualTo(MethodsIncludeMode.Implicit);
}

[Test]
public void cancel_all_abort_the_conversation()
{
    var method = Strong.Instance<AlbumManagerModel>
        .Method(am => am.CancelAll());

    var conversationInfo = metaInfo.GetConversationInfoFor(method);

    conversationInfo.ConversationEndMode
                    .Should().Be.EqualTo(EndMode.Abort);
}

[Test]
public void save_all_end_the_conversation()
{
    var method = Strong.Instance<AlbumManagerModel>
        .Method(am => am.SaveAll());

    var conversationInfo = metaInfo.GetConversationInfoFor(method);

    conversationInfo.ConversationEndMode
                    .Should().Be.EqualTo(EndMode.End);
}

I'm using a strongly typed way to get the MethodInfo extracted from this example.


Posted Sat, 15 August 2009 03:33:46 PM by jfromainello
Filed under: AOP, Session, WPF

comments powered by Disqus
© NHibernate Community 2024