ASP.NET MVC - Entity Framework, MySQL und Verwenden von Dependency Resolver zum Auswählen eines Repositorys

Legacy-Technologie
Warnung: ASP.NET MVC ist bereits veraltet. Die Verwendung von ASP.NET Core wird empfohlen. Aber wenn Sie interessiert sind, dann lesen Sie.

Ich habe beschlossen, den vorherigen Artikel über ASP.NET MVC und MySQL etwas zu erweitern. Es ging um die Arbeit mit MySQL in ASP.NET MVC, nicht über das fast standardmäßige ORM Entity Framework (EF), sondern über den direkten Zugriff auf das DBMS über ADO.NET. Und eine Implementierung dieser Zugriffsmethode wurde gegeben. Und obwohl die Methode veraltet ist und nicht für die Verwendung empfohlen wird, ist sie manchmal nützlich: Zum Beispiel in stark ausgelasteten Anwendungen oder wenn ein Entwickler mit einer Situation konfrontiert ist, in der ORM keine ordnungsgemäß funktionierende SQL-Abfrage generieren kann. Und manchmal können Sie beide Methoden in einer Anwendung kombinieren - sowohl über ORM als auch über ADO.NET. Aus diesem Grund habe ich mir überlegt und beschlossen, die Anwendung fertigzustellen: Eine Repository-Implementierung für das Entity Framework hinzuzufügen und die Auswahl mit dem Dependency Resolver vom Anwendungsparameter abhängig zu machen.

Der gesamte Code kann hier unter dieser Adresse abgerufen werden. Nachfolgend wird der Code teilweise mit kleinen Links und Erläuterungen zum vorherigen Projekt dargestellt . Und hier können Sie die Anwendung sehen .

Wir ändern das Projekt


1. Um das Entity Framework mit MySQL zu verwenden, müssen Sie die MySQL.Data.EntityFramework- Bibliothek installieren (Sie können natürlich auch eine andere haben, nur diese von Oracle, dem Eigentümer von MySQL).


Es wird MySQL.Data und EntityFramework selbst ziehen . Die folgenden Änderungen wurden an der Datei web.config vorgenommen :

<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> 

Es entstand ein interessanter Konflikt mit MySQL.Data - da MySQL.Data.EntityFramework eine Version von MySQL.Data von mindestens 8.0.19 benötigte, wurde diese aktualisiert ... und das Projekt funktionierte nicht mehr. Es trat ein Fehler auf:



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

Anscheinend wurde die nicht signierte Assembly Ubiety.Dns.Core zu MySQL.Data 8.0.19 hinzugefügt. Ich musste diese Komponente auch über Nuget in das Projekt einbinden. Der Fehler ist weg.

2. Um die Implementierung von Abhängigkeiten zu implementieren, fügen wir dem Projekt Ninject den Container für die Implementierung von Abhängigkeiten (DI) hinzu.

3. Wir werden die Projektstruktur geringfügig ändern: Wir werden die Repository-Dateien in einem separaten Repository- Verzeichnis ablegen und die ADO.NET- Unterverzeichnisse darin erstellen (wir werden die vorhandenen LanguagesRepository.cs- und UsersRepository.cs- Dateien dorthin übertragen ) und EF (es werden Repository-Dateien für das Entity Framework hier vorhanden sein).

4. Außerdem wurde der Datei appConfig Abschnitt appConfig ein Anwendungsparameter hinzugefügt: <add key="ConnectionMethod" value="ADO.NET" /> . Die Anwendung nimmt zwei Werte an: "Entity Framework" oder "ADO.NET". Der Datei Base.cs wurde ein Link zu diesem Parameter hinzugefügt :

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

Entity Framework und MySQL - Repository


Fügen Sie die Datei DbContext.cs mit der EFDbContext Klasse zum EFDbContext Repository \ EF hinzu :

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

Darin bestimmen wir die verwendete DBMS-Verbindungszeichenfolge und die Users und Languages .

Fügen Sie die Datei LanguagesRepository.cs mit der Klasse LanguagesRepositoryEF :

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

Und die Datei UsersRepository.cs mit der Klasse 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; } } 

Es ist zu erkennen, dass die Dateigröße deutlich geringer ist als bei ADO.NET - ORM erledigt die Dirty Work für uns - es erstellt SQL-Abfragen selbstständig.

Ich bin jedoch auf einige Punkte gestoßen, die bei der Implementierung von ADO.NET eine Rolle spielten, aber in EF nicht funktionierten.

