Tecnologia herdadaAviso: o ASP.NET MVC já está obsoleto. O uso do ASP.NET Core é recomendado. Mas se você estiver interessado, leia.
Decidi expandir um pouco o
artigo anterior sobre o ASP.NET MVC e o MySQL . Tratava-se de trabalhar com o MySQL no ASP.NET MVC, não através do EF (ORM Entity Framework) quase padrão, mas usando o acesso direto ao DBMS através do ADO.NET. E foi dada uma implementação desse método de acesso. E embora o método seja obsoleto e não recomendado para uso, às vezes é útil: por exemplo, em aplicativos altamente carregados ou quando um desenvolvedor enfrenta uma situação em que o ORM não pode gerar uma consulta SQL funcionando corretamente. E às vezes você pode combinar os dois métodos em um aplicativo - através do ORM e do ADO.NET. Como resultado, pensei e decidi finalizar o aplicativo: adicionando uma implementação de repositório para o Entity Framework e fazer a escolha deles dependente do parâmetro do aplicativo usando o Dependency Resolver.
Todo o código pode ser obtido aqui
neste endereço , abaixo deste código será parcialmente apresentado com pequenos links e explicações em relação ao
projeto anterior . E aqui você pode
ver o aplicativo .
Mudamos o projeto
1. Para usar o Entity Framework com MySQL, precisamos instalar a biblioteca
MySQL.Data.EntityFramework (é claro que você pode ter outra, apenas a Oracle, proprietária do MySQL).

Ele puxará o
MySQL.Data e o
próprio EntityFramework . As seguintes alterações foram feitas no
arquivo 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>
Surgiu
um conflito interessante com o
MySQL.Data - já que o
MySQL.Data.EntityFramework exigia uma versão do
MySQL.Data de pelo menos 8.0.19, foi atualizada ... e o projeto parou de funcionar. Um erro começou a ocorrer:

«Ubiety.Dns.Core» . . , , . ( HRESULT: 0x80131045)
Aparentemente, o
assembly não assinado Ubiety.Dns.Core foi adicionado ao
MySQL.Data 8.0.19 . Eu também tive que incluir esse componente através do Nuget no projeto. O erro se foi.
2. Além disso, para implementar a implementação de dependências, adicionamos ao projeto Ninject - o contêiner para implementação de dependências (DI).
3. Vamos mudar um pouco a estrutura do projeto: colocaremos os arquivos do repositório em um diretório
Repository separado e criaremos os subdiretórios
ADO.NET nele (transferiremos os arquivos
LanguagesRepository.cs e
UsersRepository.cs existentes ) e
EF (haverá arquivos de repositório para o Entity Framework aqui).
4. Além disso, um parâmetro do aplicativo foi adicionado ao
arquivo web.config na seção
appConfig
:
<add key="ConnectionMethod" value="ADO.NET" />
. O aplicativo terá dois valores: "Entity Framework" ou "ADO.NET".
Adicionado um link para esse parâmetro no arquivo
Base.cs :
public static string ConnectionMethod { get { return System.Configuration.ConfigurationManager.AppSettings["ConnectionMethod"]; } }
Entity Framework e MySQL - repositório
Adicione o arquivo
EFDbContext
com a classe
EFDbContext
ao
EFDbContext
Repository \ EF :
public class EFDbContext : DbContext { public EFDbContext() : base(Base.ConnectionString) { } public DbSet<UserClass> Users { get; set; } public DbSet<LanguageClass> Languages { get; set; } }
Nele, determinamos a cadeia de conexão DBMS usada e os conjuntos de dados
Users
e
Languages
.
Adicione o arquivo
LanguagesRepositoryEF
classe
LanguagesRepositoryEF
:
public class LanguagesRepositoryEF : ILanguagesRepository { private EFDbContext context = new EFDbContext(); public IList<LanguageClass> List() { return context.Languages.OrderBy(x => x.LanguageName).ToList(); } }
E o arquivo
UsersRepository.cs com a classe
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; } }
Pode-se ver que o tamanho do arquivo é claramente menor que o do ADO.NET - o ORM faz o trabalho sujo para nós - ele cria consultas SQL por conta própria.
No entanto, me deparei com alguns pontos que foram incluídos na implementação do ADO.NET, mas não funcionaram no EF.
A primeira coisa que fiz para alterar o arquivo
UserClass.cs (no diretório
Domínio ): adicione outro campo para a operação normal da conexão entre as tabelas
Users
e
Languages
:
[HiddenInput(DisplayValue = false)] public int? LanguageID { get; set; }
E o segundo - descobriu-se que campos no MySQL, como o
Enum
, não funcionam com o EF. Provavelmente, a razão para isso é que a enumeração no código é um valor inteiro, mas no banco de dados os valores através do EF são lidos como texto (se em uma consulta do MySQL para ler os valores de um campo do tipo enum, o MySQL retorna apenas os valores de texto dessa enumeração). E se, na versão do ADO.NET, eu posso contornar isso usando a construção
CAST(u.SupporterTier AS UNSIGNED) as SupporterTier
, então, com a EF, essa metamorfose acabou por ser insuperável para mim - nenhuma das opções tentadas surgiu. Bem, como a tecnologia
Code First gera um campo do tipo
Enum
como um campo do tipo INT, tivemos que alterar o tipo do campo
SupporterTier
no banco de dados:
CHANGE COLUMN `SupporterTier` `SupporterTier` INT(4) UNSIGNED NOT NULL DEFAULT '1' ;
Selecionar repositório usando o parâmetro do aplicativo
Usaremos a implementação por meio do construtor, exatamente como está escrito no livro. Primeiro, precisamos criar interfaces para nosso repositório compartilhado: crie o arquivo
LanguagesRepository.cs no diretório
Repository com o conteúdo:
public interface ILanguagesRepository { IList<LanguageClass> List(); }
E o arquivo
UsersRepository.cs com o conteúdo:
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); }
Bem, herdamos as classes correspondentes dessas interfaces:
public class LanguagesRepositoryADO : ILanguagesRepository public class UsersRepositoryADO : IUsersRepository public class LanguagesRepositoryEF : ILanguagesRepository public class UsersRepositoryEF : IUsersRepository
Bem, no controlador
UsersController, fazemos adições que permitem trabalhar com estas interfaces:
private ILanguagesRepository repLanguages; private IUsersRepository repUsers; public UsersController(ILanguagesRepository langsParam, IUsersRepository usersParam) { repLanguages = langsParam; repUsers = usersParam; }
E no controlador,
repLanguages
os locais de acesso aos objetos dessas classes para os
repUsers
e
repUsers
, respectivamente. Mas precisamos passar instâncias das classes de repositório através do construtor do controlador, o que, obviamente, é inconveniente. Para evitar isso, precisamos de feitiçaria forte, como o
Dependency Resolver (DR). E para isso vamos usar o Ninject:
Registramos a DR no arquivo
Global.asax.cs no método
Application_Start
:
DependencyResolver.SetResolver(new NinjectDependencyResolver());
Crie o arquivo
NinjectDependencyResolver.cs no diretório
Infrastructure com a classe
NinjectDependencyResolver
(herdada da interface
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>(); } } }
E acontece que o único local em que é determinado qual método de trabalho com o DBMS é usado (diretamente, através do ADO.NET ou do Entity Framework) é o método
AddBindings
na classe
NinjectDependencyResolver
. Magia real, se você não sabe como funciona.
No método
AddBindings
, dependendo do valor do parâmetro do aplicativo ConnectionMethod, o
ILanguagesRepository
e
IUsersRepository
com classes específicas que implementam os métodos de interface. Desde que
NinjectDependencyResolver
aplicativo, registramos a DR como um objeto da classe
NinjectDependencyResolver
e, na classe, especificamos a ligação das interfaces do repositório a uma classe específica, ao solicitar a estrutura MVC para criar o
UsersController
controlador
UsersController
, o Ninject ao analisar a classe descobrirá que é necessária a implementação das
IUsersRepository
e
IUsersRepository
criará instâncias de classes específicas e as passará para o construtor do controlador (via DR e a estrutura MVC).
Total
O aplicativo agora também suporta o método de acesso ao DBMS através do ORM Entity Framework. Ao mesmo tempo, o método de acesso através do ADO.NET não desapareceu e é selecionado quando o aplicativo é iniciado pelo parâmetro, para o qual usamos o método de injeção de dependência através do construtor do controlador usando a biblioteca Ninject.

PS E finalmente: você pode ver como este projeto funciona
neste endereço . E
aqui você pode baixar o projeto inteiro. Bem, para a pilha - um link para o
meu blog .