web-dev-qa-db-ger.com

Abhängigkeiten können mit Unity nicht in ASP.NET Web API Controller injiziert werden

Hat jemand mit einem IoC-Container erfolgreich ausgeführt, um Abhängigkeiten in ASP.NET-WebAPI-Controller einzufügen? Ich kann es scheinbar nicht zum Laufen bringen.

Das mache ich jetzt.

In meinem global.ascx.cs:

    public static void RegisterRoutes(RouteCollection routes)
    {
            // code intentionally omitted 
    }

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

        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);

        IUnityContainer container = BuildUnityContainer();

        System.Web.Http.GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
            t =>
            {
                try
                {
                    return container.Resolve(t);
                }
                catch (ResolutionFailedException)
                {
                    return null;
                }
            },
            t =>
            {
                try
                {
                    return container.ResolveAll(t);
                }
                catch (ResolutionFailedException)
                {
                    return new System.Collections.Generic.List<object>();
                }
            });

        System.Web.Mvc.ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(container)); 

        BundleTable.Bundles.RegisterTemplateBundles();
    }

    private static IUnityContainer BuildUnityContainer()
    {
        var container = new UnityContainer().LoadConfiguration();

        return container;
    }

Meine Controller-Fabrik:

public class UnityControllerFactory : DefaultControllerFactory
            {
                private IUnityContainer _container;

                public UnityControllerFactory(IUnityContainer container)
                {
                    _container = container;
                }

                public override IController CreateController(System.Web.Routing.RequestContext requestContext,
                                                    string controllerName)
                {
                    Type controllerType = base.GetControllerType(requestContext, controllerName);

                    return (IController)_container.Resolve(controllerType);
                }
            }

Es scheint nie in meiner Unity-Datei nach Abhängigkeiten zu suchen, und ich erhalte die Fehlermeldung:

Beim Erstellen eines Controllers vom Typ "PersonalShopper.Services.WebApi.Controllers.ShoppingListController" ist ein Fehler aufgetreten. Stellen Sie sicher, dass die Steuerung über einen parameterlosen öffentlichen Konstruktor verfügt.

