Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set up IoC when a key class needs Session (or other context-specific variable)

I am trying to figure out how to use IoC in situations where the dependent classes can change based on some variable in the application (in this case, Session state). For example, each of our clients have a different database, so the connection to the database needs to be built on a value stored in their Session (particularly since some users could have multiple databases if they own multiple businesses, and would switch between databases).

Here is a generic example of how we'd currently set up this structure:

public class MyTestController : ControllerBase
{
    Repository _rep;

    public MyTest(Repository rep)
    {
        _rep = rep;
    }

    public MyTest()
    {
        string connString = String.Format("Server={0}; Database={1};"
            , SessionContainer.ServerName, SessionContainer.DatabaseName;
        var dc = new DataContext(connString);
        _rep = new Repository(dc);
    }

    public int SampleFn()
    {
        return _rep.GetCountOfEmployees();
    }
}

public class Repository
{
    DataContext _context;

    public Repository(DataContext context)
    {
        _context = context;
    }
} 

Would we be able to set this up using IoC and eliminate the default c-tors? If so, how? I don't have a problem just using D.I. like this, but I'd like to explore the possibility of a StructureMap or Unity (note: we normally pass in db/server to a factory class that builds the datacontext ... above example is just for brevity).

like image 599
Beep beep Avatar asked Dec 11 '09 19:12

Beep beep


Video Answer


1 Answers

How the Repository instance is created, as well as its lifetime, is of no concern of the Controller.

When you register components in the container, you should specify the lifetime of the component. Depending on your implementation, you may simply choose to set the lifefime of the Repository to follow the session.

In any case you could use a factory to create the repository from the session, but do this from outside the Controller.

You definitely need to get rid of the default constructor.


Off the top of my head I can't remember how to do this in Unity or StructureMap, so here's a Castle Windsor example.

Define an Abstract Factory:

public interface IRepositoryFactory
{
    Repository Create();
}

and an implementation

public class MyRepositoryFactory : IRepositoryFactory
{
    private readonly HttpContextBase httpContext;

    public MyRepositoryFactory(HttpContextBase httpContext)
    {
        if (httpContext == null)
        {
            throw new ArgumentNullException("httpContext");
        }

        this.httpContext = httpContext;
    }

    #region IRepositoryFactory Members

    public Repository Create()
    {
        // return Repository created from this.httpContext
    }

    #endregion
}

Now register all the stuff

container.AddFacility<FactorySupportFacility>();
container.Register(Component.For<IRepositoryFactory>()
    .ImplementedBy<MyRepositoryFactory>()
    .LifeStyle.PerWebRequest);
container.Register(Component.For<Repository>()
    .UsingFactory((IRepositoryFactory f) => f.Create())
    .LifeStyle.PerWebRequest);

Here I've used the PerWebRequest lifestyle, but if you want to optimize you might want to create a custom PerWebSession lifestyle. This is not too hard to do in Castle, but I can't remember how hard it is in other DI Containers.

You will also need to register HttpContextBase, since MyRepositoryFactory depends on it.

like image 194
Mark Seemann Avatar answered Sep 28 '22 05:09

Mark Seemann