Das erste, was ich an der UserClass.cs- Datei (im Domain- Verzeichnis) ändern musste: Ein weiteres Feld für den normalen Betrieb der Verbindung zwischen den Tabellen Users und Languages hinzufügen:

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

Und das zweite - es stellte sich heraus, dass Felder in MySQL wie Enum nicht über EF funktionieren. Der Grund dafür ist höchstwahrscheinlich, dass die Aufzählung im Code ein ganzzahliger Wert ist, die Werte über EF jedoch aus der Datenbank als Text gelesen werden (wenn in einer Abfrage von MySQL zum Lesen der Werte eines Feldes vom Typ enum MySQL nur die Textwerte dieser Aufzählung zurückgibt). Und wenn ich dies in der Version für ADO.NET CAST(u.SupporterTier AS UNSIGNED) as SupporterTier kann, indem ich das CAST(u.SupporterTier AS UNSIGNED) as SupporterTier Konstrukt verwende, dann hat sich eine solche Metamorphose mit EF für mich als unüberwindbar erwiesen - keine der erprobten Optionen ergab sich. Nun, da die Code First- Technologie ein Feld vom Typ Enum als Feld vom Typ INT generiert, mussten wir den Typ des Feldes SupporterTier in der Datenbank ändern:

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

Wählen Sie das Repository mit dem Anwendungsparameter aus


Wir werden die Implementierung durch den Konstruktor verwenden, so wie es im Lehrbuch geschrieben ist. Zuerst müssen wir Schnittstellen für unser freigegebenes Repository erstellen: Erstellen Sie die Datei LanguagesRepository.cs im Repository- Verzeichnis mit dem Inhalt:

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

Und die UsersRepository.cs- Datei mit dem Inhalt:

 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); } 

Nun, wir erben die entsprechenden Klassen von diesen Schnittstellen:

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

Nun, im UsersController- Controller nehmen wir Ergänzungen vor, die es ihm ermöglichen, mit diesen Schnittstellen zu arbeiten:

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

Und im Controller ändern wir die Stellen, an denen auf die Objekte dieser Klassen repLanguages auf die Objekte repLanguages bzw. repUsers . Aber wir müssen Instanzen der Repository-Klassen über den Controller-Konstruktor übergeben, was natürlich unpraktisch ist. Um dies zu vermeiden, brauchen wir starke Zauberei wie Dependency Resolver (DR). Und dafür werden wir Ninject verwenden:

Wir registrieren DR in der Datei Global.asax.cs in der Application_Start Methode:

 DependencyResolver.SetResolver(new NinjectDependencyResolver()); 

Erstellen Sie die Datei NinjectDependencyResolver.cs im Infrastrukturverzeichnis mit der NinjectDependencyResolver Klasse (geerbt von der IDependencyResolver Schnittstelle):

 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>(); } } } 

Und es stellt sich heraus, dass die AddBindings Methode in der NinjectDependencyResolver Klasse der einzige Ort ist, an dem bestimmt wird, welche Methode für die Arbeit mit dem DBMS verwendet wird (direkt, über ADO.NET oder über das Entity Framework). Wirkliche Magie, wenn Sie nicht wissen, wie es funktioniert.

In der AddBindings Methode werden abhängig vom Wert des ConnectionMethod-Anwendungsparameters die IUsersRepository ILanguagesRepository und IUsersRepository mit bestimmten Klassen verbunden, die die Schnittstellenmethoden implementieren. Seit wir NinjectDependencyResolver Anwendung NinjectDependencyResolver , haben wir DR als Objekt der NinjectDependencyResolver Klasse registriert. In der Klasse haben wir die Bindung der Repository-Schnittstellen an eine bestimmte Klasse angegeben, und Ninject hat bei der Analyse der Klasse festgestellt, dass die Implementierung der ILanguagesRepository und IUsersRepository erforderlich ist erstellt Instanzen bestimmter Klassen und übergibt sie an den Controller-Konstruktor (über DR und das MVC-Framework).

Total


Die Anwendung unterstützt jetzt auch die Methode des Zugriffs auf das DBMS über das ORM-Entity-Framework. Gleichzeitig ist die Zugriffsmethode über ADO.NET nicht verloren gegangen und wird beim Starten der Anwendung über den Parameter ausgewählt. Für diesen Parameter haben wir die Abhängigkeitsinjektionsmethode über den Konstruktor des Controllers unter Verwendung der Ninject-Bibliothek verwendet.



PS Und zum Schluss: Wie dieses Projekt funktioniert, sehen Sie unter dieser Adresse . Und hier können Sie das gesamte Projekt herunterladen. Nun, zum Haufen - ein Link zu meinem Blog .

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


All Articles