ORAPOCO: V2.0

Introducción

Bueno, como habréis podido notar ando creando un ORM “ligero” para trabajar contra Oracle (Serie ORAPOCO).

Bien, en la segunda actualización, tanto @Eiximenis como @Juanma, me indicaban algo que las clases POCO no debía implementar ninguna clase Base, algo que a mi tampoco me convencía haberlo agregado.

Además de ello Eduard comentaba la posibilidad de hacer configuración de los Mapeos, para evitar hacer uso de Atributos en las clases POCO y que están sean completamente POCO. El tema de los mapeos es parecido a lo que se ve en FluentNhibernate.

Por otro lado @_PedroHurtado, me comentaba que era posible hacer Sql Injection, y me propuso que modificara los parámetros String por Expression.

Bueno por estos motivos nace está nueva versión de ORAPOCO.

Cambios

Modificación de la Plantilla T4

He modificado la plantilla T4, de tal forma que va a generar objetos POCO, sin ningún tipo de atributo:

Objeto POCO:

1: public class BL_BLOQUEO_PANTALLA


2: {


3: public System.Int32 BLPA_ID {get;set;}


4: public System.String BLPA_PANTALLA {get;set;}


5: public System.String BLPA_FECHA {get;set;}


6: public System.String BLPA_HORA {get;set;}


7: public System.String BLPA_USUARIO {get;set;}


8: }


Del mismo modo se van a generar unas nuevas clases, que serán los Mapeadores por defecto:

1: public class BL_BLOQUEO_PANTALLAMapper : GenericMapper<BL_BLOQUEO_PANTALLA>


2: {


3: public Type GetMappedType()


4: {


5: return typeof(BL_BLOQUEO_PANTALLA);


6: }


7: public BL_BLOQUEO_PANTALLAMapper() : base()


8: {


9: AddColumnMapping(x=> x.BLPA_ID);


10: SetPrimaryKeyField(x => x.BLPA_ID);


11: AddColumnMapping(x=> x.BLPA_PANTALLA);


12: SetNullable(x=> x.BLPA_PANTALLA);


13: AddColumnMapping(x=> x.BLPA_FECHA);


14: SetNullable(x=> x.BLPA_FECHA);


15: AddColumnMapping(x=> x.BLPA_HORA);


16: SetNullable(x=> x.BLPA_HORA);


17: AddColumnMapping(x=> x.BLPA_USUARIO);


18: SetNullable(x=> x.BLPA_USUARIO);


19: }


20: }


Creación de los Objetos OracleColumnInfo,Mapper, IGenericMapper<T> y GenericMapper<T>

Para facilitar el mapeo de datos, he creado las siguientes clases e interfaz:

Clase OraceColumnInfo para representar la información relativa a cada columna de los objetos POCO.

1: public class OracleColumnInfo


2: {


3: public OracleColumnInfo()


4: {


5: IsPk = false;


6: Nullable = false;


7: }


8: public bool IsPk { get; set; }


9: public string ColumnName { get; set; }


10: public bool Nullable { get; set; }


11: public string DefaultValue { get; set; }


12: public string SequenceName { get; set; }


13: }


La Interfaz IGenericMapper<T>:

1: public interface IGenericMapper<T>


2: {


3: IGenericMapper<T> SetPrimaryKeyField(Expression<Func<T, object>> property);


4: IGenericMapper<T> AddColumnMapping(Expression<Func<T, object>> property);


5: IGenericMapper<T> SetNullable(Expression<Func<T, object>> property);


6: IGenericMapper<T> SetDefaultValue(Expression<Func<T, object>> property, string DefaultValue);


7: IGenericMapper<T> SetSequence(Expression<Func<T, object>> property, string SequenceName);


8: }


La clase Mapper: Va a servir para representar el conjunto de columnas de un objeto POCO.

La clase: GenericMapper<T>, va a extender a Mapper e implementar a IGenericMapper:

1: public class GenericMapper<T> : Mapper,IGenericMapper<T>


2: {


3: public GenericMapper()


4: {


5: Columns = new List<OracleColumnInfo>();


6: }


7: 


8: public IGenericMapper<T> AddColumnMapping(Expression<Func<T, object>> property)


9: {


10: PropertyInfo propertyInfo = GetProperty(property);


11: return this;


12: }


13:


14: public IGenericMapper<T> SetPrimaryKeyField(Expression<Func<T, object>> property)


15: {


16: PropertyInfo propertyInfo = GetProperty(property);


17: AddColumn(propertyInfo);


18: Columns.Where(col => col.ColumnName.Equals(propertyInfo.Name)).FirstOrDefault().IsPk = true;


19: return this;


20: }


21:


22: public IGenericMapper<T> SetNullable(Expression<Func<T, object>> property)


23: {


24: PropertyInfo propertyInfo = GetProperty(property);


25: AddColumn(propertyInfo);


26: Columns.Where(col => col.ColumnName.Equals(propertyInfo.Name)).FirstOrDefault().Nullable = true;


27: return this;


28: }


29: public IGenericMapper<T> SetDefaultValue(Expression<Func<T, object>> property, string DefaultValue)


30: {


31: PropertyInfo propertyInfo = GetProperty(property);


32: AddColumn(propertyInfo);


33: Columns.Where(col => col.ColumnName.Equals(propertyInfo.Name)).FirstOrDefault().DefaultValue = DefaultValue;


34: return this;


35: }


36: 


37: private void AddColumn(PropertyInfo propertyInfo)


38: {


39: var column = Columns.Where(col => col.ColumnName.Equals(propertyInfo.Name)).FirstOrDefault();


40: if (column == null)


41: Columns.Add(new OracleColumnInfo { ColumnName = propertyInfo.Name });


42: }


43: private static PropertyInfo GetProperty(Expression<Func<T, object>> property)


44: {


45: PropertyInfo propertyInfo = null;


46: if (property.Body is MemberExpression)


47: {


48: propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo;


49: }


50: else


51: {


52: propertyInfo = (((UnaryExpression)property.Body).Operand as MemberExpression).Member as PropertyInfo;


53: }


54: 


55: return propertyInfo;


56: }


57:


58: public IGenericMapper<T> SetSequence(Expression<Func<T, object>> property, string SequenceName)


59: {


60: PropertyInfo propertyInfo = GetProperty(property);


61: AddColumn(propertyInfo);


62: Columns.Where(col => col.ColumnName.Equals(propertyInfo.Name)).FirstOrDefault().SequenceName = SequenceName;


63: return this;


64: }


65:


66: }


