Nel codice .Net Framework che è sotto, è assicurato che un oggetto'Entity viene inserito in db e l'operatività di pubblicazione verrà eseguita dopo. Tuttavia, in. Net Core non riuscivo a farlo. Quando provo a eseguire questo pezzo di codice, si verifica Platform Exception.
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();
}
C'è qualche buona soluzione nota per questa situazione?
Nota: l'applicazione della console .NET Core 2.2, EntityFrameworkCore 2.1 e Hangfire 1.6.21 vengono utilizzati per il test
Aggiornamento: intero stacktrace
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
Ora funziona quando si utilizza EntityFramework Core 3.0 o versioni successive. Il motivo per cui non funzionava in EFCore 2.x era che EFCore 2.x non chiudeva la connessione quando non veniva utilizzata. Invece DbConnection è stato tenuto aperto fino a quando non è stato eliminato il contesto.
Questo comportamento è cambiato in EF Core 3.0: https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaking-changes#database-connection-is-now -closed-se-non-usato-più-prima-del-TransactionScope-è-stato-completata
A partire da 3.0, EF Core chiude la connessione non appena viene utilizzata. Ciò consente il tuo scenario, in cui desideri arruolare EfCore e Hangfire nella stessa transazione, senza passare a MSDTC.