Questions About Using Ninject

Questions about using Ninject


Does Ninject ensure my DbContext is cleaned up and disposed in a timely fashion?

As per this answer:

The CLR documentation states that whoever creates a Disposable object is responsible for calling Dispose. In this case the object is created by Ninject. That means you should not call Dispose explicitly.

Ninject disposes every Disposable object that has another scope other than InTransientScope as soon as the scope object to which the created object is tied is collected by GC. That's why every Disposable object should be Bindd with a scope that is not InTransientScope(). E.g. you can use InParentScope() from the NamedScope extension which will Dispose the object as soon as the object it is injected into is garbage collected.



I have created a base class for all my application's controllers to handle any common initialization, etc. The base class accepts an instance of my DbContext argument in the constructor. But this requires me to also add this argument to every controller in my app. Is there any way to not require this?

Simply put, never use a common base class for MVC Controllers. Class inheritance tends to tightly couple your logic and it becomes difficult to maintain over time. It also tends to lead you to create a god object, because creating multiple levels of injected dependencies would mean even more required dependencies for every Controller.

If you have cross-cutting concerns, you should use globally registered filters instead. You can make a separate filter for each piece of logic, which doesn't violate the Single Responsibility Principle as a shared base class would. And if you register your filters globally, you can use constructor injection as in this action filter or this authorization filter. You can also make your own attributes (without behavior) to make them conditional per controller and/or action, if necessary.

Example:

Since you explicitly said you wanted to set common ViewBag properties based on the current user, here is how that can be done with filters.

CurrentUserProfileFilter

public class CurrentUserProfileFilter : IAuthorizationFilter
{
private readonly MyDbContext context;

public CurrentUserAuthorizationFilter(MyDbContext context)
{
this.context = context;
}

public void OnAuthorization(AuthorizationContext filterContext)
{
var currentUserName = filterContext.HttpContext.User.Identity.Name;

// Set the ViewBag for the request.
filterContext.Controller.ViewBag.UserName = currentUserName;

var userBirthdate =
from user as this.context.AspNetUsers
where user.UserName == currentUserName
select birthdate;

if (userBirthdate.Date == DateTime.Now.Date)
{
filterContext.Controller.ViewBag.Message = "Happy Birthday!";
}
}
}

GlobalFilterProvider

MVC has a static GlobalFiltersCollection where you are supposed to register filter instances globally. This isn't going to do for filters that have dependencies that have lifetimes that are managed by the DI container (such as DbContext).

To ensure the filters are resolved on demand (per-request), we make an IFilterProvider that resolves them through the container (assuming your Ninject container is registered with MVC as the DependencyResolver);

public class GlobalFilterProvider : IFilterProvider
{
private readonly IDependencyResolver dependencyResolver;

public GlobalFilterProvider(IDependencyResolver dependencyResolver)
{
this.dependencyResolver = dependencyResolver;
}

public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
foreach (var filter in this.dependencyResolver.GetServices<IActionFilter>())
{
yield return new Filter(filter, FilterScope.Global, order: null);
}
foreach (var filter in this.dependencyResolver.GetServices<IAuthorizationFilter>())
{
yield return new Filter(filter, FilterScope.Global, order: null);
}
foreach (var filter in this.dependencyResolver.GetServices<IExceptionFilter>())
{
yield return new Filter(filter, FilterScope.Global, order: null);
}
foreach (var filter in this.dependencyResolver.GetServices<IResultFilter>())
{
yield return new Filter(filter, FilterScope.Global, order: null);
}
// If MVC 5, add these as well...
//foreach (var filter in this.dependencyResolver.GetServices<System.Web.Mvc.Filters.IAuthenticationFilter>())
//{
// yield return new Filter(filter, FilterScope.Global, order: null);
//}
}
}

Usage

In your Ninject composition root, register the instance of your filter with the kernel for the type or types of filter interfaces it implements.

// Self-bind our filter, so dependencies can be injected.
kernel.Bind<IAuthorizationFilter>().To<CurrentUserProfileFilter>();

In FilterConfig, register your filter provider.

public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());

// Register the filter provider with MVC.
FilterProviders.Providers.Insert(0, new GlobalFilterProvider(DependencyResolver.Current));
}
}

Now on every request, your user details are populated.

But more importantly, your ArticlesController doesn't require MyDbContext as a dependency, nor do the rest of your controllers.

I'm not sure how expensive it is to create an instance of my DbContext. Is there any way to make the optimization that it only gets created if the request actually requires me to access the database.

Have a look at this question: One DbContext per web request... why?

How to Use Ninject


First of all do I need to use the Inject attribute on all constructors
that I want to use injection for. This seems like a really lame
design?

No you shouldn't have to do this at all actually. Since you work with ASP.NET MVC you can just install the Ninject.MVC3 Nuget package. This will get you started with a NinjectMVC3 class in the App_Start folder. You can use the RegisterServices method to register your interfaces/classes with Ninject. All controllers that have dependencies to those interfaces will then be automatically resolved by Ninject, there is no need for the Inject attribute.

Do I need to create a Kernel then use that everywhere I pass in an
injected class?

