Register | Login

Stacking Code

public interface IBlog { string Dump(Stream consciousness); }

Implementing Context and a Base Repository

Monday, 29 November, 2010 @ 9:19 PM < Adam Boddington
Tags: Architecture, Building Neno, NHibernate

This is post #6 in the Building Neno series. Please click here for a description of the Building Neno project and instructions on how to access the source code for this post.

Yesterday I created an IContext interface for my services as a way for them to kick off transactions when working with multiple repository methods. It’s time to see what that will look like in NHibernate. If you want to follow along, the code as of this post is tagged 2010/11/29/implementing-context-and-a-base-repository.

New Assembly

I’ll need to reference NHibernate for this, but I don’t want to tie NHibernate to the rest of the Moja framework. The first thing I need to do is create a new project, StackingCode.Moja.Repositories.NHibernate.

Context

Implementing IContext for NHibernate should be pretty straightforward. I should be able to pass off BeginTransaction to NHibernate's ISession.BeginTransaction. However, repository methods have no idea if they’re being called on their own or as part of a larger unit of work. Therefore they have to assume they’re on their own and wrap what they do in their own transaction. When being called as part of a larger unit of work, there will be another transaction around that, which means nested transactions. It’s not a problem, except NHibernate likes to commit on the first commit it comes across, unlike T-SQL which commits on the outermost. We can make NHibernate behave more like T-SQL with some different transaction wrappers.

public class Context : IContext
{
    protected ISession Session { get; set; }

    #region IContext Members

    public ITransaction BeginTransaction()
    {
        if (Session.Transaction.IsActive)
            return new NestedTransactionWrapper(Session.Transaction);

        return new TransactionWrapper(Session.BeginTransaction());
    }

    // ...
}

TransactionWrapper wires up Commit and Rollback to the NHibernate transaction. If I'm nesting, and an inner transaction rolls back and throws, an outer one is probably going to catch, rollback and throw too. NHibernate doesn't like multiple calls to Rollback, so I need to check for WasRolledBack.

public class TransactionWrapper : ITransaction
{
    public TransactionWrapper(global::NHibernate.ITransaction transaction)
    {
        Transaction = transaction;
    }

    protected global::NHibernate.ITransaction Transaction { get; set; }

    #region ITransaction Members

    public virtual void Commit()
    {
        Transaction.Commit();
    }

    public void Rollback()
    {
        if (Transaction.WasRolledBack)
            return;

        Transaction.Rollback();
    }

    #endregion
}

NestedTransactionWrapper simply forgoes the commits.

public class NestedTransactionWrapper : TransactionWrapper
{
    public NestedTransactionWrapper(global::NHibernate.ITransaction transaction)
        : base(transaction)
    {
    }

    public override void Commit()
    {
        // Do nothing, let the outermost transaction commit.
    }
}

A Base Base Repository

Now that I have NHibernate, I can implement the CRUD methods I put in IRepository before. Before I do however, it's probably a good idea to make sure every repository (NHibernate or not) has access to IContext -- so they can use the custom transaction code if there is any (which there is, at least in NHibernate's case). Back to StackingCode.Moja.Repositories...

public abstract class Repository<TEntity, TEntityId> : IRepository<TEntity, TEntityId> where TEntity : IEntity<TEntityId>
{
    protected IContext Context { get; set; }

    #region IRepository<TEntity,TEntityId> Members

    public abstract TEntity Get(TEntityId id);
    public abstract void Create(TEntity entity);
    public abstract void Update(TEntity entity);
    public abstract void Delete(TEntity entity);

    #endregion
}

A Base Repository

Forward again to StackingCode.Moja.Repositories.NHibernate to implement the CRUD.

public abstract class Repository<TEntity, TEntityId> : Repositories.Repository<TEntity, TEntityId> where TEntity : IEntity<TEntityId>
{
    protected ISession Session { get; set; }

    public override TEntity Get(TEntityId id)
    {
        return Session.Get<TEntity>(id);
    }

    public override void Create(TEntity entity)
    {
        ITransaction transaction = Context.BeginTransaction();

        try
        {
            Session.Save(entity);
            transaction.Commit();
        }
        catch
        {
            transaction.Rollback();

            throw;
        }
    }

