2013-04-09 12 views
30

Sto tentando di eseguire una DI con il costruttore semplice con Injector semplice e sembra che non sia in grado di risolvere le dipendenze per i controller API Web.Iniettore semplice non in grado di iniettare dipendenze nei controller API Web

  • Ho un controller API in una cartella "API", che si trova all'esterno della cartella "Controllers".
  • Ho anche provato a collocarlo nella cartella "Controllers", ma lo non sembra fare molta differenza. La traccia di stack ricevuta da è simile a quella presentata in this question.
  • Sto utilizzando una nuova installazione del pacchetto NuGet di "Simple Injector MVC Integration Quick Start" (v. 2.1.0).
  • Ho la base SimpleInjectorWebApiDependencyResolver dalla documentazione, che è anche la stessa trovata here.
  • Sto utilizzando Entity Framework e ho esaminato lo discussion thread sulle modifiche per caricare correttamente il contesto.

Questo non sembra essere un problema, ma ho ancora visualizzato il seguente errore:

Type 'MyProject.API.ArticleController' does not have a default constructor

System.ArgumentException at

System.Linq.Expressions.Expression.New(Type type) at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType) at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator) at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)

Sarebbe apprezzato se qualcuno potesse offrirmi qualche suggerimento, se qualcosa dovesse essere modificato da il suo stato attuale/ordine di chiamata.

ArticleController (struttura di base):

public class ArticleController : ApiController 
{ 
    private readonly IArticleRepository articleRepository; 
    private readonly IUserRepository userRepository; 
    private readonly IReleaseRepository releaseRepository; 

    public ArticleController(IArticleRepository articleRepository, IUserRepository userRepository, IReleaseRepository releaseRepository) 
    { 
     this.articleRepository = articleRepository; 
     this.userRepository = userRepository; 
     this.releaseRepository = releaseRepository; 
    } 

