Dalam proyek kami, kami menggunakan TransactionScope untuk memastikan lapisan akses data kami melakukan tindakannya dalam transaksi. Kami bertujuan untuk tidak mengharuskan layanan MSDTC diaktifkan di mesin pengguna akhir kami.
Masalahnya adalah, pada setengah dari mesin pengembang kami, kami dapat berjalan dengan MSDTC dinonaktifkan. Setengah lainnya harus diaktifkan atau mereka mendapatkan pesan kesalahan "MSDTC pada [SERVER] tidak tersedia" .
Ini benar-benar membuat saya menggaruk-garuk kepala dan membuat saya serius mempertimbangkan untuk kembali ke solusi seperti TransactionScope home-spun berdasarkan objek transaksi ADO.NET. Ini tampaknya gila - kode yang sama bahwa karya (dan tidak meningkat) pada setengah dari pengembang kami yang melakukan Meningkat pada pengembang lain.
Saya berharap untuk jawaban yang lebih baik untuk Trace mengapa transaksi meningkat ke DTC tetapi sayangnya tidak.
Berikut adalah sedikit contoh kode yang akan menyebabkan masalah, pada mesin yang mencoba untuk meningkatkan, ia mencoba untuk meningkat pada koneksi kedua. Buka () (dan ya, tidak ada koneksi lain yang terbuka pada saat itu.)
using (TransactionScope transactionScope = new TransactionScope() {
using (SqlConnection connection = new SqlConnection(_ConStr)) {
using (SqlCommand command = connection.CreateCommand()) {
// prep the command
connection.Open();
using (SqlDataReader reader = command.ExecuteReader()) {
// use the reader
connection.Close();
}
}
}
// Do other stuff here that may or may not involve enlisting
// in the ambient transaction
using (SqlConnection connection = new SqlConnection(_ConStr)) {
using (SqlCommand command = connection.CreateCommand()) {
// prep the command
connection.Open(); // Throws "MSDTC on [SERVER] is unavailable" on some...
// gets here on only half of the developer machines.
}
connection.Close();
}
transactionScope.Complete();
}
Kami benar-benar menggali dan mencoba mencari tahu ini. Berikut ini beberapa info tentang mesin yang bekerja:
- Pengembang 1: Windows 7 x64 SQL2008
- Pengembang 2: Windows 7 x86 SQL2008
- Pengembang 3: Windows 7 x64
SQL2005SQL2008
Pengembang yang tidak berfungsi:
- Dev 4: Windows 7 x64,
SQL2008SQL2005 - Dev 5: Windows Vista x86, SQL2005
- Dev 6: Windows XP X86, SQL2005
- PC Rumah Saya: Windows Vista Home Premium, x86, SQL2005
Saya harus menambahkan bahwa semua mesin, dalam upaya untuk mencari masalah, telah sepenuhnya ditambal dengan semua yang tersedia dari Pembaruan Microsoft.
Pembaruan 1:
- http://social.msdn.microsoft.com/forums/en-US/windowstransactionsprogramming/thread/a5462509-8d6d-4828-aefa-a197456081d3/ menjelaskan masalah yang sama ... kembali pada tahun 2006!
- http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28VS.80%29.aspx - membaca contoh kode itu, itu jelas menunjukkan koneksi bersarang-detik (ke server SQL kedua, sebenarnya) yang akan meningkat ke DTC. Kami tidak melakukan ini dalam kode kami - kami tidak menggunakan server SQL yang berbeda, atau string koneksi yang berbeda, kami juga tidak membuka sambungan koneksi sekunder - tidak boleh ada eskalasi ke DTC .
- http://davidhayden.com/blog/dave/archive/2005/12/09/2615.aspx (dari 2005) berbicara tentang bagaimana eskalasi ke DTC akan selalu terjadi ketika terhubung ke SQL2000. Kami menggunakan SQL2005 / 2008
- http://msdn.microsoft.com/en-us/library/ms229978.aspx MSDN tentang eskalasi transaksi.
Halaman eskalasi transaksi MSDN itu menyatakan bahwa kondisi berikut ini akan menyebabkan transaksi meningkat ke DTC:
- Setidaknya satu sumber daya tahan lama yang tidak mendukung pemberitahuan satu fase terdaftar dalam transaksi.
- Setidaknya dua sumber daya tahan lama yang mendukung notifikasi satu fase terdaftar dalam transaksi. Misalnya, mendaftar satu koneksi dengan tidak menyebabkan transaksi dipromosikan. Namun, setiap kali Anda membuka koneksi kedua ke database yang menyebabkan database terdaftar, infrastruktur System.Transactions mendeteksi bahwa itu adalah sumber daya tahan lama kedua dalam transaksi, dan meningkatkannya ke transaksi MSDTC.
- Permintaan untuk "marshal" transaksi ke domain aplikasi yang berbeda atau proses yang berbeda dipanggil. Misalnya, serialisasi objek transaksi melintasi batas domain aplikasi. Objek transaksi adalah marshaled-by-value, yang berarti bahwa setiap upaya untuk melewati batas domain aplikasi (bahkan dalam proses yang sama) menghasilkan serialisasi objek transaksi. Anda dapat melewati objek transaksi dengan melakukan panggilan pada metode jarak jauh yang mengambil Transaksi sebagai parameter atau Anda dapat mencoba mengakses komponen layanan transaksional jarak jauh. Ini membuat serial objek objek dan menghasilkan eskalasi, seperti ketika transaksi serial di seluruh domain aplikasi. Ini didistribusikan dan manajer transaksi lokal tidak lagi memadai.
Kami tidak mengalami # 3. # 2 tidak terjadi karena hanya ada satu koneksi pada satu waktu, dan itu juga untuk satu 'sumber daya tahan lama'. Apakah ada cara # 1 bisa terjadi? Beberapa konfigurasi SQL2005 / 8 yang menyebabkannya tidak mendukung pemberitahuan satu fase?
Pembaruan 2:
Diselidiki ulang, secara pribadi, semua versi SQL Server - "Dev 3" sebenarnya memiliki SQL2008, dan "Dev 4" sebenarnya adalah SQL2005. Itu akan mengajarkan saya untuk tidak pernah mempercayai rekan kerja saya lagi. ;) Karena perubahan data ini, saya cukup yakin kami telah menemukan masalah kami. Pengembang SQL2008 kami tidak mengalami masalah karena SQL2008 memiliki jumlah yang sangat banyak termasuk yang tidak dimiliki SQL2005.
Ini juga memberi tahu saya bahwa karena kita akan mendukung SQL2005 sehingga kita tidak dapat menggunakan TransactionScope seperti sebelumnya, dan jika kita ingin menggunakan TransactionScope kita harus melewati objek SqlConnection tunggal di sekitar ... yang tampaknya bermasalah dalam situasi di mana SqlConnection tidak dapat dengan mudah diteruskan ... hanya berbau instance global-SqlConnection. Bangku gereja!
Perbarui 3
Hanya untuk mengklarifikasi di sini dalam pertanyaan:
SQL2008:
- Mengizinkan beberapa koneksi dalam satu TransactionScope tunggal (seperti yang ditunjukkan dalam kode contoh di atas.)
- Peringatan # 1: Jika beberapa SqlConnections bersarang, yaitu, dua atau lebih SqlConnections dibuka pada saat yang sama, TransactionScope akan segera meningkat ke DTC.
- Peringatan # 2: Jika SqlConnection tambahan dibuka untuk 'sumber daya tahan lama' yang berbeda (yaitu: SQL Server yang berbeda,) itu akan segera meningkat ke DTC
SQL2005:
- Tidak mengizinkan banyak koneksi dalam satu TransactionScope, titik. Ini akan meningkat ketika / jika SqlConnection kedua dibuka.
Perbarui 4
Dalam kepentingan membuat pertanyaan ini bahkan lebih berantakan berguna, dan hanya demi kejelasan lebih lanjut, berikut adalah bagaimana Anda bisa mendapatkan SQL2005 meningkat ke DTC dengan tunggal SqlConnection
:
using (TransactionScope transactionScope = new TransactionScope()) {
using (SqlConnection connection = new SqlConnection(connectionString)) {
connection.Open();
connection.Close();
connection.Open(); // escalates to DTC
}
}
Ini sepertinya rusak bagi saya, tapi saya rasa saya bisa mengerti jika setiap panggilan masuk SqlConnection.Open()
diambil dari pool koneksi.
"Tapi mengapa ini bisa terjadi?" Nah, jika Anda menggunakan SqlTableAdapter terhadap koneksi itu sebelum dibuka, SqlTableAdapter akan membuka dan menutup koneksi, secara efektif menyelesaikan transaksi untuk Anda karena Anda sekarang tidak dapat membukanya kembali.
Jadi, pada dasarnya, agar berhasil menggunakan TransactionScope dengan SQL2005 Anda harus memiliki semacam objek koneksi global yang tetap terbuka dari titik TransactionScope pertama instantiated sampai tidak lagi diperlukan. Selain bau kode dari objek koneksi global, membuka koneksi terlebih dahulu dan menutupnya terakhir bertentangan dengan logika membuka koneksi selambat mungkin dan menutupnya sesegera mungkin.