web-dev-qa-db-fra.com

Exceptions de démarrage Api Web avec implémentation IDependencyResolver

Je développe une API Web et j'ai décidé d'utiliser un DependencyResolver personnalisé. Je fais référence à this [Injection de dépendance pour les contrôleurs API Web] article. Jusqu'à présent, tout fonctionne bien en termes d'injection de dépendance dans les contrôleurs. Extrait de code de ma configuration de ma classe de démarrage Owin

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

Mais à l'époque lorsque Api démarre pour la toute première fois} _ une exception ResolutionFailedException levée (mais capturée) dans la méthode UnityResolverGetService. Voici le message d'exception

"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?**"

Au-dessus de identique exception levée des types suivants

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

Je sais que ces exceptions ResolutionFailedException sont levées parce que je n'ai pas fourni de mappages dans ma configuration d'unité pour les types ci-dessus. 

Maintenant, voici ma question: - _, si j'implémente l'unité personnalisée DependencyResolver, j'ai besoin de définir les mappages des types ci-dessus et, si besoin, de définir leurs types d'implémentation par défaut OR, existe-t-il une alternative manière de mettre en œuvre DependencyResolver. Je suis vraiment inquiet même si l'application fonctionne bien maintenant, ne pas résoudre le type ci-dessus peut causer un problème sérieux plus tard. _ {Aide s'il vous plaît} _.

Un dernier ajout: - Pour les types suivants, même exception ResolutionFailedException émise lorsque je demande une action dans l'action de mon api Web.

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

J'utilisais le même problème avec Unity avec WebApi et OWIN/Katana.

La solution pour moi a été d’utiliser le UnityDependencyResolver défini dans le package Unity.WebApi Nuget au lieu de ma propre implémentation personnalisée (comme @Omar Alani ci-dessus).

Install-Package Unity.WebAPI

Notez que le package va essayer d’ajouter un fichier nommé UnityConfig.cs dans App_Start (le nom du fichier que j’ai moi-même utilisé)

Dans ce fichier UnityConfig.cs, le package ajoutera du code pour enregistrer le conteneur sur le GlobalConfiguration.Configuration.DependencyResolver, ce qui n'est pas ce que nous souhaitons avec OWIN. 

Donc au lieu d'utiliser:

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

Changer pour utiliser:

config.DependencyResolver = new UnityDependencyResolver(container);

Pour être complet:

Mon UnityConfig.cs

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

        // Your mappings here

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

Mon Startup.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

Si l'une des solutions ci-dessus ne fonctionne toujours pas pour les utilisateurs, voici comment j'ai résolu le problème.

Après avoir passé une journée à traquer cette erreur, il s’est avéré qu’il s’agissait d’un problème de mise en cache de VS En désespoir de cause, j'ai supprimé tous les fichiers .suo et force-get-latest, ce qui semble avoir résolu le problème.

6
Ben Hughes

Cela a été demandé il y a longtemps, mais j'ai rencontré une solution qui n'était pas mentionnée ici, alors peut-être que quelqu'un est toujours intéressé.

Dans mon cas, ces exceptions étaient déjà interceptées en interne par Unity (ou autre), mais mon Exception Settings dans Visual Studio les faisait toujours apparaître. Il me suffisait de décocher la case "Interrompre l'affichage de ce type d'exception" et l'application fonctionnait normalement.

4
Munir Husseini

L'implémentation de Unity.WebAPI n'est pas très différente de celle mentionnée dans la question. J'ai aimé la version mentionnée par l'OP, car elle ignore uniquement ResultionFailedException et laisse le reste se propager dans la pile. Unity.WebAPI supprime toutes les exceptions. Ce que je ferais, c’est ignorer les erreurs que nous savons être sûres et enregistrer les autres (ou les rediffuser).

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

Normalement, vous n'en avez pas besoin avec Unity . J'utilise cette implémentation pour IDependencyResolver avec unicité et je n'ai pas besoin d'enregistrer ni de mapper autre que mes interfaces/services.

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;
    }
}

où Disposable est juste une classe de base implémente IDispoable.

J'espère que cela pourra aider.

2
Omar.Alani

Comme cela semble toujours être contesté, voici ma version du code ...

/// <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
    }
}

et 

[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
        }
    }
}

Enfin, bits sont les extensions permettant d’utiliser le conteneur scoped dans le middleware Owin, ainsi que WebAPI direct.

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);
        }
    }
}

La raison en est l'original Élément de travail MVC où nous voyons

kichalla a écrit sur 27 oct. 2014 à 16h34

Oui ... bien ... L'extension UseWebApi ne doit être utilisée qu'avec scénarios d'auto-hébergement ... puisque nous sommes tous sur la même page, je suis clôturant ce problème comme une construction ... veuillez nous en informer si vous en avez plus de questions...

Merci Kiran

et 

kichalla a écrit le 29 octobre 2014 à 17:28

@thebothead: Merci d'avoir découvert cela! ... oui, cet échantillon n'aurait pas dû utiliser Microsoft.AspNet.WebApi.Owin dans IIS en tant que n’a jamais été destiné à être utilisé dans cet hôte ... nous étudierons le question plus loin pour voir pourquoi cette exception se produit ... mais en attendant vous pourrait suivre l’approche mentionnée dans l’échantillon que j’ai fourni plus tôt...

Merci Kiran

D'après ma propre expérience, si vous n'utilisez pas cette forme de code, cela fonctionnera dans le débogage, etc., mais ne sera pas redimensionné et commencera à se comporter étrangement.

0
Paul Hatcher