Jobs unit of work and NHibernate

I have an ASP.NET MVC application that has NHibernate as the ORM and uses HttpRequest as the session context. I’m running Hangfire as a Windows service and wished to open a session before a job is executed and close it afterwards. How do people usually go about implementing this? Is it possible to have before and after events for job processing (or something like instropection)? I’m using Unity as the IoC container.

Thanks in advance.

I managed to solve my problem by using a custom filter. It’s not quite what I wanted (since I need to manually add the attribute to all the classes), but it will do.

Hi Samir, would you mind showing your custom filter code please? I am dealing with a similar problem but not able to figure out what the fix is.

Hi,

No problem at all.

The filter attribute:

public class SessionDependentFilterAttribute : JobFilterAttribute, IServerFilter, IElectStateFilter
{
    public virtual void OnPerforming(PerformingContext filterContext)
    {
        HangfireServer.OpenSession();
    }

    public virtual void OnPerformed(PerformedContext filterContext)
    {
        HangfireServer.CloseSession();
    }

    public virtual void OnStateElection(ElectStateContext context)
    {
        var failedState = context.CandidateState as FailedState;
        if (failedState != null)
            HangfireServer.CloseAndRollbackSession();
    }
}

The bootstrap class (don’t mind the violation of the SRP):

public class HangfireServer : IDisposable
{
    protected BackgroundJobServer server;

    internal static ISessionFactory SessionFactory
    {
        get { return SessionHelper.BuildSessionFactory(ApplicationType.StandAlone); }
    }

    public virtual void Start(IUnityContainer container)
    {
        Configure(container);
        server = new BackgroundJobServer();
    }

    protected static void Configure(IUnityContainer container)
    {
        var connectionString = ConfigurationManager.ConnectionStrings["HangfireDatabase"].ConnectionString;
        GlobalConfiguration.Configuration.UseSqlServerStorage(connectionString);
        GlobalConfiguration.Configuration.UseActivator(new UnityJobActivator(container));
    }

    public static ISession OpenSession()
    {
        var session = SessionFactory.OpenSession();
        CurrentSessionContext.Bind(session);
        session.BeginTransaction(IsolationLevel.ReadCommitted);

        return session;
    }

    public static void CloseAndRollbackSession()
    {
        CloseSession(rollback: true);
    }

    public static void CloseSession()
    {
        CloseSession(rollback: false);
    }

    private static void CloseSession(bool rollback)
    {
        var session = CurrentSessionContext.Unbind(SessionFactory);

        if (session == null)
            return;

        try
        {
            session.Flush();
            if (session.Transaction == null || !session.Transaction.IsActive)
                return;

            if (rollback)
                session.Transaction.Rollback();
            else
                session.Transaction.Commit();
        }
        finally
        {
            session.Dispose();
        }
    }

    public void Dispose()
    {
        if (server != null) server.Dispose();
        CloseSession();
    }
}

Hope that helps.

Cheers,
Samir

Many thanks Samir, much appreciate!

If you dont want to add the attribute to every method, you can also add it to the global filters

GlobalJobFilters.Filters.Add(new MyAttribute());