【问题标题】:Ninject and async operationsNinject 和异步操作
【发布时间】:2014-09-18 15:22:04
【问题描述】:

我现在已经在 Google 上搜寻了一下,以找到有关如何将 Ninject 与异步/等待操作一起使用的任何有用示例。我自己也尝试了一点,以找到任何可以使其实际工作而无需编写大量代码的模式,但我还没有设法使其工作。

对基本问题采取最简单的描述是实体框架DbContext是在每个请求范围的基础上创建的(它是一个 ASP.NET MVC 应用程序),但是如果您尝试调用任何*Async 方法它将失败并显示它已经在执行的信息(这很明显)。

所以,我需要的是调用 Kernel.Get<MyContext>() 使用 Ninject 创建唯一的 DbContext 对象,并且 Ninject 负责其生命周期。使用BeginBlock() 或将范围更改为InTransientScope() 并不是一个真正的选择,因为第一个选项会使代码非常繁重,带有单独的块并处理这些块,而后一组是负责处理DbContext 的调用者代码。

我想做的代码的POC示例:

var context1 = NinjectKernelReference.Get<MyContext>(); //I want this to be a unique reference
var context2 = NinjectKernelReference.Get<MyContext>(); //I want this to be a unique reference

var task1 = context1.Customers.Where(c => c.ZipCode == "4444")
                    .Select(c => new {
                                         c.Name,
                                         c.PhoneNumber
                                     })
                    .Take(50)
                    .ToArrayAsync();

var task2 = context2.Customers.CountAsync(c => c.ZipCode == "4444");

Task.WaitAll(task1, task2);

return new Result { task1.Result, task2.Result };

那么,有什么方法可以“简单”地解决这个问题?

【问题讨论】:

  • 我认为这是有问题的,因为您可能会将对象从一个上下文泄漏到另一个上下文中。将更新哪个上下文?等等。如果您想这样做,我建议您通过工厂创建上下文并手动管理生命周期(using 块,一旦您不再需要它就会处理它)。这至少可以清楚地说明它是如何工作的。
  • 从我的代码中可以看出,它只打算与 Select 语句一起使用,因此在这些情况下不会更新上下文。但是“通过工厂创建上下文”是什么意思?你有这方面的例子吗?

标签: c# asp.net asp.net-mvc asynchronous ninject


【解决方案1】:

这是您可以自己控制对象创建的方式:

public interface IContextFactory {
    MyContext Create();
}

kernel.Bind<IContextFactory>().ToFactory // requires ninjext.extensions.factory

(Factory Extension Link)

用法:

using(var context1 = IContextFactory.Create())
using(var context2 = IContextFactory.Create())
{
    var task1 = context1.Customers.Where(c => c.ZipCode == "4444")
                .Select(c => new {
                                     c.Name,
                                     c.PhoneNumber
                                 })
                .Take(50)
                .ToArrayAsync();

    var task2 = context2.Customers.CountAsync(c => c.ZipCode == "4444");

    Task.WaitAll(task1, task2);

    return new Result { task1.Result, task2.Result };
} // context get's disposed here

注意:这要求上下文绑定.InRequestScope(),否则,两个.Create()调用将返回相同的对象。 如果您有时需要它们.InRequestScope() 有时不需要,您可以考虑使用ninject contextual binding

【讨论】:

  • 谢谢,但这需要我自己处理上下文,这是我希望不需要的,但似乎没有它就无法完成。在这篇文章之后,我意识到使用IKernel.BeginBlock() 创建激活块并在其周围使用using 语句同样容易。
【解决方案2】:

在挖掘并阅读了其他用户对这篇文章的回复后,我意识到这不是一个简单的方法。

所以我决定使用IKernel.BeginBlock 在我执行逻辑的并行线程执行中创建一个“本地”Ninject 范围。

它可能不像我想要的那么整洁,但它确实有效。

using( var block1 = NinjectKernelReference.BeginBlock())
using( var block2 = NinjectKernelReference.BeginBlock())
{
    var context1 = block1.Get<MyContext>(); 
    var context2 = block2.Get<MyContext>(); 

    var task1 = context1.Customers.Where(c => c.ZipCode == "4444")
                        .Select(c => new {
                                             c.Name,
                                             c.PhoneNumber
                                         })
                        .Take(50)
                        .ToArrayAsync();

    var task2 = context2.Customers.CountAsync(c => c.ZipCode == "4444");

    Task.WaitAll(task1, task2);

    return new Result { task1.Result, task2.Result };
}

它可能不是那么整洁,但它可以工作,我看不出它有任何缺点(除了 BeginBlock 和处理它的一些小性能问题之外)。

【讨论】:

  • 我建议您远离ActivationBlock。它有一些副作用。参见例如planetgeek.ch/2012/04/23/future-of-activation-blocks(据我所知,其中提出的更改尚未实施!)。我认为 Ninject 目前的计划是摆脱ActivationBlock
  • 感谢您在这里添加,但是我现在开始对 Ninject 和线程/并行问题的所有限制感到厌倦,所以很快就会更换它。
  • 好吧,如果您发现其他一些 DI-Container 正在为您解决这些问题,请告诉我们,我是认真的。这会很有趣。
  • 好吧,我正在尝试在一个项目中使用 AutoFac,它会做我想做的事,DI 容器会分配我的上下文并在我的操作(它是一个 MVC 项目)结束时释放它。在配置中我这样做:builder.RegisterType&lt;MyDbContext&gt;().AsSelf().PropertiesAutowired(PropertyWiringOptions.PreserveSetValues); 然后在我的逻辑类的构造函数中,我有两个 MyDbContext 参数,AutoFac 自动分配给唯一的上下文,当我的操作返回时,它会自动释放,就像我想要的那样。跨度>
猜你喜欢
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 2019-05-27
  • 1970-01-01
  • 2015-02-02
  • 2013-06-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多