[ORM-Lite] PetaPoco

Introducción

En este post voy a continuar la serie acerca de ORMS ligeros donde ya he hablado de Dapper y Massive, en esta ocasion voy a tratar PetaPoco.

¿Qué es PetaPoco?

PetaPoco es otro mini ORM basado en Massive, pero a diferencia de este para trabajar con objetos POCO.

Algunas de sus caracteristicas:

– Es un único fichero que sea agrega al proyecto.

– Permite trabajar con objetos POCO.

– Permite la generación de los objetos POCO a través de unas plantillas T4.

¿Por dónde empezar?

Para poder empezar a trabajar con PetaPoco, podemos realizar de la siguiente manera:

– Agregando desde Nuget el paquete a nuestro proyecto.

– Agregar el fichero desde GitHub.

En el ejemplo que voy a utilizar, he agregado el paquete desde Nuget. Y he configurado las plantillas de tal forma que me genere las clases poco contra SQL Server y las mismas tablas que hemos utilizado en ejemplos anteriores.

Algunos Ejemplos

Gracias a la plantilla T4, se ha generado la siguiente clase en mi proyecto:

   1: public partial class PetaPocoDB : Database

   2:     {

   3:         public PetaPocoDB() 

   4:             : base("")

   5:         {

   6:             CommonConstruct();

   7:         }

   8:  

   9:         public PetaPocoDB(string connectionStringName) 

  10:             : base(connectionStringName)

  11:         {

  12:             CommonConstruct();

  13:         }

  14:         

  15:         partial void CommonConstruct();

  16:         

  17:         public interface IFactory

  18:         {

  19:             PetaPocoDB GetInstance();

  20:         }

  21:         

  22:         public static IFactory Factory { get; set; }

  23:         public static PetaPocoDB GetInstance()

  24:         {

  25:             if (_instance!=null)

  26:                 return _instance;

  27:                 

  28:             if (Factory!=null)

  29:                 return Factory.GetInstance();

  30:             else

  31:                 return new PetaPocoDB();

  32:         }

  33:  

  34:         [ThreadStatic] static PetaPocoDB _instance;

  35:         

  36:         public override void OnBeginTransaction()

  37:         {

  38:             if (_instance==null)

  39:                 _instance=this;

  40:         }

  41:         

  42:         public override void OnEndTransaction()

  43:         {

  44:             if (_instance==this)

  45:                 _instance=null;

  46:         }

  47:         

  48:         public class Record<T> where T:new()

  49:         {

  50:             public static PetaPocoDB repo { get { return PetaPocoDB.GetInstance(); } }

  51:             public bool IsNew() { return repo.IsNew(this); }

  52:             public object Insert() { return repo.Insert(this); }

  53:             public int Update(IEnumerable<string> columns) { return repo.Update(this, columns); }

  54:             public static int Update(string sql, params object[] args) { return repo.Update<T>(sql, args); }

  55:             public static int Update(Sql sql) { return repo.Update<T>(sql); }

  56:             public int Delete() { return repo.Delete(this); }

  57:             public static int Delete(string sql, params object[] args) { return repo.Delete<T>(sql, args); }

  58:             public static int Delete(Sql sql) { return repo.Delete<T>(sql); }

  59:             public static int Delete(object primaryKey) { return repo.Delete<T>(primaryKey); }

  60:             public static bool Exists(object primaryKey) { return repo.Exists<T>(primaryKey); }

  61:             public static T SingleOrDefault(object primaryKey) { return repo.SingleOrDefault<T>(primaryKey); }

  62:             public static T SingleOrDefault(string sql, params object[] args) { return repo.SingleOrDefault<T>(sql, args); }

  63:             public static T SingleOrDefault(Sql sql) { return repo.SingleOrDefault<T>(sql); }

  64:             public static T FirstOrDefault(string sql, params object[] args) { return repo.FirstOrDefault<T>(sql, args); }

  65:             public static T FirstOrDefault(Sql sql) { return repo.FirstOrDefault<T>(sql); }

  66:             public static T Single(object primaryKey) { return repo.Single<T>(primaryKey); }

  67:             public static T Single(string sql, params object[] args) { return repo.Single<T>(sql, args); }

  68:             public static T Single(Sql sql) { return repo.Single<T>(sql); }

  69:             public static T First(string sql, params object[] args) { return repo.First<T>(sql, args); }

  70:             public static T First(Sql sql) { return repo.First<T>(sql); }

  71:             public static List<T> Fetch(string sql, params object[] args) { return repo.Fetch<T>(sql, args); }

  72:             public static List<T> Fetch(Sql sql) { return repo.Fetch<T>(sql); }

  73:             public static List<T> Fetch(long page, long itemsPerPage, string sql, params object[] args) { return repo.Fetch<T>(page, itemsPerPage, sql, args); }

  74:             public static List<T> Fetch(long page, long itemsPerPage, Sql sql) { return repo.Fetch<T>(page, itemsPerPage, sql); }

  75:             public static List<T> SkipTake(long skip, long take, string sql, params object[] args) { return repo.SkipTake<T>(skip, take, sql, args); }

  76:             public static List<T> SkipTake(long skip, long take, Sql sql) { return repo.SkipTake<T>(skip, take, sql); }

  77:             public static Page<T> Page(long page, long itemsPerPage, string sql, params object[] args) { return repo.Page<T>(page, itemsPerPage, sql, args); }

  78:             public static Page<T> Page(long page, long itemsPerPage, Sql sql) { return repo.Page<T>(page, itemsPerPage, sql); }

  79:             public static IEnumerable<T> Query(string sql, params object[] args) { return repo.Query<T>(sql, args); }

  80:             public static IEnumerable<T> Query(Sql sql) { return repo.Query<T>(sql); }

  81:             

  82:             private Dictionary<string,bool> ModifiedColumns;

  83:             private void OnLoaded()

  84:             {

  85:                 ModifiedColumns = new Dictionary<string,bool>();

  86:             }

  87:             protected void MarkColumnModified(string column_name)

  88:             {

  89:                 if (ModifiedColumns!=null)

  90:                     ModifiedColumns[column_name]=true;

  91:             }

  92:             public int Update() 

  93:             { 

  94:                 if (ModifiedColumns==null)

  95:                     return repo.Update(this); 

  96:  

  97:                 int retv = repo.Update(this, ModifiedColumns.Keys);

  98:                 ModifiedColumns.Clear();

  99:                 return retv;

 100:             }

 101:             public void Save() 

 102:             { 

 103:                 if (repo.IsNew(this))

 104:                     repo.Insert(this);

 105:                 else

 106:                     Update();

 107:             }

 108:         }

 109:     }

 

