it-swarm-eu.dev

Web Api Uruchomienie Wyjątki z implementacją IDependencyResolver

Opracowuję Web Api i zdecydowałem się użyć niestandardowego narzędzia DependencyResolver. Odsyłam to [Dependency Injection for Web API Controllers] article. Jak dotąd wszystko działa dobrze pod względem wstrzykiwania zależności do kontrolerów. Fragment kodu mojej konfiguracji z mojej klasy startowej Owin

private void RegisterIoC(HttpConfiguration config)
{
    _unityContainer = new UnityContainer();
    _unityContainer.RegisterType<IAccountService, AccountService>();
    .........
    .........
    config.DependencyResolver = new UnityResolver(_unityContainer);
}

Ale w czasie kiedy Api uruchamia się po raz pierwszy niektóre ResolutionFailedException rzucone (ale przechwycone) wewnątrz metody UnityResolver _ ​​GetService. Oto komunikat wyjątku

"Exception occurred while: while resolving. 
Exception is: InvalidOperationException - 
The current type, System.Web.Http.Hosting.IHostBufferPolicySelector, 
**is an interface and cannot be constructed. Are you missing a type mapping?**"

Powyżej tego samego wyjątku rzucono następujące typy

System.Web.Http.Hosting.IHostBufferPolicySelector
System.Web.Http.Tracing.ITraceWriter
System.Web.Http.Metadata.ModelMetadataProvider
System.Web.Http.Tracing.ITraceManager
System.Web.Http.Dispatcher.IHttpControllerSelector
System.Web.Http.Dispatcher.IAssembliesResolver
System.Web.Http.Dispatcher.IHttpControllerTypeResolver
System.Web.Http.Controllers.IHttpActionSelector
System.Web.Http.Controllers.IActionValueBinder
System.Web.Http.Validation.IBodyModelValidator
System.Net.Http.Formatting.IContentNegotiator

Wiem, że te ResolutionFailedException są zgłaszane, ponieważ nie udostępniłem mapowań w mojej konfiguracji jedności dla powyższych typów. 

Teraz jest moje pytanie: -, Jeśli implementuję niestandardową jedność DependencyResolver muszę zdefiniować mapowania powyższych typów i jeśli trzeba zdefiniować, jakie będą ich odpowiednie domyślne typy implementacji OR czy jest jakiś alternatywny sposób zaimplementuj DependencyResolver. Naprawdę się martwię, nawet jeśli aplikacja działa teraz poprawnie, nieumiejętność rozwiązania powyższego typu może później spowodować poważny problem. Proszę pomóż.

Jeden końcowy dodatek: - W przypadku następujących typów, ten sam ResolutionFailedException został zgłoszony, gdy zażądam jakiejkolwiek akcji w moim interfejsie API

System.Web.Http.Dispatcher.IHttpControllerActivator
System.Web.Http.Validation.IModelValidatorCache
System.Web.Http.Controllers.IHttpActionInvoker
25

Uruchomiłem ten sam problem, używając Unity z WebApi i OWIN/Katana.

Rozwiązaniem dla mnie było użycie UnityDependencyResolver zdefiniowanego w pakiecie Unity.WebApi Nuget zamiast własnej własnej implementacji (jak @Omar Alani powyżej)

Install-Package Unity.WebAPI

Zauważ, że pakiet spróbuje dodać plik o nazwie UnityConfig.cs w App_Start (nazwa pliku, której użyłem osobiście)

W tym pliku UnityConfig.cs pakiet doda kod w celu zarejestrowania kontenera na GlobalConfiguration.Configuration.DependencyResolver, który nie jest tym, czego chcemy w OWIN. 

Więc zamiast używać:

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

Zmień na użycie:

config.DependencyResolver = new UnityDependencyResolver(container);

Dla pełności:

Mój UnityConfig.cs

public static class UnityConfig
{
    public static void Register(HttpConfiguration config)
    {
        var container = new UnityContainer();

        // Your mappings here

        config.DependencyResolver = new UnityDependencyResolver(container);
    }
}

