32

我正在开发一个 Web Api,我决定使用自定义 DependencyResolver。我参考了这篇 [Web API 控制器的依赖注入]文章。到目前为止,在将依赖项注入控制器方面,一切都运行良好。我的 Owin 启动类中的配置代码片段

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

但是在Api 第一次启动时,UnityResolver 的GetService方法中抛出(但捕获)了一些 ResolutionFailedException。这是异常消息

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

上面抛出以下类型的相同异常

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

我知道这些 ResolutionFailedException 被抛出是因为我没有在我的统一配置中为上述类型提供映射。

现在这是我的问题:-,如果我实现自定义统一 DependencyResolver,我需要定义上述类型的映射,如果需要定义它们对应的默认实现类型,或者是否有一些替代方法来实现 DependencyResolver。即使应用程序现在运行良好,我也很担心,如果无法解决上述类型,以后可能会导致严重问题。请帮忙

最后一个补充:- 对于以下类型,当我向我的 web api 请求任何操作时,会抛出相同的 ResolutionFailedException

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

7 回答 7

31

我在使用 Unity 和 WebApi 和 OWIN/Katana 时遇到了同样的问题。

我的解决方案是使用 Unity.WebApi Nuget 包中定义的 UnityDependencyResolver 而不是我自己的自定义实现(如上面的@Omar Alani)

Install-Package Unity.WebAPI

请注意,该包将尝试在 App_Start 中添加一个名为 UnityConfig.cs 的文件(我自己使用的文件名)

在那个 UnityConfig.cs 文件中,包将添加代码来注册容器,GlobalConfiguration.Configuration.DependencyResolver这不是我们想要的 OWIN。

所以不要使用:

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

改用:

config.DependencyResolver = new UnityDependencyResolver(container);

为了完整性:

我的 UnityConfig.cs

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

        // Your mappings here

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

我的启动.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);
    }
  }
}
于 2015-02-17T12:26:55.960 回答
7

如果上述任何解决方案仍然对人们不起作用,这就是我解决它的方法。

在花了一天时间追查这个错误之后,结果证明这是某种 VS 缓存问题。出于绝望,我删除了所有 .suo 文件并强制获取最新,这似乎已经解决了问题。

于 2016-07-28T04:49:49.553 回答
6

很久以前就有人问过这个问题,但是我遇到了这里没有提到的解决方案,所以也许有人仍然感兴趣。

在我的情况下,这些异常已经被 Unity(或其他)内部捕获,但我在 Visual Studio 中的异常设置使它们仍然出现。我只需要取消选中“显示此异常类型时中断”复选框,应用程序就可以正常运行。

于 2017-01-25T21:10:01.147 回答
4

的实现Unity.WebAPI与问题中提到的没有太大不同。我喜欢 OP 引用的版本,因为它只忽略ResultionFailedException并让其余部分向上传播。Unity.WebAPI抑制所有异常。我要做的是忽略我们知道这样做是安全的错误并记录(或重新抛出)其他错误。

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;
}
于 2015-08-03T13:02:30.413 回答
2

通常,您不需要使用 Unity。我将此实现用于具有统一性的 IDependencyResolver,除了我的接口/服务之外,我不必注册或映射。

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

其中 Disposable 只是实现 IDispoable 的基类。

希望有帮助。

于 2014-10-20T03:21:12.360 回答
0

由于这似乎仍然存在争议,这是我的代码版本......

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

最后位是在 Owin 中间件以及直接 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);
        }
    }
}

这样做的理由是我们看到的原始MVC 工作项

kichalla 写于 2014 年 10 月 27 日下午 4:34

是的……对……UseWebApi 扩展应该只用于自托管场景……因为我们都在同一个页面上,我将按设计关闭这个问题……如果你有,请告诉我们还有问题...

谢谢,基兰

kichalla 写于 2014 年 10 月 29 日下午 5:28

@thebothead:感谢您发现这一点!...正确,此示例不应该在 IIS 中使用 Microsoft.AspNet.WebApi.Owin,因为它从未打算在该主机中使用...我们将调查该问题进一步了解为什么会发生此异常...但同时您可以按照我之前提供的示例中提到的方法...

谢谢,基兰

根据我自己的经验,如果您不使用这种形式的代码,它将在调试等中工作,但不会扩展并开始出现奇怪的行为。

于 2017-11-29T13:40:14.753 回答
-10

我已经删除了dependencyResolver,这个问题已经解决了

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

        // Your mappings here

        config.DependencyResolver = null;
    }
}
于 2015-08-26T09:27:11.933 回答