Come fare l'iniezione di azione per MVC 3 usando AutoFac?
-
29-10-2019 - |
Domanda
Sto creando un'applicazione ASP.NET MVC 3 cercando di sfruttare l'iniezione dell'azione del controller come descritto qui.
L'iniezione del costruttore controller funziona senza problemi, ma non riesco a far funzionare l'iniezione di azione.
Ho impostato AutoFac in Global.asax in questo modo:
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));
E il mio controller ha un'azione come questa:
public ActionResult Test(ITestInject testInject)
{
return View(testInject);
}
La classe/interfaccia TestInject/iTestinject è definita come:
public interface ITestInject
{
string TestString { get; }
}
public class TestInject : ITestInject
{
public string TestString { get; set; }
}
Quando provo a navigare all'azione del test, vedo questo errore:
Errore del server nell'applicazione '/'
Non è possibile creare un'istanza di un'interfaccia.
Descrizione: un'eccezione non gestita si è verificata durante l'esecuzione della richiesta Web corrente. Si prega di rivedere la traccia dello stack per ulteriori informazioni sull'errore e dove ha avuto origine nel codice.
Dettagli dell'eccezione: System.MissingMethodexception: non è in grado di creare un'istanza di un'interfaccia.
Errore di origine:
È stata generata un'eccezione non gestita durante l'esecuzione della richiesta Web corrente. Le informazioni relative all'origine e alla posizione dell'eccezione possono essere identificate utilizzando la traccia dello stack di eccezione di seguito.
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.wrapsyncresult`1.end () +62 System.web.mvc.<
> c_DisplayClasse.<
EndProcessRequest>b_D () +50 System.Web.MVC.SecurityUtil.<
GetCallinapptrusThunk> 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) +184Informazioni sulla versione: Microsoft .NET Framework Versione: 4.0.30319; Versione ASP.NET: 4.0.30319.1
Qualche idea su cosa sto facendo di sbagliato qui?
Sto usando la versione AutoFac 2.4.5.724.
Grazie.
Soluzione
L'iniezione di azione viene disattivata per impostazione predefinita nell'attuale implementazione AutoFAC MVC3.
Per abilitarlo, passare un parametro a ExtensibleActionInvoker
:
builder.RegisterType<ExtensibleActionInvoker>()
.As<IActionInvoker>()
.WithParameter("injectActionMethodParameters", true);
Questa è stata una modifica piuttosto recente e alcune documentazioni dovranno aggiornare, scusate per la seccatura.
Altri suggerimenti
Il problema è questa linea
builder.Register<ITestInject>(c => new TestInject { TestString = "hi" });
Il primo parametro in qualsiasi Register
Il metodo è il Servizio concreto Vuoi registrarti. A seguito di quel tipo arriva "come istanziare", quindi i parametri che definiscono l'interfaccia effettiva per esporre il servizio come. Ottieni l'eccezione perché AutoFac cerca di istanziare l'implementazione, che nel caso sopra è un'interfaccia. Ovviamente, nessun costruttore si trova su un tipo di interfaccia.
Ora guarda questo:
builder.Register<TestInject>(c => new TestInject { TestString = "hi" }).As<ITestInject>();
Questo approccio IMO è uno dei punti di forza di AutoFAC rispetto ad altri framework DI. Laddove altri iniziano la registrazione con l'interfaccia che si desidera esporre e finisce con l'implementazione, AutoFAC inizia con l'implementazione effettiva e finisce con l'interfaccia. Ciò rende la sintassi più pulita, specialmente nei casi in cui un'implementazione concreta può essere esposta come diverse interfacce, tutte in una registrazione.