التكنولوجيا القديمةتحذير: تم إهمال ASP.NET MVC بالفعل. يوصى باستخدام ASP.NET Core. ولكن إذا كنت مهتما ، فاقرأ.
قررت توسيع
المقالة السابقة حول ASP.NET MVC و MySQL قليلاً. كان الأمر يتعلق بالعمل مع MySQL في ASP.NET MVC ليس من خلال إطار عمل الكيان ORM القياسي (EF) تقريبًا ، ولكن باستخدام الوصول المباشر إلى DBMS من خلال ADO.NET. وتم تنفيذ طريقة الوصول هذه. على الرغم من أن هذه الطريقة قديمة ولا يوصى باستخدامها ، إلا أنها مفيدة في بعض الأحيان: على سبيل المثال ، في التطبيقات المحملة بدرجة عالية أو عندما يواجه المطور مشكلة لا يمكن لـ ORM إنشاء استعلام SQL يعمل بشكل صحيح. وفي بعض الأحيان ، يمكنك دمج كلتا الطريقتين في تطبيق - سواء من خلال ORM أو من خلال ADO.NET. كنتيجة لذلك ، فكرت وقررت إنهاء التطبيق: إضافة تطبيق مستودع لإطار الكيان إليه وجعل اختياره يعتمد على معلمة التطبيق باستخدام محلل التبعية.
يمكن أخذ جميع الشفرات هنا على
هذا العنوان ، أسفل هذا الرمز سيتم تقديمه جزئيًا مع روابط وتفسيرات صغيرة فيما يتعلق
بالمشروع السابق . وهنا يمكنك
رؤية التطبيق .
نحن نغير المشروع
1. لاستخدام Entity Framework مع MySQL ، نحتاج إلى تثبيت مكتبة
MySQL.Data.EntityFramework (يمكنك بالطبع الحصول على واحدة أخرى ، هذه فقط من Oracle ، مالك MySQL).

وسوف تسحب
MySQL.Data و
EntityFramework نفسها . تم إجراء التغييرات التالية على
ملف 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>
نشأ تعارض مثير مع
MySQL.Data - نظرًا لأن
MySQL.Data.EntityFramework يتطلب إصدار
MySQL.Data من 8.0.19 على الأقل ، تم تحديثه ... وتوقف المشروع عن العمل. لقد حدث خطأ:

