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.
Introducción A raíz de la serie de post que estoy escribiendo acerca de ORMS ligeros con ( Dapper , Massive
Introducción A raíz de la serie de post que estoy escribiendo acerca de ORMS ligeros con
Artículos Error: error undefined in What’s New (Activity Feeds) By Pablo Peralta