Posts Tagged ‘DAO’
DAOs sobre Dapper
La verdad es que los micro-ORMs están de moda (Massive, Dapper, PetaPOCO, SimpleData, etc). Basándome en esta abstracción y en mi querido Gentle.NET, me he decido a construir unos DAOs sobre Dapper, que es usado en Stackoverflow, y como características principales podemos decir: que es fuertemente tipado con una interfaz muy clara, y como extra soporta operaciones asíncronas a DB.
Creo que la mejor manera de entender el funcionamiento es con un ejemplo:
class Example { class Message : BusinessObject<Message> { [PrimaryKey] public Guid Id { get; set; } public string DestinationAddress { get; set; } public string SourceAddress { get; set; } public string UserData { get; set; } public int Ton { get; set; } } public void Example1() { Message m = new Message() { Id = Guid.NewGuid(), DestinationAddress = "34612345678", SourceAddress = "404", UserData = "Example 01", Ton = 1 }; m.Insert(); List<Message> l = Message.FindBy(new SqlCondition("DestinationAddress", Operator.Equals, "34612345678")); m.Remove(); } }
A continuación mostraremos el código de las funciones básicas de CRUD.
public class BusinessObject<T> where T: class { public static SqlConnection GetConnection() { return ConnectionFactory.Create(typeof(T).Namespace); } public static T Retrieve(object id) { string sql = string.Empty; DynamicParameters p = new DynamicParameters(); p.Add(GetPrimaryKey(), id, null, null, null); StringBuilder sb = new StringBuilder() .AppendFormat("select * from {0} where {1} = @{1}", GetTableName(), GetPrimaryKey()); return queryInternal(sb.ToString(), p).FirstOrDefault(); } public void Insert() { string sql = string.Empty; DynamicParameters p = new DynamicParameters(); StringBuilder columns = new StringBuilder(), values = new StringBuilder(); bool first = true; foreach (string prop in GetProperties()) { if (first) { columns.AppendFormat("{0}", prop); values.AppendFormat("@{0}", prop); } else { columns.AppendFormat(", {0}", prop); values.AppendFormat(", @{0}", prop); } p.Add(prop, getPropertyValue(this, prop), null, null, null); first = false; } sql = string.Format("insert into {0} ({1}) values ({2})", GetTableName(), columns.ToString(), values.ToString()); int res = executeInternal(sql, p); } public void Update() { // ... } public void Remove() { // ... } public static List<T> FindBy(SqlCondition[] conditions) { DynamicParameters p = new DynamicParameters(); StringBuilder sb = new StringBuilder() .AppendFormat("select * from {0} where", typeof(T).Name); bool first = true; int i = 0; foreach (SqlCondition c in conditions) { // for multiple conditions with same column string var = c.PropertyName + (i++); if (!first) sb.Append(" AND"); if (c.Value == null) { sb.AppendFormat(" {0} {1} NULL", c.PropertyName, OperatorHelper.ToString(c.Operator)); } else { sb.AppendFormat(" {0} {1} @{2}", c.PropertyName, OperatorHelper.ToString(c.Operator), var); p.Add(var, c.Value, null, null, null); } first = false; } return queryInternal(sb.ToString(), p); }
protected static List<T> queryInternal(string sql, object param) { List<T> l = null; using (var conn = GetConnection()) { try { conn.Open(); l = conn.Query<T>(sql, param, null, false, 0, null).ToList(); } finally { conn.Close(); } } return l; }
Faltaría algo de código para representar las condiciones SQL, así como algunas funciones que usan reflección para optener el nombre de la tabla, propiedades o clave primaria.
Abstrayendo operaciones básicas en business objects
Tras leer el buen artículo de Matt Long, se me ocurrió que tal podría aplicar estos principios para facilitar mi trabajo con Gentle.NET (increíble pero cierto) y tal vez que fuera el origen de un cambio en el motor de persistencia.
Así podríamos tener una clase base para todos nuestros objetos de negocio.
public class BusinessObject<T> : Persistent where T : class { static protected PersistenceBroker persistenceBroker; protected Type _type; static protected BusinessObject<T> _objToRetrieve = null; protected bool _empty = true; protected bool _changed = false; [TableColumn("id", NotNull = true), PrimaryKey(AutoGenerated = false)] public int Id { get; set; } public bool Changed { get { return _changed; } } static BusinessObject() { // TODO Singleton if (_objToRetrieve == null) _objToRetrieve = new BusinessObject<T>(); if (persistenceBroker == null) persistenceBroker = new PersistenceBroker(typeof(T)); } static public T Retrieve(int id) { return _objToRetrieve.retrieve(id); } protected T retrieve(int id) { Key key = new Key(typeof(T), true, "Id", id); return ((PersistenceBroker)persistenceBroker).RetrieveInstance(typeof(T), key) as T; } public override void Persist() { if (Changed || !IsPersisted) { base.Persist(); _changed = false; } } public override void Remove() { base.Remove(); } // TODO FindBy public override bool Equals(object obj) { bool result = false; if (obj != null) result = ((this.GetType() == obj.GetType()) && (((BusinessObject<T>)this).Id == ((BusinessObject<T>)obj).Id)); return result; } public override int GetHashCode() { return Id.GetHashCode(); } }
[Serializable] [TableName("Example")] class Exmple : BusinessObject<Example> { [TableColumn("name", NotNull = false)] public string Name { get; set; } [TableColumn("description", NotNull = false)] public string Description { get; set; } [TableColumn("date", NotNull = false)] public DateTime Date { get; set; } [TableColumn("error", NotNull = false)] public bool Error { get; set; } }
Microsoft y el acceso a datos
A través de Ayende, descubro otra nueva forma para realizar el acceso a datos. Ayende critica no sin razón este nuevo método y parece que no es el único, porque desde microsoft llegan explicaciones, realmente algo positivo que se genere conversación.
Mi opinión es que este es un campo en el que Microsoft no lo ha estado haciendo bien, aunque parecía que iban por buen camino con EF, ahora de nuevo tenemos una nueva tecnología que sólo crea incertimbre.
En aplicaciones empresariales no podemos estar cambiando cada dos años de tecnología de acceso a datos; necesitamos tecnologías que perduren en el tiempo y que sigan manteniéndose y eso es lo que parece que Microsoft no ofrece. Creo que ahora mismo la única opción es nHibernate, EF ha mejorado mucho situandolo al nivel de nHibernate, pero quién dice que seguirá haciéndolo.
Hay gente que defiende que tener opciones no es malo, de hecho es algo corriente en otras tecnologías, como Java. Pero creo que se debe explicar muy claramente cual es el ámbito de cada una, y no crear confusión con ellas. En este caso parece que va enfocado al desarrollo usando WebMatrix.
Para solucionar todos estos problemas, creo que es fundamental que se actualizen la documentación de p&p Data Guidance. Y por supuesto intentar aceptar soluciones externas sobre todo cuando éstas son mejores que las que se proponen.
Acceso a Datos, NHibernate: Ayende / Romaniello
Interesante diálogo entre Ayende y Romaniello, del que podemos aprender bastante sobre NHibernate, DAOs y arquitectura de aplicaciones. Muy recomendable tanto el contenido como algunos de sus enlaces.