Quando abbiamo bisogno di fare l'accesso al database nella nostra applicazione, usiamo i seguenti modelli:SqlConnection ed evitando la promozione in MSDTC
- Per l'esecuzione di query, abbiamo una classe factory statico con un metodo
CreateOpenConnection
, che non fa altro chenew SqlConnection(myConnectionString)
e chiamaOpen()
su di esso. Questo metodo viene chiamato prima di eseguire una query e la connessione viene eliminata dopo il ritorno della query. - Per inserti/aggiornamenti/elimina usiamo un'unità di modello di lavoro dove i cambiamenti vengono dosati e sottoposte al database con una chiamata a
work.Commit()
in questo modo:
work.Commit:
using (var tranScope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
using (var conn = DapperFactory.CreateOpenConnection())
{
var count = _changeTracker.CommitChanges(conn);
tranScope.Complete();
return count;
}
}
Questo sembra funzionare perfettamente per l'utilizzo generale come parte di un servizio web, ma attualmente mi sta dando problemi MSDTC quando provo a utilizzarlo in combinazione con Rebus.
Da quello che posso dire, Rebus (quando gestisce un messaggio in coda) crea un nuovo TransactionScope
in modo che in caso di un errore nel gestire il messaggio, è possibile eseguire il rollback. Ora, questo di per sé ha funzionato bene finora. Posso aprire un nuovo SqlConnection
all'interno di un gestore di messaggi di Rebus senza problemi (tuttavia, utilizzando le query legacy Entity Framework e manuale SqlConnections all'interno dello stesso Rebus TransactionScope
non funziona, ma non considero un problema al momento) . Ma ieri ho chiesto alla seguente domanda:
Serial processing of a certain message type in Rebus
Al che la risposta sembra essere quello di utilizzare la funzione saga di Rebus. Ho provato a implementarlo e configurato in modo tale che la saga di Rebus venga mantenuta su un nuovo database SQL Server (con una stringa di connessione distinta). Presumibilmente, usando che la persistenza di SQL Server apre una SqlConnection
propria, perché ogni volta che cerco di creare un SqlConnection
ora, ottengo la seguente eccezione:
Accesso alla rete per Distributed Transaction Manager (MSDTC) è stato disabilitato. Abilitare DTC per l'accesso alla rete nella configurazione di sicurezza per MSDTC utilizzando lo strumento di amministrazione dei servizi componenti.
Abilitazione MSDTC è qualcosa che vorrei molto, molto piace molto per evitare di fare, per quanto riguarda la configurazione e sovraccarico delle prestazioni. E potrei sbagliarmi, ma anche non sembra necessario.
Quello che presumo sta succedendo qui è che Rebus crea un ambiente TransactionScope
e che lo SqlConnection
crea arresti a tale scopo. E quando provo a creare il mio SqlConnection
, tenta anche di arruolarmi in tale ambito ambientale e poiché sono coinvolte più connessioni, esso viene promosso a MSDTC, che fallisce.
Ho un'idea su come risolvere questo problema, ma non so se è la cosa giusta da fare. Quello che vorrei fare è:
- Aggiungere
Enlist=false
alla stringa di connessione della mia applicazione in modo che non si arresti mai alle transazioni ambientali. - Modificare il metodo
Commit
in modo che non crei un nuovoTransactionScope
(che la mia connessione non sottoscriverà più perché ho appena detto che non dovrebbe) ma che utilizzaconn.BeginTransaction
.
Come così:
var transaction = conn.BeginTransaction();
try
{
var count = _changeTracker.CommitChanges(conn);
transaction.Commit();
return count;
}
catch
{
transaction.Rollback();
throw;
}
finally
{
transaction.Dispose();
}
io non sono solo sicuro se questo è l'approccio giusto e ciò che i possibili svantaggi sono.
Qualche consiglio?
AGGIORNAMENTO: Per chiarire, non è la work.Commit()
che mi sta dando problemi, sono abbastanza sicuro che avrebbe funzionato, ma non ho mai arrivare lì perché il mio l'interrogazione è quello che non riesce.
Un esempio di ciò che non riesce:
public int? GetWarehouseID(int appID)
{
var query = @"
select top 1 ID from OrganizationUnits o
where TypeID & 16 = 16 /* warehouse */";
using (var conn = _dapper.OpenConnection())
{
var id = conn.Query<int?>(query).FirstOrDefault();
return id;
}
}
Questo viene chiamato quando un TransactionScope
è stata creata da Rebus, così come dopo un SqlConnection
viene aperto da Rebus. All'apertura miaSqlConnection
, tenta di arruolarsi e va in crash
Se si utilizza "Enlist = false", sicuramente ciò renderebbe il vostro TransactionScope inutile? perché la connessione * non ci sarà * –
Allo stesso modo, il tuo codice 'BeginTransaction' * non sta usando la transazione * - una transazione ADO.NET deve essere specificata esplicitamente sul comando, quindi devi passare 'transazione' in' CommitChanges', sicuramente? –
Puoi chiarire esattamente quale versione di SQL Server stai usando? –