Bagikan melalui


Tutorial: Menggunakan ketahanan koneksi dan intersepsi perintah dengan Kerangka Kerja Entitas dalam aplikasi MVC ASP.NET

Sejauh ini aplikasi telah berjalan secara lokal di IIS Express di komputer pengembangan Anda. Untuk membuat aplikasi nyata tersedia bagi orang lain untuk digunakan melalui Internet, Anda harus menyebarkannya ke penyedia hosting web, dan Anda harus menyebarkan database ke server database.

Dalam tutorial ini Anda akan mempelajari cara menggunakan ketahanan koneksi dan intersepsi perintah. Mereka adalah dua fitur penting dari Entity Framework 6 yang sangat berharga ketika Anda menyebarkan ke lingkungan cloud: ketahanan koneksi (coba lagi otomatis untuk kesalahan sementara) dan intersepsi perintah (tangkap semua kueri SQL yang dikirim ke database untuk mencatat atau mengubahnya).

Tutorial ketahanan koneksi dan intersepsi perintah ini bersifat opsional. Jika Anda melewati tutorial ini, beberapa penyesuaian kecil harus dilakukan dalam tutorial berikutnya.

Di tutorial ini, Anda akan:

  • Aktifkan ketahanan koneksi
  • Aktifkan intersepsi perintah
  • Uji konfigurasi baru

Prasyarat

Aktifkan ketahanan koneksi

Saat Anda menyebarkan aplikasi ke Windows Azure, Anda akan menyebarkan database ke Windows Azure SQL Database, layanan database cloud. Kesalahan koneksi sementara biasanya lebih sering terjadi saat Anda tersambung ke layanan database cloud daripada ketika server web dan server database Anda terhubung langsung bersama-sama di pusat data yang sama. Bahkan jika server web cloud dan layanan database cloud dihosting di pusat data yang sama, ada lebih banyak koneksi jaringan di antara mereka yang dapat mengalami masalah, seperti load balancer.

Layanan cloud juga biasanya dibagikan oleh pengguna lain, yang berarti responsnya dapat dipengaruhi oleh mereka. Dan akses Anda ke database mungkin tunduk pada pembatasan. Pembatasan berarti layanan database melempar pengecualian ketika Anda mencoba mengaksesnya lebih sering daripada yang diizinkan dalam Perjanjian Tingkat Layanan (SLA) Anda.

Banyak atau sebagian besar masalah koneksi saat Anda mengakses layanan awan bersifat sementara, yaitu, mereka menyelesaikannya sendiri dalam waktu singkat. Jadi ketika Anda mencoba operasi database dan mendapatkan jenis kesalahan yang biasanya sementara, Anda dapat mencoba operasi lagi setelah menunggu sebentar, dan operasi mungkin berhasil. Anda dapat memberikan pengalaman yang jauh lebih baik bagi pengguna Anda jika Anda menangani kesalahan sementara dengan secara otomatis mencoba lagi, membuat sebagian besar tidak terlihat oleh pelanggan. Fitur ketahanan koneksi di Entity Framework 6 mengotomatiskan proses mencoba kembali kueri SQL yang gagal.

Fitur ketahanan koneksi harus dikonfigurasi dengan tepat untuk layanan database tertentu:

  • Ini harus tahu pengecualian mana yang cenderung sementara. Anda ingin mencoba kembali kesalahan yang disebabkan oleh hilangnya konektivitas jaringan sementara, bukan kesalahan yang disebabkan oleh bug program, misalnya.
  • Ini harus menunggu jumlah waktu yang sesuai antara percobaan kembali operasi yang gagal. Anda dapat menunggu lebih lama di antara percobaan ulang untuk proses batch daripada yang Anda bisa untuk halaman web online di mana pengguna sedang menunggu respons.
  • Ini harus mencoba kembali berapa kali sebelum menyerah. Anda mungkin ingin mencoba lagi lebih banyak kali dalam proses batch yang Anda lakukan dalam aplikasi online.

Anda dapat mengonfigurasi pengaturan ini secara manual untuk lingkungan database apa pun yang didukung oleh penyedia Kerangka Kerja Entitas, tetapi nilai default yang biasanya berfungsi dengan baik untuk aplikasi online yang menggunakan Windows Azure SQL Database telah dikonfigurasi untuk Anda, dan itu adalah pengaturan yang akan Anda terapkan untuk aplikasi Contoso University.

Yang harus Anda lakukan untuk mengaktifkan ketahanan koneksi adalah membuat kelas di rakitan Anda yang berasal dari kelas DbConfiguration, dan di kelas tersebut mengatur strategi eksekusi SQL Database, yang dalam EF adalah istilah lain untuk kebijakan coba lagi.

  1. Di folder DAL, tambahkan file kelas bernama SchoolConfiguration.cs.

  2. Ganti kode templat dengan kode berikut:

    using System.Data.Entity;
    using System.Data.Entity.SqlServer;
    
    namespace ContosoUniversity.DAL
    {
        public class SchoolConfiguration : DbConfiguration
        {
            public SchoolConfiguration()
            {
                SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
            }
        }
    }
    

    Kerangka Kerja Entitas secara otomatis menjalankan kode yang ditemukannya di kelas yang berasal dari DbConfiguration. Anda dapat menggunakan DbConfiguration kelas untuk melakukan tugas konfigurasi dalam kode yang akan Anda lakukan dalam file Web.config . Untuk informasi selengkapnya, lihat Konfigurasi Code-Based EntityFramework.

  3. Di StudentController.cs, tambahkan using pernyataan untuk System.Data.Entity.Infrastructure.

    using System.Data.Entity.Infrastructure;
    
  4. Ubah semua catch blok yang menangkap DataException pengecualian sehingga mereka menangkap RetryLimitExceededException pengecualian sebagai gantinya. Contohnya:

    catch (RetryLimitExceededException /* dex */)
    {
        //Log the error (uncomment dex variable name and add a line here to write a log.
        ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
    }
    

    Anda menggunakan DataException untuk mencoba mengidentifikasi kesalahan yang mungkin sementara untuk memberikan pesan "coba lagi" yang ramah. Tetapi sekarang setelah Anda mengaktifkan kebijakan coba lagi, satu-satunya kesalahan yang mungkin bersifat sementara sudah dicoba dan gagal beberapa kali dan pengecualian aktual yang dikembalikan akan dibungkus dalam RetryLimitExceededException pengecualian.

Untuk informasi selengkapnya, lihat Ketahanan Koneksi Kerangka Kerja Entitas/Logika Coba Lagi.

Aktifkan intersepsi perintah

Sekarang setelah Anda mengaktifkan kebijakan coba lagi, bagaimana Anda menguji untuk memverifikasi bahwa kebijakan berfungsi seperti yang diharapkan? Tidak mudah untuk memaksa kesalahan sementara terjadi, terutama ketika Anda menjalankan secara lokal, dan akan sangat sulit untuk mengintegrasikan kesalahan sementara aktual ke dalam pengujian unit otomatis. Untuk menguji fitur ketahanan koneksi, Anda memerlukan cara untuk mencegat kueri yang dikirim Oleh Kerangka Kerja Entitas ke SQL Server dan mengganti respons SQL Server dengan jenis pengecualian yang biasanya sementara.

Anda juga dapat menggunakan intersepsi kueri untuk menerapkan praktik terbaik untuk aplikasi cloud: mencatat latensi dan keberhasilan atau kegagalan semua panggilan ke layanan eksternal seperti layanan database. EF6 menyediakan API pengelogan khusus yang dapat memudahkan untuk melakukan pengelogan, tetapi di bagian tutorial ini Anda akan mempelajari cara menggunakan fitur intersepsi Kerangka Kerja Entitas secara langsung, baik untuk pengelogan maupun untuk mensimulasikan kesalahan sementara.

Membuat antarmuka dan kelas pengelogan

