ASP.NET MVC - Kerangka Entitas, MySQL dan menggunakan Dependency Resolver untuk memilih repositori

Teknologi Warisan
Peringatan: ASP.NET MVC sudah usang. Dianjurkan untuk menggunakan ASP.NET Core. Tetapi jika Anda tertarik, maka bacalah.

Saya memutuskan untuk sedikit memperluas artikel sebelumnya tentang ASP.NET MVC dan MySQL . Itu tentang bekerja dengan MySQL di ASP.NET MVC bukan melalui ORM Entity Framework (EF) yang hampir standar, tetapi menggunakan akses langsung ke DBMS melalui ADO.NET. Dan implementasi metode akses ini diberikan. Dan meskipun metode ini sudah usang dan tidak direkomendasikan untuk digunakan, kadang-kadang berguna: misalnya, dalam aplikasi yang sangat dimuat atau ketika pengembang dihadapkan pada situasi di mana ORM tidak dapat menghasilkan kueri SQL yang berfungsi dengan benar. Dan kadang-kadang Anda dapat menggabungkan kedua metode dalam aplikasi - baik melalui ORM dan melalui ADO.NET. Akibatnya, saya berpikir dan memutuskan untuk menyelesaikan aplikasi: menambahkan implementasi repositori untuk Kerangka Entitas ke dalamnya dan membuat pilihan mereka bergantung pada parameter aplikasi menggunakan Penyelesai Ketergantungan.

Semua kode dapat diambil di sini di alamat ini , di bawah kode ini akan disajikan sebagian dengan tautan kecil dan penjelasan sehubungan dengan proyek sebelumnya . Dan di sini Anda dapat melihat aplikasinya .

Kami mengubah proyek


1. Untuk menggunakan Entity Framework dengan MySQL, kita perlu menginstal pustaka MySQL.Data.EntityFramework (Anda tentu saja dapat memiliki yang lain, hanya saja ini dari Oracle, pemilik MySQL).


Ini akan menarik MySQL.Data dan EntityFramework itu sendiri . Perubahan berikut dibuat pada file web.config :

<entityFramework> <providers> <provider invariantName="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.EntityFramework, Version=8.0.19.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" /> </providers> </entityFramework> 

Konflik yang menarik muncul dengan MySQL.Data - karena MySQL.Data.EntityFramework memerlukan versi MySQL.Data minimal 8.0.19, itu diperbarui ... dan proyek berhenti bekerja. Kesalahan mulai terjadi:



«Ubiety.Dns.Core» . . , , . ( HRESULT: 0x80131045)

Rupanya, perakitan yang tidak ditandatangani Ubiety.Dns.Core ditambahkan ke MySQL.Data 8.0.19 . Saya juga harus memasukkan komponen ini melalui Nuget dalam proyek. Kesalahan hilang.

2. Selain itu, untuk mengimplementasikan implementasi dependensi, kami menambah proyek Ninject - wadah untuk mengimplementasikan dependensi (DI).

3. Kami akan sedikit mengubah struktur proyek: kami akan menempatkan file repositori ke dalam direktori Repositori terpisah dan membuat subdirektori ADO.NET di dalamnya (kami akan mentransfer file LanguagesRepository.cs dan UsersRepository.cs yang ada di sana ) dan EF (akan ada file repositori untuk Entity Framework di sini).

4. Selain itu, parameter aplikasi telah ditambahkan ke file web.config di bagian appConfig : <add key="ConnectionMethod" value="ADO.NET" /> . Aplikasi akan mengambil dua nilai: "Entity Framework" atau "ADO.NET". Menambahkan tautan ke parameter ini ke file Base.cs :

 public static string ConnectionMethod { get { return System.Configuration.ConfigurationManager.AppSettings["ConnectionMethod"]; } } 

Entity Framework dan MySQL - repository


Tambahkan file DbContext.cs dengan kelas EFDbContext ke EFDbContext Repository \ EF :

 public class EFDbContext : DbContext { public EFDbContext() : base(Base.ConnectionString) { } public DbSet<UserClass> Users { get; set; } public DbSet<LanguageClass> Languages { get; set; } } 

Di dalamnya, kami menentukan string koneksi DBMS yang digunakan dan set data Users dan Languages .

