Utilizando System.Reflection e System.Attributes para a construção de uma Ferramenta ORM - Parte 4
Aqui faremos a codificação da nossa classe DAL genérica, bem como das classes que vão nos fornecer a possibilidade de deixar nosso código independente de Banco de Dados.
Aqui faremos a codificação da nossa classe DAL genérica, bem como das classes que vão nos fornecer a possibilidade de deixar nosso código independente de Banco de Dados.
Vamos começar implementando essa independência.
Primeiramente vamos construir a seguinte estrutura de pastas no nosso projeto:
Ilustração 9 – Estrutura de pastas da projeto “DataBaseLib”
1.1.1Namespace “DBObjectsFactory”
Vamos codificar a primeira classe deste projeto. Adicione uma nova classe de nome “Factory.cs”dentro da pasta “DBObjectsFactory”.
Essa classe será abstrata e será responsável por criar objetos de conexão e Data Adapters. O intuito é estabelecer as seguintes regras para a criação destes objetos:
A) Obrigar o uso de connection strings dentro de arquivos de configurações como app.config ou web.config;
B) De acordo com a connection string contida no arquivo de configuração, a classe irá retornar um objeto de conexão específico para cada banco. Porém não serão utilizados objetos tipados como SqlConnection ou SqlDataAdapter, que são específicos para provedores SqlServer. Iremos utilizar Interfaces (IDbConnection e IDbDataAdapter) que são herdadas e implementadas por todos os objetos de conexão, dataadapters entre outyros do Ado.Net.
C) Oferecer a opção de trabalhar com bancos de dados diferentes num mesmo projeto.
·Muitos projetos trabalham com BDs diferentes. Para o nosso exemplo, vamos assumir que um projeto possui um BD principal. Essa BD principal será representado por uma connection string e uma chave no node appsettings, que indicará qual a conectionstring do BD principal.
D) Proteger os métodos para que somente classes que herdem de Factory, possam ter acesso a eles;
Vamos ver o overview da classe:
Ilustração 10 – Estrutura da classe “Factory”
Primeiramente adicione todos os usings. Se necessário, adicione referências ao projeto. Atente também à nomenclatura que demos a essa Namespace.
Basicamente temos 2 métodos internos, que se utilizando de métodos privados, retornam um IDbConnection. O método que não recebe parâmetro busca no arquivo de configuração da aplicação, uma chave de nome “current.connection” que deverá estar contida no node “appsettings”. O método que recebe uma string como parâmetro busca no arquivo de configuração uma connectionstring do mesmo nome do parâmetro de entrada do método. Esses dois métodos leêm uma connectionstring e verifica qual é o providername da conexão. De acordo com o providername, ele criará um objeto de conexão específico para cada provider e retornará este objeto como sendo um IDbConnection. Os métodos que retornam DataAdapters, utilizam essa mesma lógica.
Vale lembrar que caso haja alguma inconsistência, como por exemplo, a não existência de connectionstrings no arquivo de configuração, ou não existência da chave “current.connection” no node appsettings, serão disparadas exceções.
Segue o código completo da classe:
using System;
using System.Data.Odbc;
using System.Data.OleDb;
using System.Data.SqlClient;
using System.Configuration;
using Oracle.DataAccess.Client;
using System.Data;
namespace GenDAL.Library.DataBaseLib.DBObjectsFactory
{
public abstract class Factory
{
private IDbDataAdapter GetCurrAdapter(string ConnNameConfig)
{
IDbDataAdapter retorno = null;
ConnectionStringSettingsCollection connections = ConfigurationManager.ConnectionStrings;
if (connections != null && connections.Count > 0)
{
foreach (ConnectionStringSettings setting in connections)
{
if (setting.Name.ToLower().Equals(ConnNameConfig.ToLower()))
{
switch (setting.ProviderName.ToLower())
{
case "system.data.sqlclient":
retorno = new SqlDataAdapter();
break;
case "system.data.odbc":
retorno = new OdbcDataAdapter();
break;
case "system.data.oracleclient":
retorno = new OracleDataAdapter();
break;
}
}
}
if (retorno == null)
{
throw new Exception("A chave 'current.connection' não possui o nome de uma conexão válida");
}
}
else
{
throw new Exception("Root de conectionStrings não encontrada no arquivo de configuração");
}
return retorno;
}
private IDbConnection GetConn(string ConnNameConfig)
{
IDbConnection retorno = null;
ConnectionStringSettingsCollection connections = ConfigurationManager.ConnectionStrings;
if (connections != null && connections.Count > 0)
{
foreach (ConnectionStringSettings setting in connections)
{
if (setting.Name.ToLower().Equals(ConnNameConfig.ToLower()))
{
switch (setting.ProviderName.ToLower())
{
case "system.data.sqlclient":
retorno = new SqlConnection(ConfigurationManager.ConnectionStrings[ConnNameConfig].ConnectionString);
break;
case "system.data.odbc":
retorno = new OdbcConnection(ConfigurationManager.ConnectionStrings[ConnNameConfig].ConnectionString);
break;
case "system.data.oracleclient":
retorno = new OracleConnection(ConfigurationManager.ConnectionStrings[ConnNameConfig].ConnectionString);
break;
}
}
}
if (retorno == null)
{
throw new Exception("A chave 'current.connection' não possui o nome de uma conexão existente no web config");
}
}
else
{
throw new Exception("Root de conectionStrings não encontrada no arquivo de configuração");
}
return retorno;
}
internal IDbConnection GetConnection()
{
string currentConnName = "";
try
{
try
{
currentConnName = ConfigurationManager.AppSettings["current.connection"];
}
catch
{
throw new Exception("Key 'current.connection' not found in AppSettings of the configuration file");
}
return this.GetConn(currentConnName);
}
catch (Exception ex)
{
throw ex;
}
}
internal IDbConnection GetConnection(string CurrentConnection)
{
try
{
return this.GetConn(CurrentConnection);
}
catch (Exception ex)
{
throw ex;
}
}
internal IDbDataAdapter GetAdapter(string currentConn)
{
IDbDataAdapter retorno = null;
try
{
retorno = this.GetCurrAdapter(currentConn);
}
catch (Exception ex)
{
throw ex;
}
return retorno;
}
internal IDbDataAdapter GetAdapter()
{
string currentConnName = "";
try
{
try
{
currentConnName = ConfigurationManager.AppSettings["current.connection"];
}
catch
{
throw new Exception("Key 'current.connection' not found in AppSettings of the configuration file");
}
return this.GetCurrAdapter(currentConnName);
}
catch (Exception ex)
{
throw ex;
}
}
}
}
A próxima classe que iremos criar, será a classe que irá conter os objetos de conexão, DataAdapters e Transactions. Esta classe irá herdar da classe abstrata Factory e também a interface IDisposable.
Portanto, desta maneira iremos encapsular a forma de obter, instanciar e destruir esses objetos, não permitindo outra maneira de conseguir utilizar esses recursos.
Vamos implementar também a interface IDisposable, para escrevermos nosso próprio código que irá destruir o objeto instanciado. Desta maneira poderemos fechar as conexões e dar o Commit em transactions no momento de destruição do objeto. Assim encapsularemos tais operações.
Essa classe é bem simples. Possui três campos privados, três propriedades (get e set) , dois construtores e um método de Dispose:
using System;
using System.Data;
using GenDAL.Library.DataBaseLib.DBObjectsFactory;
namespace GenDAL.Library.DataBaseLib.DataBaseManager
{
public class DBManager : Factory, IDisposable
{
#region Private Fields
private IDbConnection _conn;
private IDbTransaction _trans;
private IDbDataAdapter _adp;
#endregion
#region Properties
public IDbTransaction Trans
{
get { return _trans; }
set { _trans = value; }
}
public IDbConnection Conn
{
get { return _conn; }
set { _conn = value; }
}
public IDbDataAdapter Adp
{
get { return _adp; }
set { _adp = value; }
}
#endregion
#region Constructors
public DBManager()
{
this._conn = base.GetConnection();
this._conn.Open();
this._trans = this._conn.BeginTransaction();
this._adp = base.GetAdapter();
}
public DBManager(string currentConn)
{
this._conn = base.GetConnection(currentConn);
this._conn.Open();
this._trans = this._conn.BeginTransaction();
this._adp = base.GetAdapter(currentConn);
}
#endregion
#region IDisposable Members
public void Dispose()
{
if (this._trans != null && this._trans.Connection != null)
{
this._trans.Commit();
}
if (this._conn != null && this._conn.State != ConnectionState.Closed)
{
this._conn.Close();
}
this._trans = null;
this._conn = null;
}
#endregion
}
}
Como podemos ver temos um IDbConnection, um IDbDataAdapter e um IDbTransaction como propriedades.
Temos dois construtores, um vazio que inicializa o IDbConnection e o IDbDataAdapter utilizando os métodos da classe base “Factory” que retornam uma conexão e um DataAdapter baseando-se na connectionstring principal da aplicação. O outro construtor faz a mesma coisa baseando-se em uma connecrtionstring específica, baseando – se na string passada como parâmetro, que deverá ser o valor de “name” de uma chave de connectionstring.
O método Dispose além de dar o Commit da transação, fecha a conexão.
Deste modo, em nossa classe DAL genérica, podemos impor que a classe que a consumir deverá colocar todas as operações de acesso a dados dentro de uma transação, como veremos a seguir.
Com essas classes implementamos dois requisitos de nosso projeto que são:
·Independência de banco de dados;
·Todas as operações de bancos de dados deverão estar associadas à uma transação (transaction);
Artigos relacionados
-
Artigo
-
Artigo
-
Artigo
-
Artigo
-
Artigo