Praktik terbaik untuk pengelogan adalah melakukannya dengan menggunakan antarmuka daripada panggilan hard-coding ke System.Diagnostics.Trace atau kelas pengelogan. Itu membuatnya lebih mudah untuk mengubah mekanisme pengelogan Anda nanti jika Anda perlu melakukannya. Jadi di bagian ini Anda akan membuat antarmuka pengelogan dan kelas untuk mengimplementasikannya./p>

  1. Buat folder dalam proyek dan beri nama Pengelogan.

  2. Di folder Pengelogan , buat file kelas bernama ILogger.cs, dan ganti kode templat dengan kode berikut:

    using System;
    
    namespace ContosoUniversity.Logging
    {
        public interface ILogger
        {
            void Information(string message);
            void Information(string fmt, params object[] vars);
            void Information(Exception exception, string fmt, params object[] vars);
    
            void Warning(string message);
            void Warning(string fmt, params object[] vars);
            void Warning(Exception exception, string fmt, params object[] vars);
    
            void Error(string message);
            void Error(string fmt, params object[] vars);
            void Error(Exception exception, string fmt, params object[] vars);
    
            void TraceApi(string componentName, string method, TimeSpan timespan);
            void TraceApi(string componentName, string method, TimeSpan timespan, string properties);
            void TraceApi(string componentName, string method, TimeSpan timespan, string fmt, params object[] vars);
        }
    }
    

    Antarmuka menyediakan tiga tingkat pelacakan untuk menunjukkan kepentingan relatif log, dan satu yang dirancang untuk memberikan informasi latensi untuk panggilan layanan eksternal seperti kueri database. Metode pengelogan memiliki kelebihan beban yang memungkinkan Anda melewati pengecualian. Ini agar informasi pengecualian termasuk jejak tumpukan dan pengecualian dalam dicatat dengan andal oleh kelas yang mengimplementasikan antarmuka, alih-alih mengandalkan yang dilakukan dalam setiap panggilan metode pengelogan di seluruh aplikasi.

    Metode TraceApi memungkinkan Anda melacak latensi setiap panggilan ke layanan eksternal seperti SQL Database.

  3. Di folder Pengelogan , buat file kelas bernama Logger.cs, dan ganti kode templat dengan kode berikut:

    using System;
    using System.Diagnostics;
    using System.Text;
    
    namespace ContosoUniversity.Logging
    {
        public class Logger : ILogger
        {
            public void Information(string message)
            {
                Trace.TraceInformation(message);
            }
    
            public void Information(string fmt, params object[] vars)
            {
                Trace.TraceInformation(fmt, vars);
            }
    
            public void Information(Exception exception, string fmt, params object[] vars)
            {
                Trace.TraceInformation(FormatExceptionMessage(exception, fmt, vars));
            }
    
            public void Warning(string message)
            {
                Trace.TraceWarning(message);
            }
    
            public void Warning(string fmt, params object[] vars)
            {
                Trace.TraceWarning(fmt, vars);
            }
    
            public void Warning(Exception exception, string fmt, params object[] vars)
            {
                Trace.TraceWarning(FormatExceptionMessage(exception, fmt, vars));
            }
    
            public void Error(string message)
            {
                Trace.TraceError(message);
            }
    
            public void Error(string fmt, params object[] vars)
            {
                Trace.TraceError(fmt, vars);
            }
    
            public void Error(Exception exception, string fmt, params object[] vars)
            {
                Trace.TraceError(FormatExceptionMessage(exception, fmt, vars));
            }
    
            public void TraceApi(string componentName, string method, TimeSpan timespan)
            {
                TraceApi(componentName, method, timespan, ""); 
            }
    
            public void TraceApi(string componentName, string method, TimeSpan timespan, string fmt, params object[] vars)
            {
                TraceApi(componentName, method, timespan, string.Format(fmt, vars));
            }
            public void TraceApi(string componentName, string method, TimeSpan timespan, string properties)
            {
                string message = String.Concat("Component:", componentName, ";Method:", method, ";Timespan:", timespan.ToString(), ";Properties:", properties);
                Trace.TraceInformation(message);
            }
    
            private static string FormatExceptionMessage(Exception exception, string fmt, object[] vars)
            {
                // Simple exception formatting: for a more comprehensive version see 
                // https://code.msdn.microsoft.com/windowsazure/Fix-It-app-for-Building-cdd80df4
                var sb = new StringBuilder();
                sb.Append(string.Format(fmt, vars));
                sb.Append(" Exception: ");
                sb.Append(exception.ToString());
                return  sb.ToString();
            }
        }
    }
    

    Implementasi menggunakan System.Diagnostics untuk melakukan pelacakan. Ini adalah fitur bawaan .NET yang memudahkan untuk menghasilkan dan menggunakan informasi pelacakan. Ada banyak "pendengar" yang dapat Anda gunakan dengan pelacakan System.Diagnostics, untuk menulis log ke file, misalnya, atau menulisnya ke penyimpanan blob di Azure. Lihat beberapa opsi, dan tautan ke sumber daya lain untuk informasi selengkapnya, di Pemecahan Masalah Situs Web Azure di Visual Studio. Untuk tutorial ini, Anda hanya akan melihat log di jendela Output Visual Studio.

    Dalam aplikasi produksi, Anda mungkin ingin mempertimbangkan paket pelacakan selain System.Diagnostics, dan antarmuka ILogger membuatnya relatif mudah untuk beralih ke mekanisme pelacakan yang berbeda jika Anda memutuskan untuk melakukannya.