Tambahkan file LanguagesRepository.cs dengan kelas LanguagesRepositoryEF :

 public class LanguagesRepositoryEF : ILanguagesRepository { private EFDbContext context = new EFDbContext(); public IList<LanguageClass> List() { return context.Languages.OrderBy(x => x.LanguageName).ToList(); } } 

Dan file UsersRepository.cs dengan kelas UsersRepositoryEF :

 public class UsersRepositoryEF : IUsersRepository { private EFDbContext context = new EFDbContext(); public IList<UserClass> List() { return context.Users.ToList(); } public IList<UserClass> List(string sortName, SortDirection sortDir, int page, int pageSize, out int count) { count = context.Users.Count(); if (sortName != null) return context.Users.OrderByDynamic(sortName, sortDir).Skip((page - 1) * pageSize).Take(pageSize).ToList(); else return context.Users.OrderBy(o => o.UserID).Skip((page - 1) * pageSize).Take(pageSize).ToList(); } public bool AddUser(UserClass user) { user.Language = context.Languages.Find(user.Language.LanguageID); if (user.Language != null && context.Users.Add(user) != null) { try { context.SaveChanges(); } catch (System.Exception ex) {} } return user.UserID > 0; } public UserClass FetchByID(int userID) { UserClass user = null; try { user = context.Users.Find(userID); } catch (System.Exception ex) { } return user; } public bool ChangeUser(UserClass user) { bool result = false; user.Language = context.Languages.Find(user.Language.LanguageID); if (user.Language != null) { UserClass olduser = context.Users.Find(user.UserID); if (olduser != null) { olduser.Email = user.Email; olduser.Loginname = user.Loginname; olduser.Language = user.Language; olduser.SupporterTier = user.SupporterTier; try { result = context.SaveChanges() > 0; } catch (System.Exception ex) { } } } return result; } public bool RemoveUser(UserClass user) { bool result = false; UserClass olduser = context.Users.Find(user.UserID); if (olduser != null) context.Users.Remove(olduser); try { result = context.SaveChanges() > 0; } catch (System.Exception ex) { } return result; } } 

Dapat dilihat bahwa ukuran file jelas lebih pendek dari itu untuk ADO.NET - ORM melakukan pekerjaan kotor untuk kami - ia menciptakan query SQL sendiri.

Namun, saya menemukan beberapa poin yang masuk ke dalam implementasi ADO.NET, tetapi tidak berhasil di EF.

Hal pertama yang saya harus lakukan untuk mengubah file UserClass.cs (dalam direktori Domain ): tambahkan bidang lain untuk operasi normal koneksi antara tabel Users dan Languages :

 [HiddenInput(DisplayValue = false)] public int? LanguageID { get; set; } 

Dan yang kedua - ternyata bidang di MySQL seperti Enum tidak bekerja melalui EF. Kemungkinan besar, alasan untuk ini adalah bahwa enumerasi dalam kode adalah nilai integer, tetapi dari database nilai-nilai melalui EF dibaca sebagai teks (jika dalam query dari MySQL untuk membaca nilai-nilai bidang tipe enum MySQL mengembalikan hanya nilai teks dari enumerasi ini). Dan jika dalam versi untuk ADO.NET saya dapat menyiasati hal ini menggunakan CAST(u.SupporterTier AS UNSIGNED) as SupporterTier konstruksi CAST(u.SupporterTier AS UNSIGNED) as SupporterTier , kemudian dengan EF metamorfosis seperti itu ternyata tidak dapat diatasi bagi saya - tidak ada opsi yang dicoba muncul. Nah, karena teknologi Code First menghasilkan bidang tipe Enum sebagai bidang tipe INT, kami harus mengubah jenis bidang SupporterTier dalam database:

 CHANGE COLUMN `SupporterTier` `SupporterTier` INT(4) UNSIGNED NOT NULL DEFAULT '1' ; 

Pilih repositori menggunakan parameter aplikasi


Kami akan menggunakan implementasi melalui konstruktor, seperti yang tertulis di buku teks. Pertama, kita perlu membuat antarmuka untuk repositori bersama kita: membuat file LanguagesRepository.cs di direktori Repositori dengan konten:

 public interface ILanguagesRepository { IList<LanguageClass> List(); } 

