Sha*_*ica 6 c# structuremap asp.net multithreading
我有一个使用多个数据库分片的API应用程序,使用StructureMap进行依赖注入.每个API调用中所需的标头之一是a ShardKey
,它告诉我此调用正在寻址的数据库.为了实现这一点,我有一个OwinMiddleware
名为的类ShardingMiddleware
,其中包含以下代码(为清晰起见,已剪切):
var nestedContainer = container.GetNestedContainer();
using (var db = MyDbContext.ForShard(shardKey)) // creates a new MyDbContext with connection string appropriate to shardKey
{
nestedContainer.Configure(cfg => cfg.For<MyDbContext>().Use(db));
await Next.Invoke(context);
}
Run Code Online (Sandbox Code Playgroud)
这在我的测试环境中运行良好,并通过了一系列集成测试.
但是集成测试实际上是单线程的.当我将其部署到QA环境中时,真正的应用程序通过多个同时呼叫击中我的API,事情开始变得梨形.Ferinstance:
System.ObjectDisposedException:无法访问已处置的对象.此错误的常见原因是处理从依赖项注入解析的上下文,然后尝试在应用程序的其他位置使用相同的上下文实例.如果您在上下文中调用Dispose()或将上下文包装在using语句中,则可能会发生这种情况.如果使用依赖项注入,则应该让依赖项注入容器负责处理上下文实例.
或者其他异常表明StructureMap没有MyDbContext
可用的有效实例.
对我来说,似乎多个线程在某种程度上混淆了彼此的配置,但对于我的生活,我无法理解如何,因为我正在使用嵌套容器来存储每个API调用的数据库上下文.
有什么想法可能会出错吗?
更新:我还尝试将我的Db上下文抽象为一个接口.没有真正的区别; 我仍然得到错误
System.InvalidOperationException:尝试创建"SomeController"类型的控制器时发生错误.确保控制器具有无参数的公共构造函数.---> StructureMap.StructureMapConfigurationException:没有注册默认实例,无法自动确定类型'MyNamespace.IMyDbContext'
更新2:我解决了问题,但赏金仍然是开放的.请参阅下面的答案.
嗯...我解决了问题,但我不明白为什么这会产生影响。
归根结底,它与我最初发布的内容存在一些细微的差异,我之所以省略这些差异,是因为我认为这些细节无关紧要,而且会分散人们对问题的注意力。事实上,我的容器不是本地定义的;相反,它是我的中间件的受保护属性(出于集成测试目的而继承):
protected IContainer Container { get; private set; }
Run Code Online (Sandbox Code Playgroud)
然后方法内部有一个初始化调用Invoke()
:
Container = context.GetNestedContainer(); // gets the nested container created by a previous middleware class, using the context.Environment dictionary
Run Code Online (Sandbox Code Playgroud)
在整个方法中使用日志记录语句,我得到了以下代码(如问题中提到的,添加了日志记录):
_logger.Debug($"Line 1 Context={context.GetHashCode}, Container={Container.GetHashCode()}");
var db = MyDbContext.ForShard(shardKey.Value); // no need for "using", since DI will automatically dispose
_logger.Debug($"Line 2 Context={context.GetHashCode}, Container={Container.GetHashCode()}");
Container.Configure(cfg => cfg.For<MyDbContext>().Use(db));
await Next.Invoke(context);
Run Code Online (Sandbox Code Playgroud)
令人惊讶的是,以下是日志中的内容:
第 1 行上下文=56852305,容器=48376271
第 1 行上下文=88275661,容器=85736099
第 2 行上下文=56852305,容器=85736099
第 2 行上下文=88275661,容器=85736099
惊人的!我的中间件的属性Container
被神奇地替换了!尽管事实上它是用 a 定义的private set
,而且无论如何,为了安全起见,我检查了代码MyDbContext.ForShard()
,发现没有任何东西可能会弄乱 的引用Container
。
那么解决方案是什么呢?我container
在初始化后声明了一个局部变量,并使用它来代替。
它现在有效,但我不明白为什么或如何会产生影响。
能够解释这一点的人将得到赏金。