    // ... and so on
}

Interceptors

Before I clock off tonight, I want to quickly modify the way OpenSession accepts an interceptor. In NHibernate, I can pass only one interceptor into a new session, which forces me to lump all the things I want to do into one big class. Things like validation, custom concurrency checks (more on that later), and auditing. Wouldn't it be nicer to put those functions into individual interceptor classes and be able to pass in a collection? While I'm at it, I could clean up the interceptor signatures to something a bit more friendly.

I'll make an interceptor adapter to handle the translation from the one interceptor NHibernate expects to the many I'll give it...

public class InterceptorAdapter : EmptyInterceptor
{
    public InterceptorAdapter(params IInterceptor[] interceptors)
    {
        Interceptors = interceptors;

        foreach (IInterceptor interceptor in Interceptors)
            interceptor.InterceptorAdapter = this;
    }

    private IInterceptor[] Interceptors { get; set; }

    public override bool OnSave(object entity, object id, object[] propertyValues, string[] propertyNames, IType[] propertyTypes)
    {
        IEnumerable<EntityProperty> properties = EntityProperty.From(propertyTypes, propertyNames, propertyValues);
        bool result = false;

        foreach (IInterceptor interceptor in Interceptors)
            result |= interceptor.OnCreate(entity, id, properties);

        return result;
    }

    // ... and so on
}

My IInterceptor looks like this...

public interface IInterceptor
{
    InterceptorAdapter InterceptorAdapter { get; set; }
    bool OnCreate(object entity, object id, IEnumerable<EntityProperty> properties);
    bool OnUpdate(object entity, object id, IEnumerable<EntityPropertyForUpdate> properties);
    bool OnDelete(object entity, object id, IEnumerable<EntityProperty> properties);
} 

EntityProperty just takes those arrays of property information that NHibernate is passing around and converts them into something a bit easier to use.

public class EntityProperty
{
    public EntityProperty(IType type, string name, object value)
    {
        Type = type;
        Name = name;
        Value = value;
    }

    public IType Type { get; private set; }
    public string Name { get; private set; }
    public object Value { get; private set; }

    public static IEnumerable<EntityProperty> From(IType[] propertyTypes, string[] propertyNames, object[] propertyValues)
    {
        return propertyTypes.Select((propertyType, index) => new EntityProperty(propertyType, propertyNames[index], propertyValues[index]));
    }
}

Tomorrow I can start to build the Neno repositories.

There are 0 comments.


Comments

Leave a Comment

Please register or login to leave a comment.


Older
Services and Repositories

Newer
Inversion of Control

Older
Services and Repositories

Newer
Inversion of Control

browse with Pivot


About


Projects

Building Neno


RSS
Recent Posts

Codility Nitrogenium Challenge
OS X Lock
HACT '13
Codility Challenges
Priority Queue


Tags

Architecture (13)
ASP.NET (2)
ASP.NET MVC (13)
Brisbane Flood (1)
Building Neno (38)
C# (4)
Challenges (3)
Collections (1)
Communicator (1)
Concurrency Control (2)
Configuration (1)
CSS (5)
DataAnnotations (2)
Database (1)
DotNetOpenAuth (2)
Entity Framework (1)
FluentNHibernate (2)
Inversion of Control (5)
JavaScript (1)
jQuery (4)
Kata (2)
Linq (7)
Markdown (4)
Mercurial (5)
NHibernate (20)
Ninject (2)
OpenID (3)
OS X (1)
Pivot (6)
PowerShell (8)
Prettify (2)
RSS (1)
Spring (3)
SQL Server (5)
T-SQL (2)
Validation (2)
Vim (1)
Visual Studio (2)
Windows Forms (3)
Windows Service (1)


Archives


Powered by Neno, ASP.NET MVC, NHibernate, and small furry mammals. Copyright 2010 - 2011 Adam Boddington.
Version 1.0 Alpha (d9e7e4b68c07), Build Date Sunday, 30 January, 2011 @ 11:37 AM