首页 > 解决方案 > Injecting a service into AccountController

问题描述

I am using ASP.NET MVC 5, and I am using the default template that MS provides when creating a new project.

I want to add an injected EmailService into AccountController:

[Authorize]
public class AccountController : ControllerBase
{
    private ApplicationSignInManager _signInManager;
    private ApplicationUserManager _userManager;
    private IEmailService _emailService; // <-- added this field to MS template

    public AccountController()
    {
    }

    // I have added emailService to constructor parameter
    public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, IEmailService emailService)
    {
        UserManager = userManager;
        SignInManager = signInManager;
        _emailService = emailService; 
    }

I am using ninject, and this is how I resolve IEmailService:

private static IKernel CreateKernel()
{
    var kernel = new StandardKernel();
    try
    {
        kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

        RegisterServices(kernel);

        return kernel;
    }
    catch
    {
        kernel.Dispose();
        throw;
    }
}
private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<IEmailService>().To<MyEmailService>().InRequestScope();
}

Now, my emailService is not getting injected into AccountController... so I started debugging the code and noticed that the parameter-less constructor is used to initialize the AccountController... but the strange thing is, userManager and signInManager are still accessible!

For example, I click on Forgot password, the parameter-less constructor initializes AccountController, and then the following action method is called (which is part of MVC project template):

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
    if (ModelState.IsValid)
    {
        /* UserManager is successfully injected!! */
        var user = await UserManager.FindByNameAsync(model.Email);

        // some code here to build an email...

        /* _emailService is NULL!! */
        await _emailService.SendEmailAsync(email);
    }  

Question 1: How is it possible userManager and signInManager are injected through the parameter-less constructor.

Question 2: How can I inject my EmailService into AccountController?

标签: asp.net-mvcdependency-injection

解决方案


Question 1: How is it possible userManager and signInManager are injected through the parameter-less constructor.

It is not being injected via parameterless constructor. If you check the controller you will see the UserManager and SignInManager lazy loading via OWIN context if they were not already set in the constructor.

public ApplicationSignInManager SignInManager {
    get {
        return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
    }
    private set {
        _signInManager = value;
    }
}

public ApplicationUserManager UserManager {
    get {
        return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
    }
    private set {
        _userManager = value;
    }
}

That was usually part of the default template.

Question 2: How can I inject my EmailService into AccountController?

Remove the parameter-less constructor and the lazy loading. Then make sure all dependencies are registered in the dependency resolver so that the object graph can be resolved via the IDependencyResolver used by the framework.

var kernel = new StandardKernel();

RegisterServices(kernel);

GlobalConfiguration.Configuration.DependencyResolver = 
    new Ninject.Web.WebApi.NinjectDependencyResolver(kernel); //Web API
System.Web.Mvc.DependencyResolver
    .SetResolver(new Ninject.Web.Mvc.NinjectDependencyResolver(kernel)); // MVC

return kernel;

推荐阅读