English 中文(简体)
Error with AccountController and Ninject 2 and ASP.NET MVC 2 Preview 2
原标题:

I m using Ninject 2 and the Ninject.Web.MVC and using the NinjectHttpApplication

Receiving the following error during the logon process: "A single instance of controller MySite.Controllers.AccountController cannot be used to handle multiple requests. If a custom controller factory is in use, make sure that it creates a new instance of the controller for each request."

My global.asax has this:

 protected override void OnApplicationStarted()
    {
        RegisterRoutes(RouteTable.Routes);


        RegisterAllControllersIn(Assembly.GetExecutingAssembly());
} 
protected override IKernel CreateKernel()
        {
            return new StandardKernel(new MySite.IoCModules.FakeRepositoriesModule(), new MySite.IoCModules.AccountControllerModule());
        }

The AccountControllerModule looks like this:

 public class AccountControllerModule:Module
{
    public override void Load()
    {
        Bind<IFormsAuthentication>().To<FormsAuthenticationService>();
        Bind<IMembershipService>().To<AccountMembershipService>();
        Bind<MembershipProvider>().ToConstant(Membership.Provider);
    }
}

My guess is that it has something to do the lifecycle set during RegisterAllControllersIn...but I m just not sure...any ideas where to go from here?

UPDATE: Just saw it happen to the HomeController too...it s gotta be trying to make a singleton out of it or something right?

最佳回答

The most recent version of Ninject.Web.Mvc is using a transient scope to register the controllers in RegisterAllControllersIn:

public void RegisterAllControllersIn(Assembly assembly, 
                                       Func<Type, string> namingConvention)
{
  foreach (Type type in assembly.GetExportedTypes().Where(IsController))
     _kernel.Bind<IController>()
        .To(type)
        .InTransientScope()
        .Named(namingConvention(type));
}

I looked into the the NinjectControllerFactory class as well. Its CreateController function is pretty basic. It does a TryGet on the kernel for the controller and returns what it gets back -- if it can t find the controller, it delegates to the base class:

public override IController CreateController(RequestContext requestContext, 
                                                    string controllerName)
{
  var controller = Kernel.TryGet<IController>(controllerName.ToLowerInvariant());

  if (controller == null)
    return base.CreateController(requestContext, controllerName);

  var standardController = controller as Controller;

  if (standardController != null)
    standardController.ActionInvoker = new NinjectActionInvoker(Kernel);

  return controller;
}

So, based on the binding setup and based on the factory, it would seem it s not creating objects in Singleton scope. One thing you could do is write a little debug code after you create your kernel and check the bindings yourself to confirm what the scope is. I did a little experiment and added the code to my HttpApplication class show below. Full disclosure, this is using ASP.Net MVC 1.0, so your mileage may vary. If I have the opportunity, I will get the latest MVC 2 preview and try the same experiment.

protected void DumpBindings() {
  var bindings = Kernel.GetBindings(typeof(IController));

  var dummyRequest = new RequestContext(
                           new HttpContextWrapper(HttpContext.Current), 
                           new RouteData());

  foreach (var binding in bindings) {
    var scope = "Custom";
    if (binding.ScopeCallback == StandardScopeCallbacks.Request)
      scope = "Request";
    else if (binding.ScopeCallback == StandardScopeCallbacks.Singleton)
      scope = "Singleton";
    else if (binding.ScopeCallback == StandardScopeCallbacks.Thread)
      scope = "Thread";
    else if (binding.ScopeCallback == StandardScopeCallbacks.Transient)
      scope = "Transient";

    HttpContext.Current.Trace.Write(
      string.Format(
        "Controller: {0} Named: {1} Scope: {2}",
        binding.Service.Name,
        binding.Metadata.Name,
        scope));
    var controllerFactory = ControllerBuilder.Current.GetControllerFactory();

    var controller1 = controllerFactory.CreateController(
                               dummyRequest, binding.Metadata.Name);
    var controller2 = controllerFactory.CreateController(
                               dummyRequest, binding.Metadata.Name);

    HttpContext.Current.Trace.Write(
      string.Format(
        "{0} controller1 == {0} controller2 ? {1}",
        binding.Metadata.Name,
        object.Equals(controller1, controller2)));
  }
}

I called this right after the call to RegisterAllControllersIn in the OnApplicationStarted. It created the following messages in the trace output:

Controller: IController Named: home
Scope: Transient home controller1
== home controller2 ? False Controller: IController Named: account
Scope: Transient account controller1
== account controller2 ? False

So, all this does is confirm that transient scope is being used and that the controller factory is returning a different instance of the same controller when requested. So, the only thing I can think of is that:

  1. Perhaps you are not using the latest builds of Ninject 2 and Ninject.Web.Mvc
  2. The issue is at the MVC level -- i.e. it s reusing the controller created by the factory
问题回答

Sounds like it s using a singleton, all right. See this page for documentation as to how to control activation behaviors using Ninject.

Note that transient (not singleton) is the default behavior for Ninject.





相关问题
WebForms and ASP.NET MVC co-existence

I am trying to make a WebForms project and ASP.NET MVC per this question. One of the things I ve done to make that happen is that I added a namespaces node to the WebForms web.config: <pages ...

Post back complex object from client side

I m using ASP.NET MVC and Entity Framework. I m going to pass a complex entity to the client side and allow the user to modify it, and post it back to the controller. But I don t know how to do that ...

Create an incremental placeholder in NHaml

What I want to reach is a way to add a script and style placeholder in my master. They will include my initial site.css and jquery.js files. Each haml page or partial can then add their own required ...

asp.net mvc automapper parsing

let s say we have something like this public class Person { public string Name {get; set;} public Country Country {get; set;} } public class PersonViewModel { public Person Person {get; ...

structureMap mocks stub help

I have an BLL that does validation on user input then inserts a parent(PorEO) and then inserts children(PorBoxEO). So there are two calls to the same InsertJCDC. One like this=>InsertJCDC(fakePor)...

ASP.NET MVC: How should it work with subversion?

So, I have an asp.net mvc app that is being worked on by multiple developers in differing capacities. This is our first time working on a mvc app and my first time working with .NET. Our app does not ...

System.Web.Mvc.Controller Initialize

i have the following base controller... public class BaseController : Controller { protected override void Initialize(System.Web.Routing.RequestContext requestContext) { if (...

热门标签