在下面的.Net Framework代码中,确保将someEntity对象插入db中,然后将执行Publish操作。但是,在.Net Core中,我无法做到这一点。当我尝试运行这段代码时,发生平台异常。
using (var transaction = new TransactionScope())
{
SomeEntity someEntity = new SomeEntity();
someEntity.Gui = Guid.NewGuid().ToString();
_dataContext.SomeEntities.Add(someEntity);
_dataContext.SaveChanges();
_backgroundJobClient.Enqueue(() => PublishSomeEntityCreatedEvent(someEntity.Id)));
transaction.Complete();
}
对于这种情况,是否有任何已知的好的解决方案?
注意:.Net Core 2.2控制台应用程序,EntityFrameworkCore 2.1和Hangfire 1.6.21用于测试
更新:整个堆栈跟踪
Hangfire.BackgroundJobClientException: Background job creation failed. See inner exception for details. ---> System.PlatformNotSupportedException: This platform
does not support distributed transactions.
at System.Transactions.Distributed.DistributedTransactionManager.GetDistributedTransactionFromTransmitterPropagationToken(Byte[] propagationToken)
at System.Transactions.TransactionInterop.GetDistributedTransactionFromTransmitterPropagationToken(Byte[] propagationToken)
at System.Transactions.TransactionStatePSPEOperation.PSPEPromote(InternalTransaction tx)
at System.Transactions.TransactionStateDelegatedBase.EnterState(InternalTransaction tx)
at System.Transactions.EnlistableStates.Promote(InternalTransaction tx)
at System.Transactions.Transaction.Promote()
at System.Transactions.TransactionInterop.ConvertToDistributedTransaction(Transaction transaction)
at System.Transactions.TransactionInterop.GetExportCookie(Transaction transaction, Byte[] whereabouts)
at System.Data.SqlClient.SqlInternalConnection.GetTransactionCookie(Transaction transaction, Byte[] whereAbouts)
at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
at System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx)
at System.Data.SqlClient.SqlInternalConnectionTds.Activate(Transaction transaction)
at System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction)
at System.Data.ProviderBase.DbConnectionPool.PrepareConnection(DbConnection owningObject, DbConnectionInternal obj, Transaction transaction)
at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
at System.Data.SqlClient.SqlConnection.Open()
at Hangfire.SqlServer.SqlServerStorage.CreateAndOpenConnection()
at Hangfire.SqlServer.SqlServerStorage.UseConnection[T](DbConnection dedicatedConnection, Func`2 func)
at Hangfire.SqlServer.SqlServerConnection.CreateExpiredJob(Job job, IDictionary`2 parameters, DateTime createdAt, TimeSpan expireIn)
at Hangfire.Client.CoreBackgroundJobFactory.Create(CreateContext context)
at Hangfire.Client.BackgroundJobFactory.<>c__DisplayClass7_0.<CreateWithFilters>b__0()
at Hangfire.Client.BackgroundJobFactory.InvokeClientFilter(IClientFilter filter, CreatingContext preContext, Func`1 continuation)
at Hangfire.Client.BackgroundJobFactory.<>c__DisplayClass7_1.<CreateWithFilters>b__2()
at Hangfire.Client.BackgroundJobFactory.CreateWithFilters(CreateContext context, IEnumerable`1 filters)
at Hangfire.Client.BackgroundJobFactory.Create(CreateContext context)
at Hangfire.BackgroundJobClient.Create(Job job, IState state)
--- End of inner exception stack trace ---
at Hangfire.BackgroundJobClient.Create(Job job, IState state)
at Hangfire.BackgroundJobClientExtensions.Create(IBackgroundJobClient client, Expression`1 methodCall, IState state)
at Hangfire.BackgroundJobClientExtensions.Enqueue(IBackgroundJobClient client, Expression`1 methodCall)
at TopShelf_Hangfire_NetCore.BusinessService.Execute(DateTime utcNow) in C:\Projects\Practices\TopShelf_Hangfire_NetCore\BusinessService.cs:line 31
at TopShelf_Hangfire_NetCore.StartupService._timer_Elapsed(Object sender, ElapsedEventArgs e) in C:\Projects\Practices\TopShelf_Hangfire_NetCore\StartupService.cs:line 35
答案 0 :(得分:0)
.net内核似乎不支持分布式事务的启动,而abd不受支持。
由于您要访问多个资源管理器(您的数据库和hangfire的数据库),所以transactionscope尝试升级要分配的事务。
您可以将hangfire的_backgroundJobClient.Enqueue()
排除在范围之外,这样就不会升级。
您将必须找到另一种方法来确保两个操作都被执行(db更新,hangfire入队)
编辑: 由于您无法进行事务处理,因此必须设计服务以处理可能的故障情况。例如: 帐户服务将:
将创建的内容持久保存到数据库
然后调用hangfire Enqueue
记录以下事实:hangfire作业是在同一用户db中创建的。
用户服务必须轮询数据库,以查看是否创建了用户,但未记录通知。
其他微服务应该能够处理重复的通知。
这样,如果创建了用户但未发送通知,则您的服务将重新发送(4)。
如果失败发生在(2)和(3)之间,则接收服务将忽略重复的请求
答案 1 :(得分:0)
在使用EntityFramework Core 3.0或更高版本时,此功能现在可以使用。 这在EFCore 2.x中不起作用的原因是EFCore 2.x在不使用时未关闭连接。相反,DbConnection保持打开状态,直到上下文被处理为止。
从3.0开始,EF Core在使用完连接后立即关闭连接。 这样可以实现您的方案,即您希望在同一事务中加入EfCore和Hangfire,而无需升级到MSDTC。