Y las clases relativas a mis entidades:

   1: [TableName("CandidateInfo")]

   2:     [PrimaryKey("CandidateId", autoIncrement=false)]

   3:     [ExplicitColumns]

   4:     public partial class CandidateInfo : PetaPocoDB.Record<CandidateInfo>  

   5:     {

   6:         [Column] 

   7:         public Guid CandidateId 

   8:         { 

   9:             get

  10:             {

  11:                 return _CandidateId;

  12:             }

  13:             set

  14:             {

  15:                 _CandidateId = value;

  16:                 MarkColumnModified("CandidateId");

  17:             }

  18:         }

  19:         Guid _CandidateId;

  20:  

  21:         [Column] 

  22:         public string CandidateName 

  23:         { 

  24:             get

  25:             {

  26:                 return _CandidateName;

  27:             }

  28:             set

  29:             {

  30:                 _CandidateName = value;

  31:                 MarkColumnModified("CandidateName");

  32:             }

  33:         }

  34:         string _CandidateName;

  35:  

  36:         [Column] 

  37:         public string CandidateLastName 

  38:         { 

  39:             get

  40:             {

  41:                 return _CandidateLastName;

  42:             }

  43:             set

  44:             {

  45:                 _CandidateLastName = value;

  46:                 MarkColumnModified("CandidateLastName");

  47:             }

  48:         }

  49:         string _CandidateLastName;

  50:  

  51:         [Column] 

  52:         public DateTime CandidateBirthDate 

  53:         { 

  54:             get

  55:             {

  56:                 return _CandidateBirthDate;

  57:             }

  58:             set

  59:             {

  60:                 _CandidateBirthDate = value;

  61:                 MarkColumnModified("CandidateBirthDate");

  62:             }

  63:         }

  64:         DateTime _CandidateBirthDate;

  65:  

  66:         [Column] 

  67:         public byte[] CandidatePhoto 

  68:         { 

  69:             get

  70:             {

  71:                 return _CandidatePhoto;

  72:             }

  73:             set

  74:             {

  75:                 _CandidatePhoto = value;

  76:                 MarkColumnModified("CandidatePhoto");

  77:             }

  78:         }

  79:         byte[] _CandidatePhoto;

  80:  

  81:         [Column] 

  82:         public int CandidateCountry 

  83:         { 

  84:             get

  85:             {

  86:                 return _CandidateCountry;

  87:             }

  88:             set

  89:             {

  90:                 _CandidateCountry = value;

  91:                 MarkColumnModified("CandidateCountry");

  92:             }

  93:         }

  94:         int _CandidateCountry;

  95:  

  96:         [Column] 

  97:         public int CandidateCity 

  98:         { 

  99:             get

 100:             {

 101:                 return _CandidateCity;

 102:             }

 103:             set

 104:             {

 105:                 _CandidateCity = value;

 106:                 MarkColumnModified("CandidateCity");

 107:             }

 108:         }

 109:         int _CandidateCity;

 110:  

 111:         [Column] 

 112:         public string CandidateEmail 

 113:         { 

 114:             get

 115:             {

 116:                 return _CandidateEmail;

 117:             }

 118:             set

 119:             {

 120:                 _CandidateEmail = value;

 121:                 MarkColumnModified("CandidateEmail");

 122:             }

 123:         }

 124:         string _CandidateEmail;

 125:  

 126:     }

 127: [TableName("CountryInfo")]

 128: [PrimaryKey("CountryID")]

 129: [ExplicitColumns]

 130: public partial class CountryInfo : PetaPocoDB.Record<CountryInfo>  

 131: {

 132:     [Column] 

 133:     public int CountryID 

 134:     { 

 135:         get

 136:         {

 137:             return _CountryID;

 138:         }

 139:         set

 140:         {

 141:             _CountryID = value;

 142:             MarkColumnModified("CountryID");

 143:         }

 144:     }

 145:     int _CountryID;

 146:  

 147:     [Column] 

 148:     public string CountryName 

 149:     { 

 150:         get

 151:         {

 152:             return _CountryName;

 153:         }

 154:         set

 155:         {

 156:             _CountryName = value;

 157:             MarkColumnModified("CountryName");

 158:         }

 159:     }

 160:     string _CountryName;

 161:  

 162: }

 163:  

 164: [TableName("CityInfo")]

 165: [PrimaryKey("CityID")]

 166: [ExplicitColumns]

 167: public partial class CityInfo : PetaPocoDB.Record<CityInfo>  

 168: {

 169:     [Column] 

 170:     public int CityID 

 171:     { 

 172:         get

 173:         {

 174:             return _CityID;

 175:         }

 176:         set

 177:         {

 178:             _CityID = value;

 179:             MarkColumnModified("CityID");

 180:         }

 181:     }

 182:     int _CityID;

 183:  

 184:     [Column] 

 185:     public string CityName 

 186:     { 

 187:         get

 188:         {

 189:             return _CityName;

 190:         }

 191:         set

 192:         {

 193:             _CityName = value;

 194:             MarkColumnModified("CityName");

 195:         }

 196:     }

 197:     string _CityName;

 198:  

 199:     [Column] 

 200:     public int CountryID 

 201:     { 

 202:         get

 203:         {

 204:             return _CountryID;

 205:         }

 206:         set

 207:         {

 208:             _CountryID = value;

 209:             MarkColumnModified("CountryID");

 210:         }

 211:     }

 212:     int _CountryID;

 213:  

 214: }