Moje Start.cs

[Assembly: OwinStartup(typeof(UnityTest.BusinessLayer.Api.ApiStartup))]
namespace UnityTest.BusinessLayer.Api
{
    public partial class ApiStartup
    {
        public void Configuration(IAppBuilder app)
        {

            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

            HttpConfiguration httpConfig = new HttpConfiguration();

            UnityConfig.Register(httpConfig);

            ConfigureAuth(app); //In App_Start ->Startup.Auth

            WebApiConfig.Register(httpConfig);

            app.UseWebApi(httpConfig);
    }
  }
}
20
James

W przypadku, gdy którekolwiek z powyższych rozwiązań nadal nie działa dla ludzi, oto jak to rozwiązałem.

Po spędzeniu dnia na pogoni za tym błędem okazało się, że jest to jakiś problem z buforowaniem VS. Z desperacji usunąłem wszystkie pliki .suo i force-get-latest, które najwyraźniej rozwiązały problem.

6
Ben Hughes

Zostało to zadane dawno temu, ale natknąłem się na rozwiązanie, które nie zostało tu wspomniane, więc może ktoś jest nadal zainteresowany.

W moim przypadku te wyjątki zostały już wykryte wewnętrznie przez Unity (lub cokolwiek innego), ale moje Ustawienia wyjątków w Visual Studio nadal je wyświetlały. Po prostu musiałem odznaczyć pole wyboru „Przerwa, gdy wyświetlany jest ten typ wyjątku”, a aplikacja działała normalnie.

4
Munir Husseini

Implementacja Unity.WebAPI nie różni się zbytnio od tej wspomnianej w pytaniu. Podobała mi się wersja, do której odnosi się OP, ponieważ ignoruje ona tylko zmienną ResultionFailedException i pozwala reszcie propagować w górę stosu. Unity.WebAPI pomija wszystkie wyjątki. To, co bym zrobił, to zignorowanie błędów, o których wiemy, że są bezpieczne, i rejestrowanie innych.

public object GetService(Type serviceType)
{
    try
    {
        return container.Resolve(serviceType);
    }
    catch(ResolutionFailedException ex)
    {
        if (!(typeof(System.Web.Http.Tracing.ITraceWriter).IsAssignableFrom(serviceType))
           || typeof(System.Web.Http.Metadata.ModelMetadataProvider).IsAssignableFrom(serviceType)
           //...
        ))
        {
            // log error
        }
    }

    return null;
}
3
Duoc Tran

Zwykle nie musisz korzystać z Unity . Używam tej implementacji dla IDependencyResolver z jednością i nie muszę się rejestrować ani mapować inaczej niż moje interfejsy/usługi.

public class UnityDependencyInjectionResolver : Disposable, IDependencyResolver
{
    protected IUnityContainer Container;

    public UnityDependencyInjectionResolver(IUnityContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }

        Container = container;
    }

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

    public T GetService<T>()
    {
        try
        {
            var serviceType = typeof(T);
            return (T)Container.Resolve(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return default(T);
        }
    }

    public T GetService<T>(string name)
    {
        try
        {
            var serviceType = typeof (T);
            return (T) Container.Resolve(serviceType, name);
        }
        catch (ResolutionFailedException)
        {
            return default(T);
        }
    }

    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 UnityDependencyInjectionResolver(child);
    }

    protected override void DisposeManagedResources()
    {
        if (Container == null)
        {
            return;
        }

        Container.Dispose();
        Container = null;
    }
}

gdzie Disposable to tylko klasa bazowa implementująca IDispoable.

Mam nadzieję, że to pomoże.

2
Omar.Alani

Ponieważ wydaje się, że wciąż jest to kwestionowane, oto moja wersja kodu ...

/// <summary>
/// Specifies the Unity configuration for the main container.
/// </summary>
public class UnityConfig
{
    private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
    {
        var container = new UnityContainer();

        RegisterTypes(container);

        return container;
    });

    /// <summary>
    /// Gets the configured Unity container.
    /// </summary>
    public static IUnityContainer GetConfiguredContainer()
    {
        return container.Value;
    }

    public static void RegisterTypes(IUnityContainer container)
    {
        // Keeping this separate allows easier unit testing
        // Your type mappings here
    }
}