Creación de ConfigureMapping y AutoConfigureMapping

Ambos métodos van  a servir para configurar los Mapeos, y serán invocados de forma Manual.

AutoConfigureMapping, va a registrar todas aquellas clases que Implementen AutoConfigureMapping dentro del Assembly en el que nos encontremos.

Código:

Primer Ejemplo: Como crear y registrar un nuevo Mapeo:

1: db.ConfigureMapping<SAMPLE>(new GenericMapper<SAMPLE>().


2: SetPrimaryKeyField(x => x.ID).


3: AddColumnMapping(x => x.NOMBRE).


4: SetDefaultValue(x => x.ID, «SysGuid()»));


El Mapeo permite llevar a cabo:

– Creación de columnas.

– Establecer PK Simple o Compuesta.

– Establecer Valor Por Defecto.

– Establecer Nulos.

– Establecer Secuencias.

A la hora de realizar una inserción, actualización o borrado, se va a comprobar si existe un mapeo Configurado, en caso de no existir se intentará ejecutar como si tuviese Atributos.

Nuevas formas de Ejecutar Consultas:

Al haber sustituido string por Expression, se ejecutarán las consultas del siguiente modo:

1: var data = db.Query<EMPRESA>(columns:


2: (x) => new { x.EMPR_NOMBRE_CORTO, x.EMPR_NOMBRE_EMPRESA },


3: where:


4: (x => ( x.EMPR_NOMBRE_EMPRESA.Contains(«D») ||


5: x.EMPR_CODIGO_EMPRESA != 101 ||


6: x.EMPR_AÑO >= 2008 ) && (


7: x.EMPR_MES == 1 )));


Los valores esperados para los parámetros “Columns”, “Where” y “Order”, van a ser expresiones lambda.

 

Finally

Bien como siempre tenéis las descargas en:

GitHub

Nuget

 

Y como siempre cualquier feedback será bien recibido.

5 comentarios sobre “ORAPOCO: V2.0”

  1. Hola Javier,

    Gracias por la iniciativa.

    Aunque no la he empezado a probar, seguro que si lo voy a hacer pronto, pero, recordando, en un post que hiciste de comparativa de ORM ligeros, hablábamos de inserciones batch y que algunos no las tenían; considerarías incluirlo o ya lo tiene ups, por no revisar antes, je,je?

    Un abrazo …….

  2. Hola Veranete,

    No lo he implementado pero, voy a darle una pensada y a evaluar su rendimiento y si veo buenos resultados lo pongo como feature.

  3. Muy buena iniciativa, sí.

    Hasta ahora utilizamos en el cliente ODP.NET (Oracle.DataAccess) con Ent.Library 4.1, y ahora para .NET 4+VS2010, EntLib 50 + EntLibContrib + Oracle.DataAccess 4.1.

    A ver si encuentro hueco para evaluar más ORAPOCO. Si incluye pruebas unitarias facilitaría mucho verlo.

    Creo que interesa un ORM ligero, y con soporte para:

    parámetros BINARY_FLOAT,BINARY_DOUBLE,BLOB,CLOB
    DATE, NUMBER, TIMESTAMP(6), VARCHAR2, UDT’s, XMLTYPE

    parámetros IN OUT de tipo SYS_REFCURSOR

    Funciones devuelven cursores
    FUNCTION GET_EMP_INFO RETURN SYS_REFCURSOR

    Múltiples cursores
    PROCEDURE GET_MULTIPLE_CURSORS(P_RC1 OUT SYS_REFCURSOR, P_RC2 OUT SYS_REFCURSOR, P_RC3 OUT SYS_REFCURSOR) IS

    Table Pipelined Functions
    FUNCTION GET_ROWS RETURN OUTPUT_SET PIPELINED

    FUNCTION GET_ROWS2 (p_Persona IN UDTPersona) RETURN UDTPersona_Tab PIPELINED is

    XMLType
    FUNCTION GET_XMLTYPE(num IN NUMBER, xml1 IN XMLTYPE) RETURN XMLTYPE IS

    PROCEDURE FRKDATA_TESTXML(qry in VARCHAR2, rslt OUT XMLTYPE) AS

    Saludos y muchas gracias.

    PD: No me he metido en ello, hay un Oracle Express creo, al estilo de Sql Server Express. Para testing puede ser interesante, en fin son muchos temas que ver.

  4. Hola @Kikenet,
    Lo primero gracias por comentar!

    A ORAPOCO todavía le queda mucho (o eso espero) por crecer 😀 espero contemplar bastantes de los temas que indicas 😀

    Por el tema de pruebas unitarias estoy en ello :-p Me guastaria publicarlas de aqui a 2 semanas aprox!

Responder a jtorrecilla Cancelar respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *