Shagglez
Shagglez

Reputation: 1522

Castle.Windsor and data access object with webforms asp.net

I've create a sample project trying out some new patterns, namely Dao and IoC.

My Dao is defined as follows:

public class Dao<T> : IDao<T>
{
    protected NHibernate.ISessionFactory _sessionFactory;

    public Dao(NHibernate.ISessionFactory sessionFactory)
    {
        this._sessionFactory = sessionFactory;
    }

    protected NHibernate.ISession Session
    {
        get { return _sessionFactory.GetCurrentSession(); }
    }

    public T GetById(object id)
    {
        return Session.Get<T>(id);
    }

    ...
}

And I have a corresponding installer:

public class DaoInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Component.For(typeof(Data.IDao<>))
            .ImplementedBy(typeof(Data.Dao<>))
            .ServiceOverrides(ServiceOverride.ForKey("SessionFactory").Eq("FirstSessionFactory"))
            .Named("FirstDao"));
    }
}

Using MVC pattern I can define a controller with a constructor that would accept IDao<MyClass> myClass as an argument and Windsor will do all the magic of instantiating Dao with the correct SessionFactory for me. My question is, how do I achieve the same behaviour in non-MVC environment? So on any particular page, how do I get an instance of myClass?

Upvotes: 0

Views: 832

Answers (2)

Shagglez
Shagglez

Reputation: 1522

Thank you for your answers. Unfortunately because I'm so new to the field (with very limited time on my hands), I am now even more confused than I was before. From what I understood webforms are not meant to be used with IoC and although work-arounds exist, they are fairly complicated. I think I will abandon Windsor until I migrate the project to MVC, for now I have utilized a simple static SessionManager class.

SessionManager is then responsible for instantiating SessionFactoriesContainer during App_Init, defined as:

public static readonly Dictionary<string, ISessionFactory> SessionFactoriesContainer = new Dictionary<string, ISessionFactory>();

and SessionsContainer defined as:

public static Dictionary<string, ISession> SessionsContainer
    {
        get
        {
            Dictionary<string, ISession> sessionContainer = (Dictionary<string, ISession>)HttpContext.Current.Items[SESSION_CONTAINER_KEY] ?? new Dictionary<string, ISession>();
            foreach (FactoryType type in Enum.GetValues(typeof(FactoryType)))
            {
                if (!sessionContainer.ContainsKey(type.ToString()))
                    sessionContainer.Add(type.ToString(), null);
            }
            HttpContext.Current.Items[SESSION_CONTAINER_KEY] = sessionContainer;
            return sessionContainer;
        }
    }

Although SessionsContainer is static, since it's stored in HttpContext my understanding is that each user will have their own container, am I wrong in assuming this?

SessionManager also has a class GetSessionFor defined as:

public static ISession GetSessionFor(FactoryType type)
    {
        ISession session = SessionsContainer[type.ToString()] ?? SessionFactoriesContainer[type.ToString()].OpenSession();
        session.BeginTransaction();
        SessionsContainer[type.ToString()] = session;

        return session;
    }

This method gets called whenever a new repository is required and ISession is then passed to the constructor. At the end of the request every open session will get either committed or transaction will be rolled back in case of errors.

I realize this is a very crude implementation, but I think it should work. If I have time at the end of the project I aim to revisit session management and hopefully implement installers. In the meantime, if any anyone has any more ideas, please feel free to add them.

Upvotes: 0

Carl Raymond
Carl Raymond

Reputation: 4479

Prior to MVC, ASP.Net was not built to use IoC patterns. MVC is IoC aware, and there are extension points where Windsor and other IoC implementations can plug into the MVC framework to take over the task of instantiating fully configured controllers from MVC's default factories. There is no such facility in a web forms site that would instantiate page classes for you.

Using NHibernate in a web forms project involves making use of the HttpRequest's Items collection, and usually an HttpModule which will execute code before and after a request is handled by the Page class. At the start of a request, the module creates an NHibernate session and puts it into the request's Items collection (the one and only place you can put it on ASP.Net). Then code on the page can then get at the session. Page classes would instantiate an IDao implementation as needed, and pass the session into it. At the end of a request, code in the module will flush and close the session. That way all session management is handled transparently to the code in the page handler. (This is what I did before switching to MVC and the Sharp Architecture framework. It was a lot of infrastructure code that was hard to get just right; I'm happy to now use Sharp Architecture instead. It's well thought out and has more eyes looking at it than just mine.)

You can still use an IoC container in a web forms project, but it's not completely transparent. You can explicitly ask your container for an implementation of IDao. You would have to configure the IoC container to provide the ISession implementation to the IDao implementation, and configure it to manage the session lifetime with per-web-request semantics. But it's not quite as clean as it is in MVC, where there is no code in a controller for this.

Upvotes: 2

Related Questions