Membuat kelas pencegat

Selanjutnya Anda akan membuat kelas yang akan dipanggil Oleh Kerangka Kerja Entitas setiap kali akan mengirim kueri ke database, satu untuk mensimulasikan kesalahan sementara dan satu untuk melakukan pengelogan. Kelas pencegat ini harus berasal dari DbCommandInterceptor kelas . Di dalamnya Anda menulis penimpaan metode yang secara otomatis dipanggil saat kueri akan dijalankan. Dalam metode ini Anda dapat memeriksa atau mencatat kueri yang sedang dikirim ke database, dan Anda bisa mengubah kueri sebelum dikirim ke database atau mengembalikan sesuatu ke Kerangka Kerja Entitas sendiri tanpa meneruskan kueri ke database.

  1. Untuk membuat kelas pencegat yang akan mencatat setiap kueri SQL yang dikirim ke database, buat file kelas bernama SchoolInterceptorLogging.cs di folder DAL , dan ganti kode templat dengan kode berikut:

    using System;
    using System.Data.Common;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure.Interception;
    using System.Data.Entity.SqlServer;
    using System.Data.SqlClient;
    using System.Diagnostics;
    using System.Reflection;
    using System.Linq;
    using ContosoUniversity.Logging;
    
    namespace ContosoUniversity.DAL
    {
        public class SchoolInterceptorLogging : DbCommandInterceptor
        {
            private ILogger _logger = new Logger();
            private readonly Stopwatch _stopwatch = new Stopwatch();
    
            public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
            {
                base.ScalarExecuting(command, interceptionContext);
                _stopwatch.Restart();
            }
    
            public override void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
            {
                _stopwatch.Stop();
                if (interceptionContext.Exception != null)
                {
                    _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText);
                }
                else
                {
                    _logger.TraceApi("SQL Database", "SchoolInterceptor.ScalarExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText);
                }
                base.ScalarExecuted(command, interceptionContext);
            }
    
            public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
            {
                base.NonQueryExecuting(command, interceptionContext);
                _stopwatch.Restart();
            }
    
            public override void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
            {
                _stopwatch.Stop();
                if (interceptionContext.Exception != null)
                {
                    _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText);
                }
                else
                {
                    _logger.TraceApi("SQL Database", "SchoolInterceptor.NonQueryExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText);
                }
                base.NonQueryExecuted(command, interceptionContext);
            }
    
            public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
            {
                base.ReaderExecuting(command, interceptionContext);
                _stopwatch.Restart();
            }
            public override void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
            {
                _stopwatch.Stop();
                if (interceptionContext.Exception != null)
                {
                    _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText);
                }
                else
                {
                    _logger.TraceApi("SQL Database", "SchoolInterceptor.ReaderExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText);
                }
                base.ReaderExecuted(command, interceptionContext);
            }
        }
    }
    

    Untuk kueri atau perintah yang berhasil, kode ini menulis log Informasi dengan informasi latensi. Untuk pengecualian, ia membuat log Kesalahan.

  2. Untuk membuat kelas pencegat yang akan menghasilkan kesalahan sementara dummy saat Anda memasukkan "Lempar" di kotak Pencarian , buat file kelas bernama SchoolInterceptorTransientErrors.cs di folder DAL , dan ganti kode templat dengan kode berikut:

    using System;
    using System.Data.Common;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure.Interception;
    using System.Data.Entity.SqlServer;
    using System.Data.SqlClient;
    using System.Diagnostics;
    using System.Reflection;
    using System.Linq;
    using ContosoUniversity.Logging;
    
    namespace ContosoUniversity.DAL
    {
        public class SchoolInterceptorTransientErrors : DbCommandInterceptor
        {
            private int _counter = 0;
            private ILogger _logger = new Logger();
    
            public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
            {
                bool throwTransientErrors = false;
                if (command.Parameters.Count > 0 && command.Parameters[0].Value.ToString() == "%Throw%")
                {
                    throwTransientErrors = true;
                    command.Parameters[0].Value = "%an%";
                    command.Parameters[1].Value = "%an%";
                }
    
                if (throwTransientErrors && _counter < 4)
                {
                    _logger.Information("Returning transient error for command: {0}", command.CommandText);
                    _counter++;
                    interceptionContext.Exception = CreateDummySqlException();
                }
            }
    
            private SqlException CreateDummySqlException()
            {
                // The instance of SQL Server you attempted to connect to does not support encryption
                var sqlErrorNumber = 20;
    
                var sqlErrorCtor = typeof(SqlError).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).Where(c => c.GetParameters().Count() == 7).Single();
                var sqlError = sqlErrorCtor.Invoke(new object[] { sqlErrorNumber, (byte)0, (byte)0, "", "", "", 1 });
    
                var errorCollection = Activator.CreateInstance(typeof(SqlErrorCollection), true);
                var addMethod = typeof(SqlErrorCollection).GetMethod("Add", BindingFlags.Instance | BindingFlags.NonPublic);
                addMethod.Invoke(errorCollection, new[] { sqlError });
    
                var sqlExceptionCtor = typeof(SqlException).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).Where(c => c.GetParameters().Count() == 4).Single();
                var sqlException = (SqlException)sqlExceptionCtor.Invoke(new object[] { "Dummy", errorCollection, null, Guid.NewGuid() });
    
                return sqlException;
            }
        }
    }
    

    Kode ini hanya mengambil ReaderExecuting alih metode , yang dipanggil untuk kueri yang dapat mengembalikan beberapa baris data. Jika Anda ingin memeriksa ketahanan koneksi untuk jenis kueri lain, Anda juga dapat mengambil alih NonQueryExecuting metode dan ScalarExecuting , seperti yang dilakukan pencegat pengelogan.

    Saat Anda menjalankan halaman Siswa dan memasukkan "Lempar" sebagai string pencarian, kode ini membuat pengecualian SQL Database dummy untuk nomor kesalahan 20, jenis yang diketahui biasanya sementara. Nomor kesalahan lain yang saat ini dikenali sebagai sementara adalah 64, 233, 10053, 10054, 10060, 10928, 10929, 40197, 40501, dan 40613, tetapi ini dapat berubah dalam versi baru SQL Database.

    Kode mengembalikan pengecualian ke Kerangka Kerja Entitas alih-alih menjalankan kueri dan meneruskan kembali hasil kueri. Pengecualian sementara dikembalikan empat kali, lalu kode kembali ke prosedur normal meneruskan kueri ke database.

    Karena semuanya dicatat, Anda akan dapat melihat bahwa Kerangka Kerja Entitas mencoba menjalankan kueri empat kali sebelum akhirnya berhasil, dan satu-satunya perbedaan dalam aplikasi adalah bahwa dibutuhkan waktu lebih lama untuk merender halaman dengan hasil kueri.

    Berapa kali Kerangka Kerja Entitas akan mencoba kembali dapat dikonfigurasi; kode menentukan empat kali karena itu adalah nilai default untuk kebijakan eksekusi SQL Database. Jika Anda mengubah kebijakan eksekusi, Anda juga akan mengubah kode di sini yang menentukan berapa kali kesalahan sementara dihasilkan. Anda juga dapat mengubah kode untuk menghasilkan lebih banyak pengecualian sehingga Kerangka Kerja Entitas akan melemparkan RetryLimitExceededException pengecualian.

    Nilai yang Anda masukkan di kotak Pencarian akan berada di command.Parameters[0] dan command.Parameters[1] (satu digunakan untuk nama depan dan satu untuk nama belakang). Ketika nilai "%Throw%" ditemukan, "Throw" digantikan dalam parameter tersebut dengan "an" sehingga beberapa siswa akan ditemukan dan dikembalikan.

    Ini hanyalah cara mudah untuk menguji ketahanan koneksi berdasarkan perubahan beberapa input ke UI aplikasi. Anda juga dapat menulis kode yang menghasilkan kesalahan sementara untuk semua kueri atau pembaruan, seperti yang dijelaskan nanti di komentar tentang metode DbInterception.Add .

  3. Di Global.asax, tambahkan pernyataan berikut using :

    using ContosoUniversity.DAL;
    using System.Data.Entity.Infrastructure.Interception;
    
  4. Tambahkan baris yang disorot ke Application_Start metode :

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        DbInterception.Add(new SchoolInterceptorTransientErrors());
        DbInterception.Add(new SchoolInterceptorLogging());
    }
    

    Baris kode inilah yang menyebabkan kode pencegat Anda dijalankan saat Kerangka Kerja Entitas mengirim kueri ke database. Perhatikan bahwa karena Anda membuat kelas pencegat terpisah untuk simulasi kesalahan sementara dan pengelogan, Anda dapat mengaktifkan dan menonaktifkannya secara independen.

    Anda dapat menambahkan pencegat menggunakan DbInterception.Add metode di mana saja dalam kode Anda; itu tidak harus dalam Application_Start metode . Opsi lain adalah menempatkan kode ini di kelas DbConfiguration yang Anda buat sebelumnya untuk mengonfigurasi kebijakan eksekusi.

    public class SchoolConfiguration : DbConfiguration
    {
        public SchoolConfiguration()
        {
            SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
            DbInterception.Add(new SchoolInterceptorTransientErrors());
            DbInterception.Add(new SchoolInterceptorLogging());
        }
    }
    

    Di mana pun Anda meletakkan kode ini, berhati-hatilah untuk tidak mengeksekusi DbInterception.Add pencegat yang sama lebih dari sekali, atau Anda akan mendapatkan instans pencegat tambahan. Misalnya, jika Anda menambahkan pencegat pengelogan dua kali, Anda akan melihat dua log untuk setiap kueri SQL.

    Pencegat dijalankan dalam urutan pendaftaran (urutan di mana DbInterception.Add metode dipanggil). Urutan mungkin penting tergantung pada apa yang Anda lakukan di pencegat. Misalnya, pencegat mungkin mengubah perintah SQL yang didapatkannya di CommandText properti . Jika mengubah perintah SQL, pencegat berikutnya akan mendapatkan perintah SQL yang diubah, bukan perintah SQL asli.

    Anda telah menulis kode simulasi kesalahan sementara dengan cara yang memungkinkan Anda menyebabkan kesalahan sementara dengan memasukkan nilai yang berbeda di UI. Sebagai alternatif, Anda dapat menulis kode pencegat untuk selalu menghasilkan urutan pengecualian sementara tanpa memeriksa nilai parameter tertentu. Anda kemudian dapat menambahkan pencegat hanya ketika Anda ingin menghasilkan kesalahan sementara. Namun, jika Anda melakukan ini, jangan tambahkan pencegat hingga setelah inisialisasi database selesai. Dengan kata lain, lakukan setidaknya satu operasi database seperti kueri pada salah satu kumpulan entitas Anda sebelum Anda mulai menghasilkan kesalahan sementara. Kerangka Kerja Entitas menjalankan beberapa kueri selama inisialisasi database, dan tidak dijalankan dalam transaksi, sehingga kesalahan selama inisialisasi dapat menyebabkan konteks masuk ke status yang tidak konsisten.

