How to do action injection for MVC 3 using Autofac

2019-04-12 14:42发布

I'm creating an ASP.NET MVC 3 application trying to take advantage of controller action injection as described here.

Controller constructor injection works without any issues, but I can't seem to get action injection to work.

I've set up Autofac in Global.asax like this:

var builder = new ContainerBuilder();

builder.Register<ITestInject>(c => new TestInject { TestString = "hi" });

builder.RegisterType<ExtensibleActionInvoker>().As<IActionInvoker>();
builder.RegisterControllers(typeof(MvcApplication).Assembly).InjectActionInvoker();

var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

And my controller has an action like this:

public ActionResult Test(ITestInject testInject)
{
        return View(testInject);
}

The TestInject/ITestInject class/interface is defined as :

public interface ITestInject
{
    string TestString { get; }
}
public class TestInject : ITestInject
{
    public string TestString { get; set; }
}

When I try to navigate to the the Test action, I see this error:

Server Error in '/' Application.

Cannot create an instance of an interface.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.MissingMethodException: Cannot create an instance of an interface.

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[MissingMethodException: Cannot create an instance of an interface.]
   System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) +0
   System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache) +98
   System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache) +241
   System.Activator.CreateInstance(Type type, Boolean nonPublic) +69
   System.Web.Mvc.DefaultModelBinder.CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) +199
   System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +572
   System.Web.Mvc.DefaultModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +449
   System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) +317
   Autofac.Integration.Mvc.ExtensibleActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) +122
   System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) +117
   System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +343
   System.Web.Mvc.Controller.ExecuteCore() +116
   System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +97
   System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +10 
   System.Web.Mvc.<>c_DisplayClassb.<BeginProcessRequest>b_5() +37
   System.Web.Mvc.Async.<>c_DisplayClass1.<MakeVoidDelegate>b_0() +21
   System.Web.Mvc.Async.<>c_DisplayClass8`1.<BeginSynchronous>b_7(IAsyncResult ) +12
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
   System.Web.Mvc.<>c_DisplayClasse.<EndProcessRequest>b_d() +50
   System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b_0(Action f) +7
   System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +22
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +60
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8841105
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +184

Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.1

Any ideas on what I'm doing wrong here?

I'm using Autofac version 2.4.5.724.

Thanks.

2条回答
2楼-- · 2019-04-12 14:49

The problem is this line

builder.Register<ITestInject>(c => new TestInject { TestString = "hi" });

The first parameter in any Register method is the concrete service you want to register. Following that type comes the "how to instantiate", and then parameters that define the actual interface to expose the service as. You get the exception because Autofac tries to instantiate the implementation, which in the case above is an interface. Obviously, no constructor is found on an interface type.

Now look at this:

builder.Register<TestInject>(c => new TestInject { TestString = "hi" }).As<ITestInject>();

This approach IMO is one of Autofacs strengths compared to other DI frameworks. Where others start out the registration with the interface you want to expose and ends up with the implementation, Autofac starts with the actual implementation and ends up with the interface. This makes for cleaner syntax especially in cases where one concrete implementation can be exposed as several interfaces, all in one registration.

查看更多
何必那么认真
3楼-- · 2019-04-12 14:53

Action injection is turned off by default in the current Autofac MVC3 implementation.

To enable it, pass a parameter to ExtensibleActionInvoker:

builder.RegisterType<ExtensibleActionInvoker>()
    .As<IActionInvoker>()
    .WithParameter("injectActionMethodParameters", true);

This was quite a recent change and some documentation will need updating - sorry about the hassle.

查看更多
登录 后发表回答