3

I am using Castle Windsor for my IoC along with NHIbernate in an ASP.NET MVC app. It works great registered as follows (with one exception):

container.Register(Component.For<ISessionFactoryBuilder.().ImplementedBy<SessionFactoryBuilder>().LifestyleSingleton());

// Register the NHibernate session factory as a singleton using custom SessionFactoryBuilder.BuildSessionFactory method.
container.Register(Component.For<ISessionFactory>().UsingFactoryMethod(k => k.Resolve<ISessionFactoryBuilder>().BuildSessionFactory("ApplicationServices")).LifestyleSingleton());

container.Register(Component.For<IInterceptor>().ImplementedBy<ChangeAuditInfoInterceptor>().LifestylePerWebRequest());
container.Register(Component.For<ISession>().UsingFactoryMethod(k => k.Resolve<ISessionFactory>()
            .OpenSession(container.Resolve<IInterceptor>())).LifestylePerWebRequest()); 

All is good except that for my ChangeAuditInterceptor in turn has an IAccountSession service injected which in turn has an NHibernate ISession injected...which leads to the following circular dependency exception:

Dependency cycle has been detected when trying to resolve component 'Late bound NHibernate.ISession'. The resolution tree that resulted in the cycle is the following: Component 'Late bound NHibernate.ISession' resolved as dependency of component 'Blah.Core.Services.AccountSession' resolved as dependency of component 'Blah.Core.Infrastructure.Data.ChangeAuditInfoInterceptor' resolved as dependency of component 'Blah.Core.Infrastructure.Installers.SessionFactoryBuilder' resolved as dependency of component 'Late bound NHibernate.ISessionFactory' resolved as dependency of component 'Late bound NHibernate.ISession' which is the root component being resolved.

For the past couple years I've usually run with an NHibernateSessionManager which took care of plunking in the IInterceptor without causing this circular dependency issue (as opposed to this usage of a SessionFactoryBuilder which uses Castle Windsor's UsingFactoryMethod functionality).

Any suggestions for how to resolve this circular dependency? Short of starting to hack in the ISession for the AccountSession via some other means (i.e. property injection which skirts around the issue and smells as a result). I've switched the ISession injection to property injection for the AccountSession service and it works fine, but I don't like the implicit contract vs. the constructor explicit contract.

public class AccountSession : IAccountSession
{
    private readonly ISession _session;

    public AccountSession(ISession session)
    {
        _session = session;
    }

    public Account GetCurrentAccount() // Called by a method in ChangeAuditInterceptor
    {
...
    }

...etc.

Ted
  • 7,122
  • 9
  • 50
  • 76
  • I guess you should give a try to transforming _session to a public auto-property(get;set;) and delete the constructor for AccountSession (hence leaving place for the compiler auto-generated parameterless public constructor). It's worth trying (IMHO) – jbl Aug 21 '13 at 12:57
  • That's what I did and it works fine. However, I don't like needing to have that implicit contract vs. explicit one, especially when use it elsewhere (although DI all handled by the container still so you don't have to think about it). It smells because it is a cyclical reference that I feel I'm missing something on (although not near as dirty as a typical cyclical reference). – Ted Aug 22 '13 at 02:11
  • Oh, and thanks for your feedback on this and the previous issue. It sounds like you have the same curiosity as I do about what the better/best solution would be. I've tried out the Castle NHibernate Facility before but it requires [Transaction] markup to flush and injecting the factory etc. Maybe the answer is to to go back to an NHibernate session manager which would be injected everywhere instead (and could handle the IInterceptor injection itself). For years I used Ninject and more recently switched to trying out Castle and Autofac. – Ted Aug 22 '13 at 02:21

1 Answers1

5

Try to add a dependency on Func< ISession > in your interceptor class

public class CustomInterceptor : EmptyInterceptor
{
    private readonly Func<ISession> sessionFunc;
    private ISession session;

    protected ISession Session
    {
        get
        {
            return session ?? (session = sessionFunc());
        }
    }

    public CustomInterceptor(Func<ISession> sessionFunc)
    {
        this.sessionFunc = sessionFunc;
    }
}

And registration:

container.Register(Component.For<ISession>().
    LifestylePerWebRequest()
    .UsingFactoryMethod(container =>
    {
        var interceptor = container.Resolve<IInterceptor>();
        return container.Resolve<ISessionFactory>.OpenSession(interceptor);
    }));
container.Register(Component.For<Func<ISession>>()
    .LifestylePerWebRequest()
    .UsingFactoryMethod(container =>
    {
        Func<ISession> func = container.Resolve<ISession>;
        return func;
    }));
Ilya Palkin
  • 14,687
  • 2
  • 23
  • 36
  • thanks for your suggestion! However, while this solves the cyclical reference and works on the first call, the ISession in subsequent calls fails with a null reference (ISession is null). – Ted Aug 24 '13 at 15:19
  • Oops, spoke too soon as I mistakenly referenced _session instead of Session in my Interceptor (and therefore the Func was never evoked so the session was null). Thanks! – Ted Aug 24 '13 at 15:24
  • Also removed my use of the IAccountSession service to get it all working, but I could take it the one step further in order to properly inject the ISession into AccountSession in much the same way. – Ted Aug 24 '13 at 15:25
  • Sure enough, just need resolve the IAccountSession (which ISession is injected into) via the same approach as the Func above. Func that is and now my ChangeAuditInfoInterceptor is as it was, but my AccountSession is now covered and does not lead to a cyclical reference. Thanks! – Ted Aug 24 '13 at 15:46
  • Make that: inject the IAccountSession into ChangeAuditInfoInterceptor just as ISession is injected as shown above. – Ted Aug 24 '13 at 15:55