bei System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create (HttpControllerContext controllerContext, Typ controllerType) bei System.Web.Http.Dispatcher.DefaultHttpControllerFactory.CreateInstance (HttpControllerContext controllerContext, HttpControllerDescriptor (HttpControllerContext controllerContext, String controllerName) unter System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncInternal (HttpRequestMessage-Anforderung, CancellationToken CancellationToken) unter System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncestMoken (HttpControllerDispatcher.S

Controller sieht aus wie:

public class ShoppingListController : System.Web.Http.ApiController
    {
        private Repositories.IProductListRepository _ProductListRepository;


        public ShoppingListController(Repositories.IUserRepository userRepository,
            Repositories.IProductListRepository productListRepository)
        {
            _ProductListRepository = productListRepository;
        }
}

Meine Einheitsdatei sieht folgendermaßen aus:

<unity xmlns="http://schemas.Microsoft.com/practices/2010/unity">
  <container>
    <register type="PersonalShopper.Repositories.IProductListRepository, PersonalShopper.Repositories" mapTo="PersonalShopper.Implementations.MongoRepositories.ProductListRepository, PersonalShopper.Implementations" />
  </container>
</unity>

Beachten Sie, dass ich keine Registrierung für den Controller selbst habe, da die Controller-Factory in früheren Versionen von mvc herausgefunden hat, dass die Abhängigkeiten aufgelöst werden mussten.

Es scheint, als würde meine Controller-Fabrik niemals aufgerufen.

81
Oved D

Herausgefunden.

Für ApiControllers verwendet MVC 4 System.Web.Http.Dispatcher.IHttpControllerFactory und System.Web.Http.Dispatcher.IHttpControllerActivator zum Erstellen des Steuerungen. Wenn es keine statische Methode gibt, um zu registrieren, um welche Implementierung es sich handelt; Wenn sie aufgelöst werden, sucht das MVC-Framework nach den Implementierungen im Abhängigkeits-Resolver und verwendet die Standardimplementierungen, wenn sie nicht gefunden werden.

Ich habe die Einheitsauflösung von Controller-Abhängigkeiten erhalten, indem ich Folgendes ausgeführt habe:

Erstellt einen UnityHttpControllerActivator:

public class UnityHttpControllerActivator : IHttpControllerActivator
{
    private IUnityContainer _container;

    public UnityHttpControllerActivator(IUnityContainer container)
    {
        _container = container;
    }

    public IHttpController Create(HttpControllerContext controllerContext, Type controllerType)
    {
        return (IHttpController)_container.Resolve(controllerType);
    }
}

Diesen Controller-Aktivator als Implementierung im Unity-Container selbst registriert:

protected void Application_Start()
{
    // code intentionally omitted

    IUnityContainer container = BuildUnityContainer();
    container.RegisterInstance<IHttpControllerActivator>(new UnityHttpControllerActivator(container));

    ServiceResolver.SetResolver(t =>
       {
         // rest of code is the same as in question above, and is omitted.
       });
}
42
Oved D

Es gibt eine bessere Lösung, die hier richtig funktioniert

http://www.asp.net/web-api/overview/extensibility/using-the-web-api-dependency-resolver

37
CodeKata

Vielleicht möchten Sie einen Blick auf das Unity.WebApi NuGet-Paket werfen, das all dies regelt und auch IDisposable-Komponenten berücksichtigt.

sehen

http://nuget.org/packages/Unity.WebAPI

oder

http://www.devtrends.co.uk/blog/introducing-the-unity.webapi-nuget-package

22
Paul Hiles

Microsoft hat hierfür ein Paket erstellt.

Führen Sie den folgenden Befehl in der Package Manager-Konsole aus.

installationspaket Unity.AspNet.WebApi

Wenn Sie Unity bereits installiert haben, werden Sie gefragt, ob Sie App_Start\UnityConfig.cs überschreiben möchten. Antworten Sie mit Nein und fahren Sie fort.

Es ist nicht erforderlich, einen anderen Code zu ändern, und DI (mit Einheit) funktioniert.

9
garethb

Ich hatte den gleichen Fehler und suchte einige Stunden lang im Internet nach Lösungen. Schließlich schien es, dass ich Unity registrieren musste, bevor ich das WebApiConfig.Register aufrief. Mein global.asax sieht jetzt so aus

public class WebApiApplication : System.Web.HttpApplication
{
   protected void Application_Start()
   {
       UnityConfig.RegisterComponents();
       AreaRegistration.RegisterAllAreas();
       GlobalConfiguration.Configure(WebApiConfig.Register);
       FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
       RouteConfig.RegisterRoutes(RouteTable.Routes);
       BundleConfig.RegisterBundles(BundleTable.Bundles);
   }
}

Für mich löste dies das Problem, dass Unity die Abhängigkeiten in meinen Controllern nicht auflösen konnte

3
Vincent

Nachdem ich die Antworten gelesen hatte, musste ich immer noch viel herumgraben, um auf diesem Gotcha zu landen. Hier ist es also zum Vorteil der Kollegen: Dies ist alles, was Sie in ASP.NET 4 Web API RC tun müssen (Stand: 8. August) '13):

  1. Verweis auf "Microsoft.Practices.Unity.dll" hinzufügen [Ich bin auf Version 3.0.0.0, hinzugefügt durch NuGet]
  2. Verweis auf "Unity.WebApi.dll" hinzufügen [Ich bin auf Version 0.10.0.0, hinzugefügt durch NuGet]
  3. Registrieren Sie Ihre Typzuordnungen im Container - ähnlich dem Code in Bootstrapper.cs, der von Unity.WebApi-Projekt zu Ihrem Projekt hinzugefügt wird.
  4. Erstellen Sie in den Controllern, die von der ApiController-Klasse erben, parametrisierte Konstruktoren, deren Parametertypen den zugeordneten Typen entsprechen

Und siehe da, Sie bekommen die Abhängigkeiten ohne eine weitere Codezeile in Ihren Konstruktor injiziert!

HINWEIS: Ich habe diese Information von einem der Kommentare in THIS blog von seinem Autor erhalten.

2

In einem aktuellen RC finde ich, dass es keine SetResolver-Methode mehr gibt. Um sowohl IoC für Controller als auch Webapi zu aktivieren, verwende ich Unity.WebApi (NuGet) und den folgenden Code:

public static class Bootstrapper
{
    public static void Initialise()
    {
        var container = BuildUnityContainer();

        GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);

        ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(new ControllerActivator()));
    }

    private static IUnityContainer BuildUnityContainer()
    {
        var container = new UnityContainer();

        container.Configure(c => c.Scan(scan =>
        {
            scan.AssembliesInBaseDirectory();
            scan.With<UnityConfiguration.FirstInterfaceConvention>().IgnoreInterfacesOnBaseTypes();
        }));

        return container;
    }
}

