ASP.NET MVC-实体框架,MySQL并使用Dependency Resolver选择存储库

传统技术
警告:不建议使用ASP.NET MVC。 建议使用ASP.NET Core。 但是,如果您有兴趣,请阅读。

我决定略微扩展以前有关ASP.NET MVC和MySQL的文章 。 这是关于在ASP.NET MVC中使用MySQL而不是通过几乎标准的ORM实体框架(EF),而是通过ADO.NET直接访问DBMS。 并给出了该访问方法的实现。 而且,尽管该方法已经过时并且不建议使用,但它有时还是有用的:例如,在高负载的应用程序中或开发人员遇到ORM无法生成正确的SQL查询的情况时。 有时,您可以在应用程序中结合使用这两种方法-通过ORM和ADO.NET。 结果,我想到并决定完成该应用程序:向其添加Entity Framework的存储库实现,并使用Dependency Resolver依赖于应用程序参数来选择它们。

所有代码都可以在此处获取,下面的代码将部分地带有小链接和与上一个项目有关的解释。 在这里您可以看到该应用程序

我们改变项目


1.要将Entity Framework与MySQL一起使用,我们需要安装MySQL.Data.EntityFramework库(当然,您可以拥有另一个库,就是MySQL所有者Oracle的一个库)。


它将拉MySQL.DataEntityFramework本身 。 对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.csUsersRepository.cs文件)和EF (此处将提供实体框架的存储库文件)。

4.另外,在appConfig部分的web.config文件中添加了一个应用程序参数: <add key="ConnectionMethod" value="ADO.NET" /> 。 该应用程序将采用两个值:“实体框架”或“ ADO.NET”。 在Base.cs文件中添加了此参数链接:

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

实体框架和MySQL-存储库


将具有EFDbContext类的DbContext.cs文件添加到Repository \ EF EFDbContext

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

在其中,我们确定使用的DBMS连接字符串以及“ UsersLanguages数据集。

使用LanguagesRepositoryEF类添加LanguagesRepository.cs文件:

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

以及具有UsersRepositoryEF类的UsersRepository.cs文件:

 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文件(在Domain目录中)进行更改的第一件事:为UsersLanguages表之间的连接的正常操作添加另一个字段:

 [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' ; 

使用应用程序参数选择存储库


我们将通过构造函数使用实现,就像在教科书中写的那样。 首先,我们需要为共享存储库创建接口:在Repository目录中创建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分别更改为repLanguagesrepUsers 。 但是我们需要通过控制器构造函数传递存储库类的实例,这当然很不方便。 为了避免这种情况,我们需要强大的功能,例如依赖解析器 (DR)。 为此,我们将使用Ninject:

我们在Application_Start方法的Global.asax.cs文件中注册DR:

 DependencyResolver.SetResolver(new NinjectDependencyResolver()); 

使用NinjectDependencyResolver类(从IDependencyResolver接口继承)在Infrastructure目录中创建NinjectDependencyResolver.cs文件:

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

结果发现,唯一确定使用哪种方法(直接通过ADO.NET或通过实体框架)使用AddBindings方法是NinjectDependencyResolver类中的AddBindings方法。 如果您不知道它是如何工作的,那真是不可思议。

AddBindings方法中,取决于ConnectionMethod应用程序参数的值, ILanguagesRepositoryIUsersRepository与实现接口方法的特定类。 自从NinjectDependencyResolver应用程序以来,我们将DR注册为NinjectDependencyResolver类的对象,并且在该类中,我们指定了存储库接口与特定类的绑定,因此当请求MVC框架创建UsersController控制器UsersController ,Ninject在分析该类时会发现它需要实现ILanguagesRepositoryIUsersRepository ,将创建特定类的实例,并将它们传递给控制器​​构造函数(通过DR和MVC框架)。

合计


该应用程序现在支持通过ORM实体框架访问DBMS的方法。 同时,通过ADO.NET的访问方法并没有消失,而是在通过参数启动应用程序时选择了访问方法,为此,我们使用Ninject库通过控制器构造函数使用了依赖项注入方法。



PS最后:您可以在此地址查看该项目的工作方式。 在这里您可以下载整个项目。 好了,到堆- 我的博客的链接。

Source: https://habr.com/ru/post/zh-CN484582/


All Articles