«Ubiety.Dns.Core» . . , , . ( HRESULT: 0x80131045)
على ما يبدو ، تمت إضافة
التجميع غير الموقّع Ubiety.Dns.Core إلى
MySQL.Data 8.0.19 . كما اضطررت إلى تضمين هذا المكون من خلال Nuget في المشروع. ذهب الخطأ.
2. بالإضافة إلى ذلك ، لتنفيذ تنفيذ التبعيات ، نضيف إلى المشروع Ninject - الحاوية لتنفيذ التبعيات (DI).
3. سوف نقوم بتغيير بنية المشروع بشكل طفيف: سنضع ملفات المستودع في دليل
مستودع منفصل
وننشئ الدلائل الفرعية
ADO.NET فيه (سنقوم بنقل ملفات
LanguagesRepository.cs ومستخدمي UsersRepository.cs الموجودة هناك ) و
EF (ستكون هناك ملفات مستودع للإطار Entity Framework هنا).
4. بالإضافة إلى ذلك ، تمت إضافة معلمة تطبيق إلى
ملف web.config في قسم
appConfig
:
<add key="ConnectionMethod" value="ADO.NET" />
. سيستغرق التطبيق قيمتين: "إطار الكيان" أو "ADO.NET".
تمت إضافة رابط لهذه المعلمة إلى ملف
Base.cs :
public static string ConnectionMethod { get { return System.Configuration.ConfigurationManager.AppSettings["ConnectionMethod"]; } }
إطار الكيان و MySQL - مستودع
إضافة ملف
DbContext.cs مع فئة
EFDbContext
إلى
EFDbContext
\ EF Repository :
public class EFDbContext : DbContext { public EFDbContext() : base(Base.ConnectionString) { } public DbSet<UserClass> Users { get; set; } public DbSet<LanguageClass> Languages { get; set; } }
في ذلك ، نحدد سلسلة اتصال DBMS
Users
ومجموعات بيانات
Users
Languages
.
أضف ملف
LanguagesRepository.cs مع فئة
LanguagesRepositoryEF
:
public class LanguagesRepositoryEF : ILanguagesRepository { private EFDbContext context = new EFDbContext(); public IList<LanguageClass> List() { return context.Languages.OrderBy(x => x.LanguageName).ToList(); } }
وملف
UsersRepository.cs مع فئة
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; } }
يمكن ملاحظة أن حجم الملف أقصر بشكل واضح من ADO.NET - يقوم ORM بالعمل القذر بالنسبة لنا - يقوم بإنشاء استعلامات SQL من تلقاء نفسه.
ومع ذلك ، صادفت بضع نقاط دخلت في تنفيذ ADO.NET ، ولكن لم تنجح في EF.
أول شيء اضطررت لإجراء تغيير على ملف
UserClass.cs (في دليل
المجال ): أضف حقلًا آخر للتشغيل العادي للاتصال بين جدولي
Users
Languages
:
[HiddenInput(DisplayValue = false)] public int? LanguageID { get; set; }
والثاني - اتضح أن الحقول في MySQL مثل
Enum
لا تعمل من خلال EF. على الأرجح ، السبب في ذلك هو أن التعداد في الكود يمثل قيمة عددية ، ولكن من قاعدة البيانات ، تتم قراءة القيم من خلال EF كنص (إذا كان في استعلام من MySQL لقراءة قيم حقل التعداد بالكتابة ، فإن MySQL تُرجع فقط القيم النصية لهذا التعداد). وإذا كان في الإصدار الخاص بـ ADO.NET ، يمكنني الالتفاف حول ذلك باستخدام
CAST(u.SupporterTier AS UNSIGNED) as SupporterTier
، ثم مع EF تحول هذا التحول إلى أنه لا يمكن التغلب عليه بالنسبة لي - لم يظهر أي من الخيارات المجربة. حسنًا ، نظرًا لأن تقنية
Code First تنشئ حقلًا من النوع
Enum
كحقل من النوع INT ، كان علينا تغيير نوع الحقل
SupporterTier
في قاعدة البيانات:
CHANGE COLUMN `SupporterTier` `SupporterTier` INT(4) UNSIGNED NOT NULL DEFAULT '1' ;
حدد مستودع التخزين باستخدام معامل التطبيق
سنستخدم التطبيق من خلال المنشئ ، تمامًا كما هو مكتوب في الكتاب المدرسي. أولاً ، نحتاج إلى إنشاء واجهات لمستودعنا المشترك: إنشاء ملف
LanguagesRepository.cs في دليل
السجل بالمحتويات:
public interface ILanguagesRepository { IList<LanguageClass> List(); }
وملف
UsersRepository.cs مع المحتويات:
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); }
حسنًا ، نرث الفئات المقابلة من هذه الواجهات:
public class LanguagesRepositoryADO : ILanguagesRepository public class UsersRepositoryADO : IUsersRepository public class LanguagesRepositoryEF : ILanguagesRepository public class UsersRepositoryEF : IUsersRepository
حسنًا ، في وحدة تحكم
UsersController ، نقوم بإضافة إضافات تسمح لها بالعمل مع هذه الواجهات:
private ILanguagesRepository repLanguages; private IUsersRepository repUsers; public UsersController(ILanguagesRepository langsParam, IUsersRepository usersParam) { repLanguages = langsParam; repUsers = usersParam; }
وفي وحدة التحكم ، نقوم بتغيير أماكن الوصول إلى كائنات هذه الفئات إلى
repUsers
و
repUsers
، على التوالي. لكننا نحتاج إلى تمرير مثيلات فئات المستودع من خلال مُنشئ وحدة التحكم ، والتي ، بالطبع ، غير مريحة. لتجنب هذا ، نحن بحاجة إلى سحر قوي مثل
Dependency Resolver (DR). ولهذا سنستخدم Ninject:
نقوم بتسجيل DR في ملف
Global.asax.cs في الأسلوب
Application_Start
:
DependencyResolver.SetResolver(new NinjectDependencyResolver());
قم
بإنشاء ملف
NinjectDependencyResolver.cs في دليل
البنية التحتية مع فئة
NinjectDependencyResolver
(الموروثة من واجهة
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>(); } } }
وتبين أن المكان الوحيد الذي يتم فيه تحديد طريقة العمل مع DBMS المستخدم (مباشرة ، من خلال ADO.NET أو من خلال Entity Framework) هو أسلوب
AddBindings
في الفئة
NinjectDependencyResolver
. سحر حقيقي إذا كنت لا تعرف كيف يعمل.
في أسلوب
AddBindings
، اعتمادًا على قيمة معلمة تطبيق ConnectionMethod ،
IUsersRepository
و
IUsersRepository
مع فئات محددة تقوم بتطبيق أساليب الواجهة. منذ أن
NinjectDependencyResolver
التطبيق ، سجلنا DR ككائن من الفئة
NinjectDependencyResolver
، وفي الفصل حددنا الربط بين واجهات مستودع التخزين لفئة معينة ، عند طلب إطار عمل MVC لإنشاء
UsersController
تحكم UsersController ، سيجد Ninject عند تحليل الفئة أنه يتطلب تطبيق
ILanguagesRepository
و
IUsersRepository
سيخلق مثيلات لفئات محددة ويمررها إلى مُنشئ وحدة التحكم (عبر DR وإطار عمل MVC).
في المجموع
يدعم التطبيق الآن أيضًا طريقة الوصول إلى DBMS من خلال ORM Entity Framework. في الوقت نفسه ، لم تختف طريقة الوصول من خلال ADO.NET ويتم تحديدها عندما يتم تشغيل التطبيق بواسطة المعلمة ، والتي استخدمناها طريقة حقن التبعية من خلال مُنشئ وحدة التحكم باستخدام مكتبة Ninject.

ملاحظة: وأخيرا ، يمكنك أن ترى كيف يعمل هذا المشروع على
هذا العنوان .
وهنا يمكنك تنزيل المشروع بأكمله. حسنا ، إلى الكومة - رابط
لبلدي بلوق .