public class ControllerActivator : IControllerActivator
{
    IController IControllerActivator.Create(RequestContext requestContext, Type controllerType)
    {
        return GlobalConfiguration.Configuration.DependencyResolver.GetService(controllerType) as IController;
    }
}

Ich benutze auch UnityConfiguration (ebenfalls von NuGet) für IoC-Magie. ;)

2
Noogen

Kurze Zusammenfassung für ASP.NET Web API 2.

Installieren Sie Unity von NuGet.

Erstellen Sie eine neue Klasse mit dem Namen UnityResolver:

using Microsoft.Practices.Unity;
using System;
using System.Collections.Generic;
using System.Web.Http.Dependencies;

public class UnityResolver : IDependencyResolver
{
    protected IUnityContainer container;

    public UnityResolver(IUnityContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }
        this.container = container;
    }

    public object GetService(Type serviceType)
    {
        try
        {
            return container.Resolve(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return null;
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        try
        {
            return container.ResolveAll(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return new List<object>();
        }
    }

    public IDependencyScope BeginScope()
    {
        var child = container.CreateChildContainer();
        return new UnityResolver(child);
    }

    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        container.Dispose();
    }
}

Erstellen Sie eine neue Klasse mit dem Namen UnityConfig:

public static class UnityConfig
{
    public static void ConfigureUnity(HttpConfiguration config)
    {
        var container = new UnityContainer();
        container.RegisterType<ISomethingRepository, SomethingRepository>();
        config.DependencyResolver = new UnityResolver(container);
    }
}

Bearbeiten Sie App_Start -> WebApiConfig.cs

public static void Register(HttpConfiguration config)
{
    UnityConfig.ConfigureUnity(config);
    ...

Jetzt wird es funktionieren.

Originalquelle, aber etwas geändert: https://docs.Microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection

1
Ogglas

Ich hatte die gleiche Ausnahme geworfen, und in meinem Fall hatte ich einen Konflikt zwischen MVC3 und MVC4-Binärdateien. Dies verhinderte, dass meine Controller ordnungsgemäß in meinem Container IOC registriert wurden. Überprüfen Sie Ihre web.config und vergewissern Sie sich, dass sie auf die richtigen Versionen von MVC verweist.

1
user1233012

Dieses Problem tritt aufgrund der Registrierung von Controllern bei Unity auf. Ich habe dieses Problem gelöst, indem ich mich wie unten gezeigt gemäß der Konvention registriert habe. Bitte filtern Sie zusätzliche Typen nach Bedarf heraus.

    IUnityContainer container = new UnityContainer();

    // Do not register ApiControllers with Unity.
    List<Type> typesOtherThanApiControllers
        = AllClasses.FromLoadedAssemblies()
            .Where(type => (null != type.BaseType)
                           && (type.BaseType != typeof (ApiController))).ToList();

    container.RegisterTypes(
        typesOtherThanApiControllers,
        WithMappings.FromMatchingInterface,
        WithName.Default,
        WithLifetime.ContainerControlled);

Auch im obigen Beispiel wird AllClasses.FromLoadedAssemblies() verwendet. Wenn Sie Assemblys aus dem Basispfad laden möchten, funktioniert dies in einem Web-API-Projekt mit Unity möglicherweise nicht wie erwartet. Bitte werfen Sie einen Blick auf meine Antwort auf eine andere diesbezügliche Frage. https://stackoverflow.com/a/26624602/1350747

1

Ich hatte das gleiche Problem bei der Verwendung des Unity.WebAPI NuGet-Pakets. Das Problem war, dass das Paket UnityConfig.RegisterComponents() in meiner Datei Global.asax nie aufgerufen hat.

Global.asax.cs sollte folgendermaßen aussehen:

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        UnityConfig.RegisterComponents();
        ...
    }
}
0
Keith