c# - 在自定义 TypeFormatter 中使用 RegisterWebApiRequest 时出现 SimpleInjector.ActivationException

标签 c# dependency-injection asp.net-web-api2 formatter simple-injector

我使用自定义 JsonMediaTypeFormatter 来扩展请求中的数据。但是当我使用 GetInstance 方法时,格式化程序会抛出异常。我做错了什么?

全局.asax:

// Create the container as usual.
var container = new Container();

// Register your types, for instance using the RegisterWebApiRequest
// extension from the integration package:
container.RegisterWebApiRequest<TestDataContext>();

// This is an extension method from the integration package.
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

container.Verify();

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

// Here your usual Web API configuration stuff.
GlobalConfiguration.Configuration.Formatters.Add(new StringFormatter(container));

格式化程序(经过简化,因此可以与新的 MVC Web Api 项目配合使用):

public class StringFormatter : JsonMediaTypeFormatter
{
    private readonly Container _container;

    public StringFormatter(Container container)
    {
        _container = container;
        SupportedMediaTypes.Add(
            new MediaTypeHeaderValue("application/vnd.mywebapplication+json"));
    }

    public override bool CanReadType(Type type)
    {
        return false;
    }

    public override bool CanWriteType(Type type)
    {
        return (type == typeof (IEnumerable<string>) || type == typeof (string));
    }

    public override void WriteToStream(Type type, object value, Stream writeStream,
        System.Text.Encoding effectiveEncoding)
    {
        // Throws SimpleInjector.ActivationException
        var dataContext = _container.GetInstance<TestDataContext>(); 

        var result = dataContext.GetText();
        type = result.GetType();
        value = result;

        base.WriteToStream(type, value, writeStream, effectiveEncoding);
    }
}

这是异常消息:

The registered delegate for type TestDataContext threw an exception. The TestDataContext is registered as 'Web API Request' lifestyle, but the instance is requested outside the context of a Web API Request.

有点困惑的堆栈跟踪:

at SimpleInjector.InstanceProducer.GetInstance() 
at SimpleInjector.Container.GetInstance[TService]() 
at WebApplication4.Formatter.StringFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding) in d:\UserProfiles\luuk\Documents\Visual Studio 2013\Projects\WebApplication4\WebApplication4\Formatter\StringFormatter.cs:line 35 
at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content) 
at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- 
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
at System.Runtime.CompilerServices.TaskAwaiter.GetResult() 
at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()"

at SimpleInjector.Scope.GetScopelessInstance[TService,TImplementation](ScopedRegistration'2 registration) 
at SimpleInjector.Scope.GetInstance[TService,TImplementation](ScopedRegistration'2 registration, Scope scope) 
at SimpleInjector.Advanced.Internal.LazyScopedRegistration'2.GetInstance(Scope scope) 
at DynamicInstanceProducer2.GetInstance(Object[] constants) 
at SimpleInjector.CompilationHelpers.<>c__DisplayClass1b'1.

最佳答案

经过一些调试,我发现 Web API 处理 TypeFormatter 的方式有些奇怪。它在创建请求的 IDependencyScope 之后、释放该范围之前调用 WriteToStream。但是,尽管在 IDependencyScope 的开始和结束之间调用 WriteToStream,但它并不是在与 Web API 请求的其余部分相同的执行上下文中调用。

这意味着对于 Simple Injector 而言,当时没有请求,因为 Simple Injector 使用 .NET 的执行上下文来跟踪 SimpleInjector.Scope 类。

这可能是 Web API 中的一个错误,但它可能是 Web API 中的设计初衷。也许原因是类型格式化程序仅包含基本逻辑,并且不与当前请求或某些后端数据库进行交互。这对我来说听起来很合理,但并不能解决您的问题。

解决此问题的一个简单方法是显式创建 ExecutionContextScope:

public override void WriteToStream(Type type, object value, Stream writeStream,
    System.Text.Encoding effectiveEncoding)
{
    using (container.BeginExecutionContextScope())
    {
        var dataContext = _container.GetInstance<TestDataContext>(); 

        var result = dataContext.GetText();
        type = result.GetType();
        value = result;

        base.WriteToStream(type, value, writeStream, effectiveEncoding);
    }
}

WebApiRequestLifestyle 实际上只是 ExecutionContextScopeLifestyle 的实用包装器,只是查找事件的 ExecutionContextScope。因此,通过显式启动这样的范围(使用 BeginExecutionContextScope),您可以确保存在一个可以解析 TestDataContext 的范围。

我想你在使用其他 DI 框架时也会遇到同样的问题。这些框架也将使用执行上下文,或者它们允许您将事件解析范围传递给调用者,这两种情况在此方法中都是不可能的。然而,某些容器会让您认为解析上下文会起作用,而实际上,当没有作用域时,它们会返回一个单例实例。使用单例数据上下文当然是一件非常糟糕的事情,您的应用程序将严重崩溃。但它当然只会在生产中中断,因为那是并发错误的薄弱环节。

关于c# - 在自定义 TypeFormatter 中使用 RegisterWebApiRequest 时出现 SimpleInjector.ActivationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23884775/

相关文章:

c# - 如何将两个列值添加到列表

c# - Microsoft.Extensions.DependencyInjection : DI'd code runs fine on dotnet CLI/Rider default . NET 配置文件,但导致 VS2022 崩溃

java - Spring Boot 测试类可以重用应用程序上下文以更快地运行测试吗?

asp.net-mvc - 如何修改.net mvc web api中的响应

c# - 每个 Http 请求的分散式 NLog 目标

c# - 将属性作为参数传递

c# - 使用 EF 数据库设置 WCF 服务的正确方法是什么?

c# - Assembly Resolve递归成无限循环

java - 如何在 jersey2/hk2 应用程序中获取对 Jackson Object Mapper 的引用

c# - ASP.NET Web Api 2 中的异常处理