I\'m using NInject with NInject.Web.Mvc.
To start with, I\'ve created a simple test project in which I want an instance of IPostRepository
to be shared
I eventually managed to solve it with a factory as suggested. However, I just could not figure out how to accomplish this with Ninject.Extensions.Factory
which is what I would've preferred. Here is what I ended up with:
The factory interface:
public interface IPostRepositoryFactory
{
IPostRepository CreatePostRepository();
}
The factory implementation:
public class PostRepositoryFactory : IPostRepositoryFactory
{
private readonly string key = "PostRepository";
public IPostRepository CreatePostRepository()
{
IPostRepository repository;
if (HttpContext.Current.Items[key] == null)
{
repository = new PostRepository();
HttpContext.Current.Items.Add(key, repository);
}
else
{
repository = HttpContext.Current.Items[key] as PostRepository;
}
return repository;
}
}
The Ninject module for the factory:
public class PostRepositoryFactoryModule : NinjectModule
{
public override void Load()
{
this.Bind<IPostRepositoryFactory>().To<PostRepositoryFactory>();
}
}
The custom model binder:
public class CustomModelBinder : DefaultModelBinder
{
private IPostRepositoryFactory factory;
public CustomModelBinder(IPostRepositoryFactory factory)
{
this.factory = factory;
}
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
IPostRepository repository = factory.CreatePostRepository();
repository.Add("Model binder");
return base.BindModel(controllerContext, bindingContext);
}
}
The controller:
public class HomeController : Controller
{
private IPostRepository repository;
public HomeController(IPostRepositoryFactory factory)
{
this.repository = factory.CreatePostRepository();
}
public ActionResult Index(string whatever)
{
repository.Add("Action method");
return View(repository.GetList());
}
}
Global.asax to wire up the custom model binder:
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.Add(typeof(string), kernel.Get<CustomModelBinder>());
}
Which in my view, gave me the desired output of:
Model binder
Action method
It's actually really simple to get the Ninject factory extension up and running, but that wasn't clear to me from the existing answers.
The factory extensions plugin is a prerequisite, which can be installed via NUGet:
Install-Package Ninject.Extensions.Factory
You just need the factory injected into your model binder somewhere, eg:
private IPostRepositoryFactory _factory;
public CustomModelBinder(IPostRepositoryFactory factory) {
_factory = factory;
}
Then create an interface for the factory. The name of the factory and the name of the method doesn't actually matter at all, just the return type. (Good to know if you want to inject an NHibernate session but don't want to have to worry about referencing the correct namespace for ISessionFactory
, also useful to know if GetCurrentRepository
makes what it actually does more clear in context):
public interface IPostRepositoryFactory {
IPostRepository CreatePostRepository();
}
Then, assuming your IPostRepository
is already being managed by Ninject correctly, the extension will do everything else for you just by calling the .ToFactory() method.
kernel.Bind<IPostRepository().To<PostRepository>();
kernel.Bind<IPostRepositoryFactory>().ToFactory();
Then you just call your factory method in the code where you need it:
var repo = _factory.CreatePostRepository();
repo.DoStuff();
(Update: Apparently naming your factory function GetXXX
will actually fail if the service doesn't already exist in the session. So you do actually have to be somewhat careful with what you name the method.)
The ModelBinders are reused by MVC for multiple requests. This means they have a longer lifecycle than request scope and therefore aren't allowed to depend on objects with the shorter request scope life cycle.
Use a Factory instead to create the IPostRepository for every execution of BindModel