[英]Hangfire Transactional Process (Unit of Work)
在下面的 .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 Console 應用,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
這現在在使用 EntityFramework Core 3.0 或更高版本時有效。 這在 EFCore 2.x 中不起作用的原因是 EFCore 2.x 在不使用時沒有關閉連接。 相反,DbConnection 一直保持打開狀態,直到上下文被處理。
這種行為在 EF Core 3.0 中發生了變化: https : //docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/break-changes#database-connection-is-now -關閉如果未使用-在交易范圍已經完成之前
從 3.0 開始,EF Core 會在使用完畢后立即關閉連接。 這支持您的方案,您希望在同一事務中登記 EfCore 和 Hangfire,而不升級到 MSDTC。
看起來正在啟動 distrubutrd 事務,在 .net core 下不支持 abd。
由於您正在訪問多個資源管理器(您的數據庫和 hangfire 的數據庫),因此事務范圍會嘗試升級要分發的事務。
您可以將_backgroundJobClient.Enqueue()
的_backgroundJobClient.Enqueue()
移出 scope ,因此不會發生升級。
您必須找到另一種方法來確保執行這兩個操作(數據庫更新、掛火入隊)
編輯:由於您不能進行交易,因此您必須設計您的服務來處理可能的故障情況。 例如:帳戶服務將:
將創建的用於數據庫的持久化
然后調用hangfire Enqueue
記錄 hangfire 作業是在同一個用戶數據庫中創建的事實。
用戶服務必須輪詢數據庫,以查看是否創建了用戶但未記錄通知。
其他微服務應該能夠處理重復通知。
這樣,如果創建了用戶但未發送通知,您的服務將重新發送 (4)。
如果失敗發生在 (2) 和 (3) 之間,接收服務將忽略重復的請求
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.