如何使用Ninject InRequestScope处理异步调用?

我们在ASP.NET Web Api应用程序中使用Ninject ,并将DbContextInRequestScope绑定。 这适用于我们的大多数请求,因为它们同步完成所有工作,因此在请求完成后可以安全地处理上下文。

但是,我们根据请求进行异步Web服务调用,它具有作为回调传递的continuation方法,并且该回调方法需要使用数据库上下文。 但是我们的请求不应该等待异步服务调用完成,而是立即返回(这是一个明确的要求)。

这是一个简化的情况示例。

 public class MyController : ApiController { private readonly MyDbContext dbContext; private readonly SomeWebService service; public MyController(MyDbContext dbContext, SomeWebService service) { this.dbContext = dbContext; this.service = service; } public IHttpActionResult MyActionWithAsyncCall() { // Doing stuff. // Calling webservice method, passing the Callback as the continuation. service.MethodWithCallback(param1, param2, this.Callback); // Returning without waiting for the service call to be completed. return Ok(); } private void Callback() { // Trying to use the DbContext: var person = dbContext.People.First(); // The above line sometimes throws exception, because the context has been disposed. } } 

如何用Ninject处理这种情况? 有没有办法以某种方式“延长”绑定的DbContext实例的生命周期? 或者Callback方法应该创建全新的DbContext ? 如果应该,它应该使用什么范围?

使用.InRequestScope()扩展到请求结束后,无法显式延长对象的生命周期。

如果没有业务要求请求期间的工作和@回调必须在单个事务中发生,那么我将使用两个DbContext实例。 一个在请求期间,另一个在回调期间。 注意:据我所知,这也意味着您无法从第一个上下文中获取实体并在第二个上下文中更新/保存它。 这意味着您必须只将标识符(以及与操作相关的其他数据)从请求传递回调。 回调必须“创建”新的DbContext并从上下文中检索相应的权限。

条件绑定替代方案

作为替代方案,您可以为此特殊情况声明特殊绑定。 Ninject支持所谓的上下文绑定 。 这意味着你将有两个绑定,标准绑定和一个上下文,特殊情况绑定:

 Bind().ToSelf().InRequestScope(); Bind().ToSelf() .WhenInjectedInto(); 

请注意,第二个绑定未指定范围 – 这意味着SomeController负责调用.Dispose() 。 在你的情况下,这意味着回调必须处理上下文。 您还需要在所有错误情况下处理上下文(回调代码中的错误,触发回调之前发生的错误,……)。

此外,实际上你的应用程序可能更复杂,而且.WhenInjectedInto不够/正确,因为您可能希望将相同的实例注入控制器以及存储库和查询对象..什么不是。

这意味着您将需要范围,但范围不同于.InRequestScope() 。 您可以使用.InCallScope()或命名范围 – 两者都包含在命名范围扩展中 。

此外,您需要调整When条件。 您可以调整它以便遍历请求并查看请求链中的任何位置是否有FooController 。 但这并不是非常高效,相反我建议使用ninject IParameter来指定您需要特殊情况处理。 参数是:

 public class NonRequestScopedParameter : Ninject.Parameters.IParameter { public bool Equals(IParameter other) { if (other == null) { return false; } return other is NonRequestScopedParameter; } public object GetValue(IContext context, ITarget target) { throw new NotSupportedException("this parameter does not provide a value"); } public string Name { get { return typeof(NonRequestScopedParameter).Name; } } // this is very important public bool ShouldInherit { get { return true; } } } 

这将应用于绑定,如:

 kernel.Bind().ToSelf() .WithParameter(new NonRequestScopedParameter()); kernel.Bind().ToSelf() .When(x => x.Parameters.OfType().Any()) .InCallScope(); // or whatever scope you're using