Dan file UsersRepository.cs dengan konten:

 public interface IUsersRepository { IList<UserClass> List(); IList<UserClass> List(string sortName, SortDirection sortDir, int page, int pageSize, out int count); bool AddUser(UserClass user); UserClass FetchByID(int userID); bool ChangeUser(UserClass user); bool RemoveUser(UserClass user); } 

Kami mewarisi kelas yang sesuai dari antarmuka ini:

 public class LanguagesRepositoryADO : ILanguagesRepository public class UsersRepositoryADO : IUsersRepository public class LanguagesRepositoryEF : ILanguagesRepository public class UsersRepositoryEF : IUsersRepository 

Nah, pada pengontrol UsersController, kami membuat tambahan yang memungkinkannya bekerja dengan antarmuka ini:

 private ILanguagesRepository repLanguages; private IUsersRepository repUsers; public UsersController(ILanguagesRepository langsParam, IUsersRepository usersParam) { repLanguages = langsParam; repUsers = usersParam; } 

Dan di controller, kami mengubah tempat-tempat mengakses objek kelas-kelas ini ke objek repLanguages dan repUsers , masing-masing. Tetapi kita perlu melewatkan instance dari kelas repositori melalui konstruktor controller, yang, tentu saja, tidak nyaman. Untuk menghindarinya, kita membutuhkan ilmu sihir yang kuat seperti Dependency Resolver (DR). Dan untuk ini kita akan menggunakan Ninject:

Kami mendaftarkan DR di file Global.asax.cs dalam metode Application_Start :

 DependencyResolver.SetResolver(new NinjectDependencyResolver()); 

Buat file NinjectDependencyResolver.cs di direktori Infrastruktur dengan kelas NinjectDependencyResolver (diwarisi dari antarmuka IDependencyResolver ):

 public class NinjectDependencyResolver : IDependencyResolver { private IKernel kernel; public NinjectDependencyResolver() { kernel = new StandardKernel(); AddBindings(); } public object GetService(Type serviceType) { return kernel.TryGet(serviceType); } public IEnumerable<object> GetServices(Type serviceType) { return kernel.GetAll(serviceType); } private void AddBindings() { if (Domain.Base.ConnectionMethod == "Entity Framework") { kernel.Bind<ILanguagesRepository>().To<LanguagesRepositoryEF>(); kernel.Bind<IUsersRepository>().To<UsersRepositoryEF>(); } else { kernel.Bind<ILanguagesRepository>().To<LanguagesRepositoryADO>(); kernel.Bind<IUsersRepository>().To<UsersRepositoryADO>(); } } } 

Dan ternyata satu-satunya tempat di mana ditentukan metode mana yang bekerja dengan DBMS digunakan (langsung, melalui ADO.NET atau melalui Entity Framework) adalah metode AddBindings di kelas NinjectDependencyResolver . Sihir sungguhan jika Anda tidak tahu cara kerjanya.

Dalam metode AddBindings , tergantung pada nilai parameter aplikasi ConnectionMethod, IUsersRepository dan IUsersRepository dengan kelas tertentu yang menerapkan metode antarmuka. Karena ketika kami NinjectDependencyResolver aplikasi, kami mendaftarkan DR sebagai objek dari kelas NinjectDependencyResolver , dan di dalam kelas kami menentukan pengikatan antarmuka repositori ke kelas tertentu, ketika meminta kerangka kerja MVC untuk membuat UsersController pengontrol UsersController , Ninject ketika menganalisis kelas akan menemukan bahwa itu memerlukan implementasi IUsersRepository dan ILanguagesRepository dan IUsersRepository akan membuat instance kelas tertentu dan meneruskannya ke konstruktor kontroler (melalui DR dan kerangka kerja MVC).

Total


Aplikasi sekarang juga mendukung metode akses ke DBMS melalui ORM Entity Framework. Pada saat yang sama, metode akses melalui ADO.NET belum hilang dan dipilih ketika aplikasi diluncurkan oleh parameter, di mana kami menggunakan metode injeksi ketergantungan melalui konstruktor pengontrol menggunakan perpustakaan Ninject.



PS Dan akhirnya: Anda bisa melihat bagaimana proyek ini bekerja di alamat ini . Dan di sini Anda dapat mengunduh seluruh proyek. Nah, untuk heap - tautan ke blog saya .

Source: https://habr.com/ru/post/id484582/


All Articles