- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在使用 Simple Injector 来管理我注入(inject)的依赖项的生命周期(在本例中为 UnitOfWork
),我很高兴有一个单独的装饰器而不是我的服务或命令处理程序来处理保存在编写业务逻辑层时,处置使代码更容易(我遵循 this blog post 中概述的架构)。
通过在构建组合根容器期间使用 Simple Injector MVC NuGet 包和以下代码,以上代码完美(并且非常容易)工作,如果图中存在多个依赖项,则同一实例将注入(inject)所有- 非常适合 Entity Framework 模型上下文。
private static void InitializeContainer(Container container)
{
container.RegisterPerWebRequest<IUnitOfWork, UnitOfWork>();
// register all other interfaces with:
// container.Register<Interface, Implementation>();
}
我现在需要运行一些后台线程并从 Simple Injector 了解 documentation on threads该命令可以代理如下:
public sealed class TransactionCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand>
{
private readonly ICommandHandler<TCommand> handlerToCall;
private readonly IUnitOfWork unitOfWork;
public TransactionCommandHandlerDecorator(
IUnitOfWork unitOfWork,
ICommandHandler<TCommand> decorated)
{
this.handlerToCall = decorated;
this.unitOfWork = unitOfWork;
}
public void Handle(TCommand command)
{
this.handlerToCall.Handle(command);
unitOfWork.Save();
}
}
ThreadedCommandHandlerProxy:
public class ThreadedCommandHandlerProxy<TCommand>
: ICommandHandler<TCommand>
{
Func<ICommandHandler<TCommand>> instanceCreator;
public ThreadedCommandHandlerProxy(
Func<ICommandHandler<TCommand>> creator)
{
this.instanceCreator = creator;
}
public void Handle(TCommand command)
{
Task.Factory.StartNew(() =>
{
var handler = this.instanceCreator();
handler.Handle(command);
});
}
}
但是,从这个线程示例文档中我可以看到使用了工厂,如果我将工厂引入我的命令和服务层,事情就会变得困惑和不一致,因为我将为不同的服务使用不同的保存方法(一个容器处理保存,其他容器处理保存服务中的实例化工厂处理保存和处理)——您可以看到没有任何工厂的服务代码框架是多么清晰和简单:
public class BusinessUnitCommandHandlers :
ICommandHandler<AddBusinessUnitCommand>,
ICommandHandler<DeleteBusinessUnitCommand>
{
private IBusinessUnitService businessUnitService;
private IInvoiceService invoiceService;
public BusinessUnitCommandHandlers(
IBusinessUnitService businessUnitService,
IInvoiceService invoiceService)
{
this.businessUnitService = businessUnitService;
this.invoiceService = invoiceService;
}
public void Handle(AddBusinessUnitCommand command)
{
businessUnitService.AddCompany(command.name);
}
public void Handle(DeleteBusinessUnitCommand command)
{
invoiceService.DeleteAllInvoicesForCompany(command.ID);
businessUnitService.DeleteCompany(command.ID);
}
}
public class BusinessUnitService : IBusinessUnitService
{
private readonly IUnitOfWork unitOfWork;
private readonly ILogger logger;
public BusinessUnitService(IUnitOfWork unitOfWork,
ILogger logger)
{
this.unitOfWork = unitOfWork;
this.logger = logger;
}
void IBusinessUnitService.AddCompany(string name)
{
// snip... let container call IUnitOfWork.Save()
}
void IBusinessUnitService.DeleteCompany(int ID)
{
// snip... let container call IUnitOfWork.Save()
}
}
public class InvoiceService : IInvoiceService
{
private readonly IUnitOfWork unitOfWork;
private readonly ILogger logger;
public BusinessUnitService(IUnitOfWork unitOfWork,
ILogger logger)
{
this.unitOfWork = unitOfWork;
this.logger = logger;
}
void IInvoiceService.DeleteAllInvoicesForCompany(int ID)
{
// snip... let container call IUnitOfWork.Save()
}
}
根据我从 documentation on ASP .NET PerWebRequest lifetimes 中了解到的情况,我的问题开始形成了,使用了以下代码:
public T GetInstance()
{
var context = HttpContext.Current;
if (context == null)
{
// No HttpContext: Let's create a transient object.
return this.instanceCreator();
}
object key = this.GetType();
T instance = (T)context.Items[key];
if (instance == null)
{
context.Items[key] = instance = this.instanceCreator();
}
return instance;
}
上面的代码对于每个 HTTP 请求都工作正常,将有一个有效的 HttpContext.Current
,但是如果我使用 ThreadedCommandHandlerProxy
启动一个新线程,它将创建一个新线程和 HttpContext
将不再存在于该线程中。
由于 HttpContext
在每次后续调用中都为 null,因此注入(inject)到服务构造函数中的所有对象实例都将是新的且唯一的,这与正常的 HTTP 每个 Web 请求相反,其中对象被正确共享为所有服务中的相同实例。
所以把上面的总结成问题:
无论是从 HTTP 请求还是通过新线程创建的,我将如何构建对象并注入(inject)公共(public)项目?
让 UnitOfWork
由命令处理程序代理中的线程管理是否有任何特殊注意事项?如何确保它在处理程序执行后得到保存和处置?
如果我们在命令处理程序/服务层中遇到问题并且不想保存 UnitOfWork
,我们会直接抛出异常吗?如果是这样,是否有可能在全局级别捕获它,或者我们是否需要在处理程序装饰器或代理中的 try
-catch
中捕获每个请求的异常?
谢谢,
克里斯
最佳答案
首先让我警告一下,如果您希望在 Web 应用程序中异步执行命令,您可能需要退后一步,看看您要实现的目标。在后台线程上启动处理程序后,始终存在 Web 应用程序被回收的风险。当 ASP.NET 应用程序被回收时,所有后台线程都将中止。将命令发布到(事务性)队列并让后台服务接收它们可能会更好。这确保命令不会“丢失”。并且还允许您在处理程序未成功成功时重新执行命令。它还可以让您免于进行一些麻烦的注册(无论您选择哪种 DI 框架,您都可能需要注册),但这可能只是一个附带问题。如果您确实需要异步运行处理程序,至少要尽量减少异步运行的处理程序的数量。
除此之外,您需要的是以下内容。
正如您所指出的,由于您正在异步运行(一些)命令处理程序,因此您不能对它们使用每个网络请求的生活方式。您将需要一个混合解决方案,该解决方案在每个 Web 请求和“其他”之间混合。其他东西很可能是 per lifetime scope .由于几个原因,这些混合解决方案没有内置扩展。首先,它是一个非常奇特的功能,没有多少人需要。其次,你可以将任意两种或三种生活方式混合在一起,这样就几乎是无穷无尽的混合体。最后,自行注册非常(非常)容易。
在 Simple Injector 2 中,Lifestyle
已添加类,它包含一个 CreateHybrid
允许结合任何两种生活方式来创造新生活方式的方法。这是一个例子:
var hybridLifestyle = Lifestyle.CreateHybrid(
() => HttpContext.Current != null,
new WebRequestLifestyle(),
new LifetimeScopeLifestyle());
您可以使用这种混合生活方式来注册工作单元:
container.Register<IUnitOfWork, DiUnitOfWork>(hybridLifestyle);
由于您将工作单元注册为 Per Lifetime Scope,因此您必须为特定线程显式创建和处置 Lifetime Scope。最简单的做法是将其添加到您的 ThreadedCommandHandlerProxy
.这不是最SOLID做事的方式,但这是我向您展示如何做到这一点的最简单方式。
If we had a problem within the command-handler/service-layer and didn't want to save the UnitOfWork, would we simply throw an exception?
典型的做法是抛出异常。这实际上是异常(exception)的一般规则:
If your method can't do what it's name promises it can, throw. ->
命令处理程序应该不知道它是如何执行的上下文,你最不想做的就是区分它是否应该抛出异常。所以 throw 是你最好的选择。然而,当在后台线程上运行时,您最好捕获该异常,因为如果您不捕获它,.NET 将杀死整个 AppDomain。在 Web 应用程序中,这意味着 AppDomain 回收,这意味着您的 Web 应用程序(或至少该服务器)将在短时间内脱机。
另一方面,您也不希望丢失任何异常信息,因此您应该记录该异常,并且可能想要记录带有该异常的该命令的序列化表示,以便您可以查看传递了哪些数据in. 当添加到 ThreadedCommandHandlerProxy.Handle
时方法,它看起来像这样:
public void Handle(TCommand command)
{
string xml = this.commandSerializer.ToXml(command);
Task.Factory.StartNew(() =>
{
var logger =
this.container.GetInstance<ILogger>();
try
{
using (container.BeginTransactionScope())
{
// must be created INSIDE the scope.
var handler = this.instanceCreator();
handler.Handle(command);
}
}
catch (Exception ex)
{
// Don't let the exception bubble up,
// because we run in a background thread.
this.logger.Log(ex, xml);
}
});
}
我警告过异步运行处理程序可能不是最好的主意。但是,由于您正在应用此命令/处理程序模式,您稍后将能够切换到使用队列,而无需更改应用程序中的一行代码。这只是写一些 QueueCommandHandlerDecorator<T>
的问题(它序列化命令并将其发送到队列)并改变组合根中的连接方式,你就可以开始了(当然不要忘记实现从队列中执行命令的服务) .换句话说,这种 SOLID 设计的优点在于,实现这些功能对于应用程序的大小是不变的。
关于c# - 如何配置 Simple Injector 以在 ASP.NET MVC 中运行后台线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11041601/
我试图对 ASP.Net MVC 有一个高层次的理解,我开始意识到它看起来很像原始的 ASP 脚本。过去,我们将“模型”/业务逻辑代码组织到 VBScript 类或 VB COM 组件中。 当然,现在
我已经搜索了一段时间,但似乎找不到答案。 我想在我的旋转木马中显示一个计数器,左边是当前项目(工作),左边是项目总数。 我的代码:
. 最佳答案 Scott Gu 称这些为代码块。这就是我的看法。 http://weblogs.asp.net/scottgu/archive/2010/04/06/new-lt-gt-syntax
我有一个使用 Visual Studio 2010/.net 4/VB 制作的网站。 我真的很喜欢我发现的 FAQ 系统的布局,因为它很简单,但它是经典的 asp。所以,显然,我不能包括我的母版页布局
好吧,对于你们许多人来说,这个问题可能有一个非常明显的答案,但它让我难住了。 我有一个 asp.net Web 表单,上面有两个控件(嗯,不止这两个,但我们将重点关注这些) - 第一个是 asp:dr
当我将 ASP.NET 复选框控件设置为 asp.net 更新面板的异步回发触发器时,EventName 属性是什么? 最佳答案 我相信它是 CheckedChanged。 关于asp.net - a
我有一个用经典 asp 编写的(巨大的)网站。现在我必须切换到 vb.net (razor)。有没有办法将这两个结合起来直到切换完成? 有没有办法让应用程序与经典的 asp 和 vb.net 一起工作
I am creating a products page, where the user selects an option in a radiobuttonlist for example, an
我最近将一个经典的 ASP 应用程序转换为 ASP.NET 3.5,但我觉得我的经典 ASP 版本要快一些(我不知道可能买家会后悔)。 所以你们能帮我解决这个问题吗,让我知道哪个更快,asp、asp.
从本周开始,我被要求开始学习如何使用 ASP 开发网站。我通过 XNA 对 C# 有一定的经验,所以这部分对我来说并不是什么麻烦。 我一直在关注Music Store Tutorial这需要我设置一个
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 8 年前。 Improve this ques
我想将一些表单变量发布到经典 ASP 页面中。我不想改变经典的 ASP 页面,因为需要完成大量的工作,以及消耗它们的页面数量。 经典的 ASP 页面需要将表单变量 Username 和 Userpas
已结束。此问题正在寻求书籍、工具、软件库等的推荐。它不满足Stack Overflow guidelines 。目前不接受答案。 我们不允许提出寻求书籍、工具、软件库等推荐的问题。您可以编辑问题,以便
在某种程度上,这可能是一个异端问题。我们有一个大型站点,其中许多页面仍在ASP中。通常,并没有真正动态的,而是包括(通过SSI或Server.Execute)定期重新生成的HTML块。看起来好像是一个
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 9 年前。 Improve this ques
我有一个遗留的 ASP 应用程序——在不久的某个时候——需要迁移到 ASP.Net 2.0(以与也在 2.0 中的其他应用程序兼容)。 对于这类事情是否有最佳实践,即作为第一步将当前 html、vbs
我目前在一家公司工作,该公司使用 ASP.NET Webforms 和旧 ASP 页面的组合进行 Web 开发。这对于他们当前的项目来说效果很好,但我想说服/建议他们切换到 ASP.NET MVC,因
我有一个经典的 asp 应用程序。我想将该页面的竞赛表格发布到 Asp.Net 表格。原因是我想在进入数据库之前使用我在 Asp.Net 页面中内置的大量逻辑进行验证,而我对 asp 不太了解。更不用
我知道在 ASP.NET MVC 中,您可以拥有移动 View 并执行类似 Index.mobile.cshtml 的操作。和 _Layout.mobile.cshtml并且服务器知道将这些 View
我需要从一些服务器端 c#.net 代码中调用经典 asp 页面上的 VBscript 函数 - 有谁知道一种干净的方法来做到这一点?在 .net 中重写函数不是一种选择。 我会再解释一下这个问题..
我是一名优秀的程序员,十分优秀!