Bien el primer paso vamos a ver como realizar un conjunto  de consultas a la Base de Datos:

Consultar Toda la Tabla

 

   1: var db = PetaPocoDB.GetInstance();

   2: var candidates = db.Fetch<PetaPoco.CandidateInfo>("SELECT * FROM CANDIDATEINFO");

   3: foreach (var candidate in candidates)

   4: {

   5:     Console.WriteLine(String.Format("Name: {0}", candidate.CandidateName));

   6: }

 

Consultar Filtrando

   1: var candidates = db.Fetch<PetaPoco.CandidateInfo>("SELECT * FROM CANDIDATEINFO WHERE CANDIDATELASTNAME LIKE @0","%Puertas%");

   2:  

   3:            foreach (var candidate in candidates)

   4:            {

   5:                Console.WriteLine(String.Format("Name: {0}", candidate.CandidateName));

   6:            }

 

   1: var candidates = db.Fetch<PetaPoco.CandidateInfo>(PetaPoco.Sql.Builder.

   2:                                                                     Select("*").

   3:                                                                     From("CandidateInfo").

   4:                                                                     Where("CandidateLastName like @0", "%Puertas%"));

   5:  

   6:             foreach (var candidate in candidates)

   7:             {

   8:                 Console.WriteLine(String.Format("Name: {0}", candidate.CandidateName));

   9:             }

 