    // GET api/Article 
    public IEnumerable<Article> GetArticles(){ // code } 

    // GET api/Article/5 
    public Article GetArticle(int id){ // code } 

    // PUT api/Article/5 
    public HttpResponseMessage PutArticle(int id, Article article){ // code } 

    // POST api/Article 
    public HttpResponseMessage PostArticle(ArticleModel article){ // code } 

    // DELETE api/Article/5 
    public HttpResponseMessage DeleteArticle(int id){ // code } 
} 

SimpleInjectorInitializer:

public static class SimpleInjectorInitializer 
{ 
    public static void Initialize() 
    { 
     var container = new Container(); 
     InitializeContainer(container); 
     container.RegisterMvcControllers(Assembly.GetExecutingAssembly()); 
     container.RegisterMvcAttributeFilterProvider(); 
     container.Verify(); 

     DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container)); 
    } 

    private static void InitializeContainer(Container container) 
    { 
     container.Register<IArticleRepository, ArticleRepository>(); 
     container.Register<IUserRepository, UserRepository>(); 
     container.Register<IReleaseRepository, ReleaseRepository>(); 
    } 
} 

Global.asax.cs:

public class WebApiApplication : System.Web.HttpApplication 
{ 
    private void ConfigureApi() 
    { 
     // Create the container as usual. 
     var container = new Container(); 

     // Verify the container configuration 
     // container.Verify(); 

     // Register the dependency resolver. 
     GlobalConfiguration.Configuration.DependencyResolver = 
       new SimpleInjectorWebApiDependencyResolver(container); 
    } 

    protected void Application_Start() 
    { 
     AreaRegistration.RegisterAllAreas(); 
     ConfigureApi(); 

     WebApiConfig.Register(GlobalConfiguration.Configuration); 
     FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 
     RouteConfig.RegisterRoutes(RouteTable.Routes); 
     BundleConfig.RegisterBundles(BundleTable.Bundles); 
    } 
} 
+0

Sei sicuro che il metodo 'ConfigureApi' è in realtà in esecuzione? Hai impostato un punto di rottura in esso? – Steven

+0

Qualsiasi motivo per cui si crea un'istanza 'Container' separata per la configurazione dell'API Web? – Steven

+0

Sì, sono sicuro che è in esecuzione. Ho un punto di interruzione impostato sulla linea che imposta DependencyResolver su GlobalConfiguration. Se intendi all'interno di Global.asax.cs, seguivo questa guida (http://www.asp.net/web-api/overview/extensibility/using-the-web-api-dependency-resolver), per vedere se avrebbe risolto il mio problema. Tuttavia, non sembrava. – user1417835

risposta

33

TLTR: il problema è causato dal implicito la modalità API Web gestisce la risoluzione dei tipi di controller; registra i controller della tua API Web in modo esplicito e vedrai dove si trova il problema.

Ecco un passo per passo cosa sta succedendo sotto le coperte:

  1. I System.Web.Http.DefaultHttpControllerActivator chiamate nel SimpleInjectorWebApiDependencyResolver e chiede la creazione di un controller API.
  2. SimpleInjectorWebApiDependencyResolver inoltro chiamata all'istanza SimpleInjector.Container.
  3. L'istanza Container tuttavia non ha alcuna registrazione esplicita per quel controller API (poiché è stato fornito un contenitore vuoto al resolver).
  4. Poiché non esiste una registrazione esplicita, il contenitore tenta di eseguire una registrazione dell'ultimo minuto per quel tipo.
  5. Quel tipo di controller tuttavia dipende da interfacce che non possono essere risolte perché non sono registrate nel contenitore (ricordare, il contenitore è vuoto).
  6. Sebbene il contenitore generi normalmente un'eccezione, in questo caso viene restituito un valore nullo, poiché il tipo è richiesto tramite il metodo IServiceProvider.GetService e il tipo non è stato registrato esplicitamente.
  7. Il metodo GetService restituirà null, poiché è per definizione che deve restituire null; Dovrebbe restituire null quando non esiste alcuna registrazione (che attualmente è il caso).
  8. Poiché il valore DependencyResolver ha restituito un valore nullo, DefaultHttpControllerActivator tornerà al suo comportamento predefinito, che significa creare quel tipo stesso, ma ciò richiede che il controllore abbia un costruttore predefinito.

Per farla breve, il problema è causato dal modo in cui implicita Web API maniglie risolvere tipi di controller.

Quindi la soluzione è quella di:

  1. avere solo una singola Container nell'applicazione Web. Ciò impedisce ogni sorta di problemi e complicazioni della configurazione.
  2. Registrare esplicitamente tutti i controller API Web nel contenitore. La registrazione dei controller in modo esplicito garantirà che Simple Injector genererà un'eccezione quando un controller non può essere risolto. Inoltre, questo ti consente di chiamare container.Verify() che farà fallire l'applicazione durante l'avvio quando la configurazione non è valida (a verifiable configuration is important). E questo ti consente anche di diagnose the configuration che ti dà ancora più sicurezza sulla correttezza della tua configurazione.

Il mio advice is to place MVC and Web API in their own project. Questo renderà le cose molto più facili.

Registrazione di tutti i controller Web API può essere fatto con il seguente codice:

container.RegisterWebApiControllers(GlobalConfiguration.Configuration); 

UPDATE:

Poiché questo errore è così comune, le versioni più recenti della classe SimpleInjectorWebApiDependencyResolver sarà semplicemente mai restituire null quando viene richiesto un tipo di controller. Invece genererà un errore descrittivo. Per questo motivo non si dovrebbe più vedere l'errore, a patto che si usi il SimpleInjectorWebApiDependencyResolver ufficiale.

+0

Grazie per la risposta dettagliata! Dove esattamente dovrei posizionare questo metodo? È implicito che dovrei creare una nuova classe e implementarla come metodo di estensione del Container? – user1417835

+0

È un metodo di estensione. Puoi posizionarlo in qualsiasi classe statica in modo da poter eseguire 'container.RegisterApiControllers()' all'interno della tua Root Composizione. – Steven

+0

Potrebbe essere appropriato impostare ** GlobalConfiguration ** 'DependencyResolver' all'interno di' SimpleInitializer'? O dovrebbe essere tenuto all'interno del Global.asax.cs? La documentazione (http://simpleinjector.codeplex.com/wikipage?title=Web%20API%20Integration) suggerisce che la creazione del contenitore e la registrazione di servizio/risorsa dovrebbero essere eseguite in 'Application_Start()' in Global.asax.cs, ma i commenti anche in 'InitializeContainer()' sembra suggerire che la registrazione dovrebbe avvenire lì. – user1417835

Problemi correlati