No - what you are describing sounds more like the Service Locator pattern, not dependency injection - you will want to pass in your dependencies ideally in the constructor, instead of resolving them within particular classes using the kernel. There should be just one central composition root where the resolving is done, which is within the composition root in either the RegisterServices method mentioned above or a separate Ninject module instantiated there - the later approach will allow you a little more flexibility and modularity (no pun intended) in changing how you resolve your dependencies.

Here's a good beginner's tutorial on dependency injection with Ninject and MVC3.

Creating objects based on a string using Ninject

Ninject's IKernel's Get<T>() method has an overload which takes an name argument to get a named instance.

The usage would be:

public int Main()
{
IKernel kernel = new StandardKernel();

kernel.Bind<IFoo>().To<AFoo>().Named("AFoo");
kernel.Bind<IFoo>().To<BFoo>().Named("BFoo");

//returns an AFoo instance
var afoo = kernel.Get<IFoo>("AFoo");

//returns an BFoo instance
var bfoo = kernel.Get<IFoo>("BFoo");
}

Regarding your question about injecting Ninject's IKernel into the Factory's constructor, I don't think there should be any problems.
Your factory should look like this:

public interface IFooFactory
{
IFoo GetFooByName(string name);
}

public class FooFactory : IFooFactory
{
private readonly IKernel _kernel;

public FooFactory(IKernel kernel)
{
_kernel = kernel;
}

public IFoo GetFooByName(string name)
{
return _kernel.Get<IFoo>(name);
}
}

Also you could add a binding to IKernel like this:

kernel.Bind<IKernel>().ToConstant(kernel);

Using Ninject in BaseController in ASP.NET MVC

You have to change your derived types (WishListController, CartController etc...) constructor to pass the required parameter (ICurrencyRepo) to the base controller constructor.

Something like:

public class WishListController : BaseController
{
public WishListController(ICurrencyRepo currencyRepo) : base(currencyRepo)
{
}
}

See MSDN

Injection question when using Ninject 2 in ASP.NET MVC application

In order for Ninject to inject dependencies, you have to create the object using the kernel. That's easy for objects in the natural dependency chain (ie. in your app, Controllers->Services->Repositories), but can be tricky for those outside of it.

You have to either add the additional types as dependencies of one of the types that is created in the natural chain, or somehow get a hook on the kernel and call Get<T>. To do that, you might have to use a static service locator.

Passing Parameters across Classes using Ninject

Ninject 3 provided a convenient function that allows me to pass the parameters across classes. By creating an interface like

public interface IViewPersonViewModelFactory()
{
IViewPersonViewModel CreateViewPersonViewModel([parameterType parameterName..]);
}

and adding the following to the Ninject module to be loaded:

public override void Load()
{
Bind<IMainControllingViewModelFactory>().ToFactory();
... ... //other bindings
}

We can then obtain an instance of the ViewPersonViewModel class (for e.g. in the MainViewModel class)

 public class MainViewModel
{
private IViewPersonViewModel _viewModel;

public MainViewModel(IViewPersonViewModelFactory viewModelFactory)
{
_viewModel = viewModelFactory.CreateViewPersonViewModel(parameters..);
}
}

Note that no concrete factory has to be created.

The wiki can be found at: https://github.com/ninject/ninject.extensions.factory/wiki/Factory-interface

using ninject to inject dependency to The Model classes or non-controller classes

Your problem is, you're expecting magic. You inject an implementation for your repository, and then you expect that data objects created by that repository are injected with references of the creating repository.

First of all, that doesn't work like that. The implementation of the repository will call new() (or Activator.CreateInstance) on your entities, not ask for an instance from a Ninject Kernel or Factory. You could rewrite the repository (it'll get trickier if you're using EF there...) but it's probably not worth the hassle.

On the top of it all, you shouldn't be needing that at all. Entities shouldn't depend on repositories, imho not even their interfaces.

EDIT: now I see why you want to see a repo in your model. What I recommend is a static factory maybe.

public class Factories
{
public static readonly Instance = new Factories();
[Inject]
public Func<IVillageRepository> VillageRepo {get; set;}
}

Then call Kernel.Inject(Factories.Instance); from your Ninject initialization code (where you bind IVillageRepository). Then modify your validatable implementation to Factories.Instance.VillageRepo().Find(...);

Error using Ninject with ASP.NET V4

When you install the Nuget package for Ninject.MVC3, it should create a file NinjectWebCommon.cs inside the App_Start folder of your MVC project.

Use that file to load your modules and configure your bindings. As far as I know, you don't need to use this Global.asax code anymore, so don't subclass the NinjectHttpApplication inside it.

Using Ninject with Membership.Provider

This is how it should be done today with new versions of both MVC and Ninject (version 3):

You have access to the DependencyResolver instance and Ninject sets itself as the current DependencyResolver. That way you don't need hacks to get access to the static Ninject kernel. Please note, my example uses my own IUserService repository for Membership...

IUserService _userService = DependencyResolver.Current.GetService<IUserService>();


Related Topics



Leave a reply



Submit