Consulta Scalar

   1: var results = db.ExecuteScalar<long>("SELECT COUNT(*) FROM CANDIDATEINFO");

   2: Console.WriteLine(results.ToString());

Insertar un Dato

   1: var candidate = new CandidateInfo

   2:     {

   3:         CandidateName = "Álvaro",

   4:         CandidateLastName = "Torrecilla",

   5:         CandidateEmail = "a@torrecilla.com",

   6:         CandidateBirthDate = Convert.ToDateTime("30/12/2009"),

   7:         CandidateCountry = 1,

   8:         CandidateCity = 1

   9:     };

  10: db.Insert(candidate);

Actualizar un Dato

   1: var candidate = new CandidateInfo

   2: {

   3:     CandidateName = "Álvaro",

   4:     CandidateLastName = "Torrecilla",

   5:     CandidateEmail = "a@torrecilla.com",

   6:     CandidateBirthDate = Convert.ToDateTime("28/12/2009"),

   7:     CandidateCountry = 1,

   8:     CandidateCity = 1

   9: };

  10: db.Update(candidate, new Guid("00000000-0000-0000-0000-000000000000"));

Eliminar un Dato

   1: db.Delete<CandidateInfo>(new Guid("00000000-0000-0000-0000-000000000000"));

Peculiaridades

Durante las pruebas y examinando el código me he encontrado con algunas cosas curiosas:

– Los métodos IsNew y Save solo funcionan con columnas definidas en la Base de datos como Identity.

– Para Base de Datos Oracle se puede configurar a una determinada columna una secuencia.

– El mapeo de campos de tipo Image de SQL server los realiza a NVarchar por lo que genera problemas a la hora de generar inserciones a la BD.

– Si existe un valor por defecto definido en una columna de la BD, no lo utiliza, sino que genera el comando completo.

 

Conclusiones

PetaPoco nos permite de una forma rápida generar las clases POCO de nuestra BD gracias a la utilización de unas Plantillas T4, lo cual nos permite configurar y aportar nuestras propias mejoras a las mismas.

Tiene cosas mejorables, pero al ser un fichero que agregamos a nuestro proyecto, podemos incluir las características que consideremos oportunas.

3 comentarios sobre “[ORM-Lite] PetaPoco”

Deja un comentario

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