What you want, from what I understand, is to implement your own Framework with CRUD operations. I think that in this first step it is not necessary to use dependency injection control inversion.
First think about what the data architecture will look like. You can use business classes like Repositories, for example, or else implement the Data Access Layer (DAL) directly. The ideal is that this layer of data access is based on an interface using a generic type. I’ll give an example to be clearer:
namespace MeuProjeto.Data.Interfaces
{
public interface IAcessoDados<T>
where T: class
{
List<T> Selecionar();
List<T> Selecionar(IEnumerable<Operador> operadores);
void Incluir(T objeto);
void Atualizar(T objeto);
}
}
Any and all data access classes must use this interface. This ensures that the methods are the same for all data classes.
Notice that Selecionar
is polymorphic. Calling without operators, you must implement a method that will select all elements of your data source (a relational database, for example). In the second you must implement a method that selects the data according to some parameters.
You can also implement a common class that implements this interface, more or less like this:
namespace MeuProjeto.Data
{
public abstract class Comum<T>: IDisposable, IComum<T>
where T: class
{
#region Propriedades
protected string ConexaoBancoDados { get; set; }
private String _whereOuAnd = " WHERE ";
protected String WhereOuAnd
{
get
{
var retorno = _whereOuAnd;
_whereOuAnd = " AND ";
return retorno;
}
}
private string _virgulaOuEspaco = " ";
public String VirgulaOuEspaco {
get
{
var retorno = _virgulaOuEspaco;
_virgulaOuEspaco = ", ";
return retorno;
}
}
protected void ReiniciarWhereEVirgula()
{
_whereOuAnd = " WHERE ";
_virgulaOuEspaco = " ";
}
#endregion
#region Construtores
protected Comum() { }
protected Comum(String conexaoBancoDados)
{
ConexaoBancoDados = conexaoBancoDados;
}
#endregion
#region Métodos
/// <summary>
/// Overload que retorna uma lista com todos os objetos do tipo T.
/// </summary>
/// <returns>
/// Lista com objetos do tipo Job.
/// </returns>
public virtual List<T> Selecionar()
{
return Selecionar(new List<Operador>());
}
/// <summary>
/// Método selecionar padrão. Recebe uma lista de operadores para selecionar do banco e devolver uma lista
/// </summary>
/// <param name="operadores"></param>
/// <returns></returns>
/// <remarks>Deve ser implementado em cada classe derivada.</remarks>
public abstract List<T> Selecionar(IEnumerable<Operador> operadores);
public abstract void Incluir(T objeto);
public abstract void Atualizar(T objeto);
#endregion
public void Dispose()
{
}
}
}
If your data classes inherit from this class Comum
, automatically C# forces you to implement the method Selecionar(IEnumerable<Operador> operadores)
(by being Abstract) and your data access class already wins a method called Selecionar()
no parameters. Then you can implement your class as that of a project of mine:
namespace MeuProjeto.Data
{
public class AtividadesComentarios : Comum<AtividadesComentario>, IDisposable
{
public AtividadesComentarios() { }
public AtividadesComentarios(String conexaoBancoDados) : base(conexaoBancoDados) { }
/// <summary>
///
/// </summary>
/// <param name="parametros"></param>
/// <returns></returns>
public override List<AtividadesComentario> Selecionar(IEnumerable<Operadores.Operador> operadores)
{
using (var obj = new Common.Database())
{
var sSql =
"select ac.ID_COMENTARIO, ac.ID_ATIVIDADE, ac.TEXTO, ac.DATA, ac.ID_USUARIO, ac.HISTORICO_ANTIGO " +
" from ATIVIDADES_COMENTARIOS ac ";
foreach (var operador in operadores)
{
sSql += WhereOuAnd + " ac." + operador;
}
var parametros = operadores.Where(o => o.GetType().IsAssignableFrom(typeof(Igual))).Select(o2 => ((Igual)o2).ParametroOracle).ToList();
var retorno = new List<AtividadesComentario>();
OracleConnection connection;
using (OracleDataReader reader = obj.ConsultarSqlReader(ConexaoBancoDados, sSql, parametros, out connection))
{
while (reader.Read())
{
retorno.Add(new AtividadesComentario
{
AtividadesComentarioId = reader.GetInt32(reader.GetOrdinal("ID_COMENTARIO")),
AtividadeId = reader.GetInt32(reader.GetOrdinal("ID_ATIVIDADE")),
UsuarioId = reader.GetInt32(reader.GetOrdinal("ID_USUARIO")),
HistoricoAntigoId = reader.GetInt32(reader.GetOrdinal("HISTORICO_ANTIGO")),
Texto = reader.GetOracleClob(reader.GetOrdinal("TEXTO")).Value,
Data = reader.GetDateTime(reader.GetOrdinal("DATA"))
});
}
reader.Close();
connection.Close();
}
return retorno;
}
}
/// <summary>
/// Inclui um novo comentário na atividade.
/// </summary>
/// <param name="objeto">O objeto a ser inserido.</param>
public override void Incluir(AtividadesComentario objeto)
{
try
{
var oDataBase = new Database();
objeto.AtividadesComentarioId = oDataBase.RecuperaIDSequence(ConexaoBancoDados, "SEQ_ID_COMENTARIO_ATIVIDADE");
const string sSql = "INSERT INTO ATIVIDADES_COMENTARIOS (ID_COMENTARIO, ID_ATIVIDADE, TEXTO, DATA, ID_USUARIO, HISTORICO_ANTIGO) " +
" VALUES (:ID_COMENTARIO, :ID_ATIVIDADE, :TEXTO, :DATA, :ID_USUARIO, :HISTORICO_ANTIGO)";
var oParams = ExtrairParametros(objeto);
oDataBase.ExecutaComandoNonQuery(ConexaoBancoDados, sSql, oParams.ToList());
}
catch (Exception ex)
{
throw ex;
}
}
public override void Atualizar(AtividadesComentario objeto)
{
throw new NotImplementedException();
}
}
}
That object oDatabase
is an object that communicates with the specific technology of your database (in my case, an Oracle database). Note that I haven’t left it too generic yet, but if you are improving this pattern (for example, doing the method Inserir
class Comum
read the properties of a data object and mount a dynamic SQL), I believe you can get a very cool result without using too much specific code.
Let me give you a few more tips on the methods I’ve built to read data objects via Reflection:
Extract Primary Keys from a Data Object
/// <summary>
/// Extrai a chave primária de um objeto (property decorada com o atributo [Key]).
/// </summary>
/// <param name="objeto">Um objeto pertencente ao Namespace Metadata.</param>
/// <returns></returns>
protected IEnumerable<OracleParameter> ExtrairChavesPrimarias(Object objeto)
{
var type = objeto.GetType();
var properties =
type.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
foreach (var property in properties)
{
var keyAttribute = Attribute.GetCustomAttribute(property, typeof (KeyAttribute)) as KeyAttribute;
if (keyAttribute != null)
{
var columnAttribute = (ColumnAttribute)property.GetCustomAttributes(typeof(ColumnAttribute), true).FirstOrDefault();
if (columnAttribute != null)
{
yield return new OracleParameter
{
ParameterName = columnAttribute.Name,
Value = property.GetValue(objeto, null)
};
}
else
{
yield return new OracleParameter
{
ParameterName = property.Name,
Value = property.GetValue(objeto, null)
};
}
}
}
}
Extract Parameters from a Data Object
/// <summary>
/// Método selecionar padrão. Recebe uma lista de operadores para selecionar do banco e devolver uma lista
/// </summary>
/// <param name="operadores"></param>
/// <returns></returns>
/// <remarks>Deve ser implementado em cada classe derivada.</remarks>
public abstract List<T> Selecionar(IEnumerable<Operador> operadores);
private IEnumerable<PropertyInfo> ExtrairPropertiesDeObjeto(Object objeto)
{
return objeto.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
}
/// <summary>
/// Monta uma lista de parâmetros por Reflection. Um parâmetro é uma property decorada com o atributo [Column].
/// </summary>
/// <param name="objeto"></param>
/// <returns></returns>
protected IEnumerable<OracleParameter> ExtrairParametros(Object objeto)
{
foreach (var propertyInfo in ExtrairPropertiesDeObjeto(objeto))
{
var columnAttribute = (ColumnAttribute)propertyInfo.GetCustomAttributes(typeof(ColumnAttribute), true).FirstOrDefault();
if (columnAttribute != null)
{
yield return new OracleParameter
{
ParameterName = columnAttribute.Name,
Value = propertyInfo.GetValue(objeto, null)
};
}
else
{
yield return new OracleParameter
{
ParameterName = propertyInfo.Name,
Value = propertyInfo.GetValue(objeto, null)
};
}
}
}
If you need me to detail the answer at any point, just talk.
Have you thought about using the Entity framework?
– Makah
So.. I thought of using Entity only to create my models. I wanted to do it in the hand to learn, so I imagine it will be easier to fix the concepts in the mind. Sometimes agent ends up using a framework like entities and doesn’t know exactly what happens inside. ;) @Makah
– eusouofernando
Hello, welcome to Stackoverflow, a suggestion is to put the language of which you search for information in the title of the question, this helps in the search for the answer.
– Luiz Carvalho
@Luizcarvalho thanks for the tip!
– eusouofernando
@Luizcarvalho where did you invent this? Nobody does it in the OS.
– Makah
@Makah, where did I invent it? it is obvious that they do... in addition to the tag the language/technology information used in the question helps and many other users with the same doubt and obviously the identification of someone who can answer it.
– Luiz Carvalho
Another ORM (Object-Relational mapper) that is quite simple and opensource is Dapper. I’ve used it quite successfully.
– brazilianldsjaguar
It’s @Luizcarvalho, we really need a moderation and a person who knows the rules of the OS to be able to put good practice references.
– Makah
@Luizcarvalho This was (little) discussed at meta. Give your opinion there.
– utluiz
@Makah This was (little) discussed at meta. Give your opinion there.
– utluiz