Uji konfigurasi baru

  1. Tekan F5 untuk menjalankan aplikasi dalam mode debug, lalu klik tab Siswa .

  2. Lihat jendela Output Visual Studio untuk melihat output pelacakan. Anda mungkin harus menggulir ke atas melewati beberapa kesalahan JavaScript untuk masuk ke log yang ditulis oleh pencatat Anda.

    Perhatikan bahwa Anda dapat melihat kueri SQL aktual yang dikirim ke database. Anda melihat beberapa kueri dan perintah awal yang dilakukan Kerangka Kerja Entitas untuk memulai, memeriksa versi database dan tabel riwayat migrasi (Anda akan mempelajari tentang migrasi di tutorial berikutnya). Dan Anda melihat kueri untuk halaman, untuk mengetahui berapa banyak siswa di sana, dan akhirnya Anda melihat kueri yang mendapatkan data siswa.

    Pengelogan untuk kueri normal

  3. Di halaman Siswa , masukkan "Lempar" sebagai string pencarian, dan klik Cari.

    Melempar string pencarian

    Anda akan melihat bahwa browser tampaknya macet selama beberapa detik saat Kerangka Kerja Entitas mencoba kembali kueri beberapa kali. Percobaan ulang pertama terjadi dengan sangat cepat, kemudian tunggu sebelum meningkat sebelum setiap percobaan ulang tambahan. Proses menunggu lebih lama sebelum setiap percobaan kembali disebut backoff eksponensial.

    Saat halaman ditampilkan, memperlihatkan siswa yang memiliki "an" dalam namanya, melihat jendela output, dan Anda akan melihat bahwa kueri yang sama dicoba lima kali, empat kali pertama mengembalikan pengecualian sementara. Untuk setiap kesalahan sementara, Anda akan melihat log yang Anda tulis saat menghasilkan kesalahan sementara di SchoolInterceptorTransientErrors kelas ("Mengembalikan kesalahan sementara untuk perintah...") dan Anda akan melihat log yang ditulis saat SchoolInterceptorLogging mendapatkan pengecualian.

    Output pengelogan memperlihatkan percobaan ulang

    Karena Anda memasukkan string pencarian, kueri yang mengembalikan data siswa diparameterkan:

    SELECT TOP (3) 
        [Project1].[ID] AS [ID], 
        [Project1].[LastName] AS [LastName], 
        [Project1].[FirstMidName] AS [FirstMidName], 
        [Project1].[EnrollmentDate] AS [EnrollmentDate]
        FROM ( SELECT [Project1].[ID] AS [ID], [Project1].[LastName] AS [LastName], [Project1].[FirstMidName] AS [FirstMidName], [Project1].[EnrollmentDate] AS [EnrollmentDate], row_number() OVER (ORDER BY [Project1].[LastName] ASC) AS [row_number]
            FROM ( SELECT 
                [Extent1].[ID] AS [ID], 
                [Extent1].[LastName] AS [LastName], 
                [Extent1].[FirstMidName] AS [FirstMidName], 
                [Extent1].[EnrollmentDate] AS [EnrollmentDate]
                FROM [dbo].[Student] AS [Extent1]
                WHERE ([Extent1].[LastName] LIKE @p__linq__0 ESCAPE N'~') OR ([Extent1].[FirstMidName] LIKE @p__linq__1 ESCAPE N'~')
            )  AS [Project1]
        )  AS [Project1]
        WHERE [Project1].[row_number] > 0
        ORDER BY [Project1].[LastName] ASC:
    

    Anda tidak mencatat nilai parameter, tetapi Anda dapat melakukannya. Jika Anda ingin melihat nilai parameter, Anda dapat menulis kode pengelogan untuk mendapatkan nilai parameter dari Parameters properti DbCommand objek yang Anda dapatkan dalam metode pencegat.

    Perhatikan bahwa Anda tidak dapat mengulangi pengujian ini kecuali Anda menghentikan aplikasi dan memulai ulang. Jika Anda ingin dapat menguji ketahanan koneksi beberapa kali dalam satu proses aplikasi, Anda dapat menulis kode untuk mengatur ulang penghitung kesalahan di SchoolInterceptorTransientErrors.

  4. Untuk melihat perbedaan strategi eksekusi (kebijakan percobaan kembali) yang dilakukan, komentari SetExecutionStrategy baris di SchoolConfiguration.cs, jalankan halaman Siswa dalam mode debug lagi, dan cari "Lempar" lagi.

    Kali ini debugger berhenti pada pengecualian pertama yang dihasilkan segera ketika mencoba menjalankan kueri untuk pertama kalinya.

    Pengecualian Dummy

  5. Hapus komentar baris SetExecutionStrategy di SchoolConfiguration.cs.

Mendapatkan kode

Unduh Proyek yang Selesai

Sumber Daya Tambahan:

Tautan ke sumber daya Kerangka Kerja Entitas lainnya dapat ditemukan di ASP.NET Akses Data - Sumber Daya yang Direkomendasikan.

Langkah berikutnya

Di tutorial ini, Anda akan:

  • Ketahanan koneksi yang diaktifkan
  • Intersepsi perintah yang diaktifkan
  • Menguji konfigurasi baru

Lanjutkan ke artikel berikutnya untuk mempelajari tentang migrasi Kode Pertama dan penyebaran Azure.