[Assembly: OwinStartup(typeof(UnityTest.BusinessLayer.Api.ApiStartup))]
namespace UnityTest.BusinessLayer.Api
{
    public static HttpConfiguration Config { get; private set; }

    public partial class ApiStartup
    {
        public void Configuration(IAppBuilder app)
        {
            // IoC
            var container = UnityConfig.GetConfiguredContainer();                
            var resolver = new UnityHierarchicalDependencyResolver(container);  // Gets us scoped resolution            
            app.UseDependencyResolverScope(resolver);  // And for the OWIN

            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

            // NB Must be before WebApiConfig.Register
            ConfigureAuth(app); //In App_Start ->Startup.Auth

            // See http://stackoverflow.com/questions/33402654/web-api-with-owin-throws-objectdisposedexception-for-httpmessageinvoker
            // and http://aspnetwebstack.codeplex.com/workitem/2091
#if SELFHOST
            // WebAPI configuration
            Config = new HttpConfiguration
            {
                DependencyResolver = resolver
            };

            WebApiConfig.Register(Config);

            app.UseWebApi(Config);
#else
            GlobalConfiguration.Configuration.DependencyResolver = resolver;
            // http://stackoverflow.com/questions/19907226/asp-net-webapi-2-attribute-routing-not-working
            // Needs to be before RouteConfig.RegisterRoutes(RouteTable.Routes);
            GlobalConfiguration.Configure(WebApiConfig.Register);

            Config = GlobalConfiguration.Configuration;
#endif

            // Now do MVC configuration if appropriate
        }
    }
}

Wreszcie bity są rozszerzeniami do używania kontenera o zasięgu w oprogramowaniu pośrednim Owin, a także prostego interfejsu WebAPI

public static class AppBuilderExtensions
{
    public static IAppBuilder UseDependencyResolverScope(this IAppBuilder app, IDependencyResolver resolver)
    {
        return app.Use<DependencyResolverScopeMiddleware>(resolver);
    }
}

/// <summary>
/// Wraps middleware in a <see cref="IDependencyResolver"/> scope.
/// </summary>
public class DependencyResolverScopeMiddleware : OwinMiddleware
{
    private readonly IDependencyResolver resolver;

    public DependencyResolverScopeMiddleware(OwinMiddleware next, IDependencyResolver resolver) : base(next)
    {
        this.resolver = resolver;
    }

    public override async Task Invoke(IOwinContext context)
    {
        using (var scope = resolver.BeginScope())
        {
            context.SetDependencyScope(scope);
            await Next.Invoke(context);
        }
    }
}

Uzasadnieniem tego jest oryginalny MVC Work Item gdzie widzimy

kichalla napisał 27 października 2014 o 16:34

Tak ... dobrze ... Rozszerzenie UseWebApi powinno być używane tylko z scenariusze samodzielnego hostingu ... ponieważ wszyscy jesteśmy na tej samej stronie, jestem zamykając ten problem jako projekt ... daj nam znać, jeśli masz jakieś więcej pytań...

Dzięki, Kiran

kichalla napisał 29 października 2014 o 17:28

@thebothead: Dziękuję, że to odkryłeś! ... prawda, ta próbka nie powinien był używać Microsoft.AspNet.WebApi.Owin w IIS tak jak nigdy nie był przeznaczony do użycia w tym Hostie ... zbadamy problem dalej, aby zobaczyć, dlaczego ten wyjątek się dzieje ... ale w międzyczasie może podążać za podejściem wspomnianym w próbce, którą podałem wcześniej...

Dzięki, Kiran

Z własnego doświadczenia, jeśli nie użyjesz tej formy kodu, zadziała ona w debugowaniu itp., Ale nie będzie skalować i nie będzie zachowywać się dziwnie.

0
Paul Hatcher