我正在构建一个Asp.Net MVC应用程序。我使用Entity Framework Core进行数据库访问。我正在使用SQLServer作为数据库。所有EF Stuff都可以在控制器方法中正常工作。
我还有一个相关的NServiceBus服务应用程序,可以处理不需要在Web服务器上运行的东西。常见的事情之一是在超时后执行任务。 (例如,用户在网站上做某事,30分钟后做某事。)
我正在使用SQL Transport进行服务总线设置,并使用与Entity Framework数据相同的数据库。
由于Web服务器不需要处理NServiceBus消息,因此它具有向IOC容器注册的Send Only IEndpointInstance。需要发送NServiceBus消息的控制器获取IEndpointInstance并使用它发送。
现在表面上看,这一切似乎都工作正常,控制器处理客户端请求,使用EF来改变数据库,并使用端点发送NServiceBus消息,但是存在问题。虽然EF和NServiceBus都使用相同的SQL数据库来存储数据和消息,但这些任务不属于同一个数据库事务。这意味着存在边缘情况,如果出现问题,EF将保存更改保存到数据库,但是NServiceBus消息的发送可能无法完成,现在数据和消息处于不一致状态。
所以问题是,如何让IEndpointInstance在与EF DbContext Save相同的事务中发送?
我发现在配置EndPoint Transport时,我可以使用UseCustomSqlConnectionFactory扩展方法。我已经能够提供一个工厂,它从IOC容器中获取Web请求范围中使用的DbContext,并提取其SqlConnection并将其提供给端点。这里的问题是IEndpointInstance.Send返回的任务在我.Wait()完成发送时永远不会返回。
虽然有大量关于使用EF共享IMessageHandlerContext中包含的事务的文档和示例,但我找不到任何有关让IEndpointInstance与EF共享事务的信息。
答案 0 :(得分:2)
你当然可以这样做。您可以在DbContext之上实现UnitOfWork,并将其与消息处理程序管道集成。这确保了如果您有多个处理程序,他们将全部参与同一事务。这种方法适用于仅发送端点以及发送和接收(给定底层传输支持),尽管您需要更改行为以在不同时间执行仅发送端点(如显然,对于仅发送端点,没有“传入”消息。
您这样做的方法是在收到消息时将行为注入消息处理管道:
public class UnitOfWorkSetupBehaviorBehavior : Behavior<IIncomingLogicalMessageContext>
{
public override async Task Invoke(IIncomingLogicalMessageContext context, Func<Task> next)
{
var uow = new EntityFrameworkUnitOfWork();
context.Extensions.Set(uow);
await next().ConfigureAwait(false); //executes the next op in the chain
context.Extensions.Remove<EntityFrameworkUnitOfWork>();
}
}
实际的工作单元实现方式如下(注意它使用环境事务):
class EntityFrameworkUnitOfWork
{
MyDataContext context;
public MyDataContext GetDataContext(SynchronizedStorageSession storageSession)
{
if (context == null)
{
var dbConnection = storageSession.Session().Connection;
context = new MyDataContext (dbConnection);
//Don't use transaction because connection is enlisted in the TransactionScope
context.Database.UseTransaction(null);
//Call SaveChanges before completing storage session
storageSession.OnSaveChanges(x => context.SaveChangesAsync());
}
return context;
}
}
如果这一切看起来太令人生畏,那么您可以在文档网站上download找到一个很棒的样本。