当启动一个新线程时,明智的做法是让 Container 为您组成一个全新的对象图。当您重用在 HTTP 请求(或线程上下文或您拥有的)上下文中创建的依赖项时,这可能会导致竞争条件和其他类型的失败和奇怪的行为。
当您知道这些依赖项是线程安全的(或使用每个请求的生命周期创建并且在触发异步操作后不被请求使用)时,当然不必是这种情况,但这不是这些依赖项的使用者应该知道或应该依赖,因为将所有内容连接在一起的是应用程序部分的责任(组合根)。这样做也会使以后更改该依赖项配置变得更加困难。
相反,您应该将您的DoThings
方法重构为它自己的类,并让您的 Controller 依赖于某种IDoThings
接口。通过这种方式,您可以推迟关于异步处理事物的决定,直到您将所有内容连接在一起的那一刻。在另一种类型的应用程序(例如 Windows 服务)中重用该逻辑时,它甚至允许您更改此操作的异步执行方式(或简单地同步执行)。
更进一步,实际的DoThings
业务逻辑和异步执行该逻辑的部分是两个不同的关注点:两个独立的职责。您应该将它们分成不同的类。
这是我建议您执行的示例:
定义一个接口:
public interface IDoThings
{
void DoThings();
}
让您的控制器依赖于该接口:
public class SomeController : Controller
{
private readonly IDoThings thingsDoer;
public SomeController(IDoThings thingsDoer)
{
this.thingsDoer = thingsDoer;
}
public ActionResult DoThings()
{
this.thingsDoer.DoThings();
return View();
}
}
定义一个包含业务逻辑的实现:
public class DoingThings : IDoThings
{
private readonly ICommandBus commandBus;
private readonly IObjectRepository objectRepository;
public DoingThings(ICommandBus commandBus,
IObjectRepository objectRepository)
{
this.commandBus = commandBus;
this.objectRepository = objectRepository;
}
public void DoThings()
{
var objects = objectRepository.getAll();
foreach (Thing thing in objects)
{
thing.modifiedOn = DateTime.Now;
objectRepository.Update(thing);
}
}
}
定义一个知道如何DoingThings
异步处理的代理:
public class DoingThingsAsync : IDoThings
{
private readonly Container container;
public DoingThingsAsync(Container container)
{
this.container = container;
}
public void DoThings()
{
Action handler = () => DoThingsNow();
handler.BeginInvoke(null, null);
}
private void DoThingsNow()
{
// Here we run in a new thread and HERE we ask
// the container for a new DoingThings instance.
// This way we will be sure that all its
// dependencies are safe to use. Never move
// dependencies from thread to thread.
IDoThings doer =
this.container.GetInstance<DoingThings>();
doer.DoThings();
}
}
现在,不是将 a 注入DoingThings
到 中,而是将 aSomeController
注入DoingThingsAsync
到控制器中。控制器不知道操作是否同步执行,它并不关心。除此之外,通过这种方式,您还可以将业务逻辑与表示逻辑分开,这很重要,原因有很多。
您可能需要考虑使用命令模式作为改变状态的业务操作的基础(如果您还没有使用这样的东西,请考虑ICommandBus
接口)。以看看这篇文章为例。使用此模式,您可以更轻松地配置某些命令以异步运行或将它们批处理到外部事务队列,以供以后处理,而无需更改这些命令的任何使用者。