Classes e Métodos Genéricos permitem que não sejam especificados os tipos até que sejam declarados e instanciados pelo código. Os genéricos são usados com mais frequência com coleções e com os métodos que operam nelas e que podem ser usados para desenvolvimento de classes que pegam dados do banco de dados e jogam em classes que servem como Camada Modelo dentro de um modelo MVC por exemplo.
Exemplo de Classe Genérica: Explicar public class ListaGenerica<T> { public void Add(T input) { } } //Eu posso posteriormente instanciar e inicializar como // Inteiro ListaGenerica<int> Lista1 = new ListaGenerica<int>(); Lista1.Add(1); // String ListaGenerica<string> Lista2 = new ListaGenerica<string>(); Lista2.Add(“Teste”); // Outra classe qualquer ListaGenerica<QualquerClass> Lista3 = new ListaGenerica< QualquerClass >(); Lista3.Add(new QualquerClass);
Agora vamos aplicar esta funcionalidade num modelo pratico onde crio um método que pego um Select consulto no banco de dados, jogando num DataReader, deste DataReader iremos jogar nas classes que fazem papel de Model no modelo MVC.
Sem usar Genéricos para cada tabela você repetiria muitos dos métodos, tornando complicado e mais demorado para dar manutenção, maiores chances de erros, etc. Se utilizando de Heranças, Generics, classes abstratas, podemos criar Métodos de busca de dados no banco que podem ser usadas para qualquer classe que faça papel de Model, variando praticamente somente a parte de pegar do datareader para a classe modelo, por obviamente cada tabela do banco e classe Model que a representa ter campos diferentes. Vamos considerar o cenário no banco de dados:
Usuarios | Produtos | |||||
Campo | Tipo | Tamanho | Campo | Tipo | Tamanho | |
Chave | INT | Chave | INT | |||
Usuario | Varchar | 50 | Codigo | Varchar | 30 | |
Senha | Varchar | 50 | Descricao | Varchar | 100 | |
Estoque | Float | |||||
Preco | Float |
Logo temos a representação dos modelos das duas tabelas acima da seguinte forma e vamos conforme post anterior já se aproveitar do conceito de Herança onde herda de BaseModel que tem o atributo Chave que será comum em todas tabelas do banco de dados:
public class UsuarioModel : BaseModel { [DisplayName("Usuário do Sistema")] public string Usuario { get; set; } [DisplayName("Senha")] public string Senha { get; set; } } public class ProdutoModel : BaseModel { [DisplayName("Código do Produto")] public string Codigo{ get; set; } [DisplayName("Descrição")] public string Descricao { get; set; } [DisplayName("Estoque Total")] public double Estoque { get; set; } [DisplayName("Preço em R$")] public double Preco { get; set; } }
Acima temos as duas classes que tem atributos diferentes tanto em quantidade de atributos como em tipos diferentes. Então vamos agora construir as classes de acesso a banco de dados de forma genérica e abstrata (classes que não são instanciáveis e servem de base para outros objetos) para que faça a coleta dos dados no banco de dados, jogue pra um dataReader e este jogue para a classe de usuarios e produtos.
Primeiro vamos criar uma classe que retire os dados de um DBDataReader e jogue em uma classe ConversorDataReader dentro de um array de string com as colunas que será usado posteriormente
public sealed class ConversorDataReader { #region Atributos private readonly DbDataReader dbDataReader; private readonly string[] colunas; #endregion #region Construtores public ConversorDataReader (DbDataReader dbDataReader) { this.dbDataReader = dbDataReader ?? throw new ArgumentNullException(nameof(dbDataReader)); this.colunas = new string[dbDataReader.FieldCount]; for (int i = 0; i < dbDataReader.FieldCount; i++) this.colunas[i] = dbDataReader.GetName(i); } #endregion // Método para retornar uma string do dbDataReader public string ConverterString(string nomeColuna) { return this.dbDataReader[nomeColuna].ToString(); } // Método para retornar uma Inteiro dbDataReader public int? ConverterInt32(string nomeColuna) { return Convert.ToInt32(this.dbDataReader[nomeColuna]); } }
Criaremos a classe AcessoBaseDados que recebe uma classe Generica T, que posteriormente iremos usar passando a classe Modelo desejada, neste caso usamos a classe System.Data.SqlCLient (SqlServer)
public abstract class AcessoBaseDados<T> { // classe abstrata que devera ser implementada quando instanciar classe herdada de AcessoBaseDados protected abstract T CriaRegistro(ConversorDataReader conversorDataReader); // classe que irá executar o comando sql passado por parametro e retornar uma classe que instanciar T protected ObservableCollection<T> BuscarRegistros(string commandText) { // Coleção de T genericos que irá retornar ao fim do metodo ObservableCollection<T> registros = new ObservableCollection<T>(); // configuração de conexão SqlConnection conexao = new SqlConnection(); conexao.ConnectionString = <connectionString a ser usada>; try { // criação do comando que será executado SqlCommand comando= new SqlCommand { CommandText = commandText, }; // abrindo conexão commando.Connection = conexao ; commando.Connection.Open(); // Criando dataReader com o resutlado do ExecuteReader using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader()) { while (sqlDataReader.Read()) { // para cada registro retorndo do bando e dentro do dataReader, será cridoa dum registro do tipo T e adicionado na lista de registros var registro = this.CriaRegistro(new ConversorDataReader (sqlDataReader)); registros.Add(registro); } sqlDataReader.Close(); } } catch (SqlException) { throw; } finally { if (commando!= null) commando.Dispose(); if (conexao!= null) conexao.Dispose(); } // retornando a lista de registros return registros; } }
Acima temos alguns pontos importantes que destacamos
Criamos a classe abstrata CriaRegistro que ela somente serve como Modelo para quando for instanciado esta classe esta sim terá para cada classe modelo sua implementacao diferente, para seus atributos diferentes. Ela retorna um tipo Genérico T que será as classes modelos
Criamos a classe BuscaRegistros que retorna uma ObservableCollection Genérica (que poderia ser qualquer tipo de lista desejada como List por exemplo e dentro dela fazemos a conexão com banco de dados e usando o parametro de entrada commandText que será o select a ser executado, irá gerar uma lista com os registros que resultarem deste select.. Veja que ele executa o select (executeReader) e pra cada registro passara no metodo CriaRegistro que retornará o registro no Tipo T Generico e ser adicionado na lista “registros” que é uma coleçao de objeto do Tipo T Genérico.
Desta forma, até o momento não nos importamos com os atributos que serão tratados dentro da classe, podendo ser usada para qualquer modelo então a partir de agora sempre que se conectar no banco pode ser usado este Método Genérico já trazendo os dados para a classe modelo desejada
Vamos agora implementar o uso destas , mas agora sim com as classes Model inicialmente criadas, onde tomaremos por exemplo a classe UsuarioViewModel que vai herdar de AcessoBasedados e onde estava o T Genérico agora terá a classe desejada
public class UsuarioViewModel : AcessoBaseDados<UsuarioModel> { // Construtor public UsuarioViewModel () { } public void RetornaLista() { // defino o select a ser usado string comando = "SELECT CHAVE, USUARIO, SENHA FROM USUARIOS" // crio uma lista que recebera os dados do select ObservableCollection<UsuarioModel> ListaUsuarios = new ObservableCollection<UsuarioModel>; // faço chamada do Buscar Registros ListaUsuarios = BuscarRegistros(comando); } // Override (sobrescrita) da classe abstrata CriaRegistro mas retornando agora no modelo desejado protected override UsuarioModel CriaRegistro(ConversorDataReader conversorDataReader ) { UsuarioModel registro = new UsuarioModel (); registro.Chave = conversorDataReader.ConverterInt32("CHAVE"); registro.Usuario= conversorDataReader.ConverterString("USUARIO"); registro.Senha= conversorDataReader.ConverterString("SENHA"); return registro; } }
No código acima podemos verificar que retornar registros de usuarios (UsuarioModel) ou qualquer outra classe (poderia ser por exemplo a ProdutoModel é bem simples basta seguir os seguintes passos:
- Criar a classe herdando da AcessoBaseDados e passando que classe Model voce deseja usar;
- Criar um método qualquer onde voce criará uma Lista da Classe Model desejada (deve ser a mesma da criação da classe) ;
- Dentro deste método basta passar o comando sql para o metodo que vem herdado de AcessoBaseDados que ele irá executar e retornar no tipo de Dados (Classe Model) que voce criou o objeto;
- Fazer o Override da Classe CriaRegistro onde voce faz o de-para do datareader para a classe desejada;
Toda questão de conexão com banco de dados, (abrir , fechar conexão, montar o command, criar dataReader, ler cada registro do datareader) está pronto para ser usado para qualquer tipo de classe desejada e não precisa ser desenvolvido para cada tabela do banco de dados o que geraria muito trabalho para dar manutenção e mais código a ser escrito, ficando somente o que é essencialmente particular para cada tipo de dados para ser programado no local desejado.
Views: 1