Enlaces e información

Comenta lo que creas oportuno.


Otros posts
Guerra abierta al switch… case
Syd Barret ha muerto…

MonoRails o .NET on Rails

Escrito por Roberto M. Oliva en Julio 25th, 2006

Tenia que pasar. Algo tan espectacular como Ruby on Rails no podía dejar indiferente al resto del mundo informático. Sabía que algo así existiría y lo he encontrado: Un framework estilo Rails pero no sobre Ruby, sino sobre .NET. Esta plataforma la han denominado: MonoRails.
El modelo de datos se basa en un framework denominado ActiveRecord (si, bastante original). Este framework no es más que una adaptación del patrón ActiveRecord sobre la librería de objetos persistentes: NHibernate, proporcionando además una serie de ventajas, entre la que destaca la generación de la declaración de las clases usando reflexión. Lo cual elimina los ficherso XML declarativos característicos de Hibernate.
Ultimamente estoy usando las librerias ActiveRecord de MonoRails para mapear la base de datos en mi aplicación, ayudando mucho al modelo de negocios: reduce el código generado, facilita los cambios y, por tanto, es una gran base para el desarrollo de la metodología basada en TDD (Test Driven Development) o BDD (Behaviour Driven Development).

Como ejemplo os acordais de como se mapeaba un modelo: Categoria-Tema en Ruby on Rails? Algo similar con ActiveRecord sería lo siguiente:

Clase CCategoría que representa un árbol:


{\rtf1\ansi\ansicpg\lang1024\noproof1252\uc1 \deff0{\fonttbl{\f0\fnil\fcharset0\fprq1 Courier New;}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0??;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;??\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;??\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;??\red192\green192\blue192;}??\fs20 [\cf10 ActiveRecord\cf0 (\cf13 “Categoria”\cf0 )]\par ?? \cf2 public\cf0 \cf2 class\cf0 \cf10 CCategoria\cf0 : \cf10 ActiveRecordBase\cf0 <\cf10 CCategoria\cf0 >\par ?? \{\par ?? \cf2 private\cf0 \cf2 int\cf0 _id;\par ?? \cf2 private\cf0 \cf2 string\cf0 _nombre;\par ?? \cf2 private\cf0 \cf2 string\cf0 _descripcion;\par ?? \cf2 private\cf0 \cf10 CCategoria\cf0 _padre;\par ?? \cf2 private\cf0 \cf10 EntitySet\cf0 <\cf10 CCategoria\cf0 > _subCategorias;\par ?? \cf2 private\cf0 \cf10 EntitySet\cf0 <\cf10 CArchivo\cf0 > _archivos;\par ??\par ?? \cf15 ///\cf11 \cf15 <summary>\par ??\cf0 \cf15 ///\cf11 Identificador de la categoria\par ??\cf0 \cf15 ///\cf11 \cf15 </summary>\par ??\cf0 [\cf10 PrimaryKey\cf0 (\cf10 PrimaryKeyType\cf0 .Identity, \cf13 “Id”\cf0 )]\par ?? \cf2 public\cf0 \cf2 int\cf0 ID\par ?? \{\par ?? \cf2 get\cf0 \{ \cf2 return\cf0 _id; \}\par ?? \cf2 set\cf0 \{ _id = \cf2 value\cf0 ; \}\par ?? \}\par ??\par ?? [\cf10 BelongsTo\cf0 (\cf13 “padre_id”\cf0 )]\par ?? \cf2 public\cf0 \cf10 CCategoria\cf0 Padre\par ?? \{\par ?? \cf2 get\cf0 \{ \cf2 return\cf0 _padre; \}\par ?? \cf2 set\cf0 \{ _padre = \cf2 value\cf0 ; \}\par ?? \}\par ??\par ?? [\cf10 HasMany\cf0 (\cf2 typeof\cf0 (\cf10 CCategoria\cf0 ), Table = \cf13 “categoria”\cf0 ,\par ?? ColumnKey = \cf13 “padre_id”\cf0 , RelationType = \cf10 RelationType\cf0 .Set,\par ?? CustomAccess = \cf13 “NHibernate.Generics.GenericAccessor, NHibernate.Generics”\cf0 ,\par ?? OrderBy = \cf13 “Nombre”\cf0 )]\par ?? \cf2 public\cf0 \cf10 EntitySet\cf0 <\cf10 CCategoria\cf0 > SubCategorias\par ?? \{\par ?? \cf2 get\cf0 \{ \cf2 return\cf0 _subCategorias; \}\par ?? \cf11 //set \{ _subCategorias = value; \}\par ??\cf0 \}\par ??\par ?? [\cf10 HasMany\cf0 (\cf2 typeof\cf0 (\cf10 CArchivo\cf0 ), Table = \cf13 “archivo”\cf0 ,\par ?? ColumnKey = \cf13 “padre_id”\cf0 , RelationType = \cf10 RelationType\cf0 .Set,\par ?? CustomAccess = \cf13 “NHibernate.Generics.GenericAccessor, NHibernate.Generics”\cf0 ,\par ?? OrderBy = \cf13 “titulo”\cf0 )]\par ?? \cf2 public\cf0 \cf10 EntitySet\cf0 <\cf10 CArchivo\cf0 > Archivos\par ?? \{\par ?? \cf2 get\cf0 \{ \cf2 return\cf0 _archivos; \}\par ?? \cf11 //set \{ _subCategorias = value; \}\par ??\cf0 \}\par ??\par ?? \cf15 ///\cf11 \cf15 <summary>\par ??\cf0 \cf15 ///\cf11 Nombre de la categoria, es obligatorio\par ??\cf0 \cf15 ///\cf11 \cf15 </summary>\par ??\cf0 [\cf10 Property\cf0 (\cf13 “Nombre”\cf0 )]\par ?? \cf2 public\cf0 \cf2 string\cf0 Nombre\par ?? \{\par ?? \cf2 get\cf0 \{ \cf2 return\cf0 _nombre; \}\par ?? \cf2 set\cf0 \{ _nombre = \cf2 value\cf0 ; \}\par ?? \}\par ??\par ?? \cf15 ///\cf11 \cf15 <summary>\par ??\cf0 \cf15 ///\cf11 Descripcion de la categoria, no es obligatorio\par ??\cf0 \cf15 ///\cf11 \cf15 </summary>\par ??\cf0 [\cf10 Property\cf0 (\cf13 “Descripcion”\cf0 )]\par ?? \cf2 public\cf0 \cf2 string\cf0 Descripcion\par ?? \{\par ?? \cf2 get\cf0 \{ \cf2 return\cf0 _descripcion; \}\par ?? \cf2 set\cf0 \{ _descripcion = \cf2 value\cf0 ; \}\par ?? \}\par ??\par ??\cf2 #region\cf0 Constructor\par ??\par ?? \cf15 ///\cf11 \cf15 <summary>\par ??\cf0 \cf15 ///\cf11 Constructor sin parametros.\par ??\cf0 \cf15 ///\cf11 Hay que inicializar valores para que sea valido\par ??\cf0 \cf15 ///\cf11 \cf15 </summary>\par ??\cf0 \cf2 private\cf0 CCategoria()\par ?? \{\par ?? \cf11 // Valores por defecto:\par ??\cf0 _nombre = \cf13 “”\cf0 ;\par ?? _descripcion = \cf13 “”\cf0 ;\par ?? _id = 0;\par ?? _padre = \cf2 null\cf0 ;\par ??\par ?? \cf11 // Crear la lista donde se guardaran los usuarios\par ??\cf0 _subCategorias = \cf2 new\cf0 \cf10 EntitySet\cf0 <\cf10 CCategoria\cf0 >();\par ?? _archivos = \cf2 new\cf0 \cf10 EntitySet\cf0 <\cf10 CArchivo\cf0 >();\par ?? \}\par ??\par ?? \cf15 ///\cf11 \cf15 <summary>\par ??\cf0 \cf15 ///\cf11 Constructor con los minimos parametros\par ??\cf0 \cf15 ///\cf11 \cf15 </summary>\par ??\cf0 \cf15 ///\cf11 \cf15 <param name=”nombre”></param>\par ??\cf0 \cf2 private\cf0 CCategoria(\cf2 string\cf0 nombre, \cf2 string\cf0 descripcion, \cf10 CCategoria\cf0 objCategoriaPadre) : \cf2 this\cf0 ()\par ?? \{\par ?? _nombre = nombre;\par ?? _descripcion = descripcion;\par ?? _padre = objCategoriaPadre;\par ?? \}\par ??\par ?? \cf2 public\cf0 \cf2 static\cf0 \cf10 CCategoria\cf0 CreateCategoria(\cf2 string\cf0 nombre, \cf2 string\cf0 descripcion, \cf10 CCategoria\cf0 objCategoriaPadre)\par ?? \{\par ?? \cf2 return\cf0 \cf2 new\cf0 \cf10 CCategoria\cf0 (nombre, descripcion, objCategoriaPadre);\par ?? \}\par ??\par ??\cf2 #endregion\par ??\par ??\cf0 \}\par ??}
–>

9 [ActiveRecord(”Categoria”)]

10 public class CCategoria : ActiveRecordBase<CCategoria>

11 {

12 private int _id;

13 private string _nombre;

14 private string _descripcion;

15 private CCategoria _padre;

16 private EntitySet<CCategoria> _subCategorias;

17 private EntitySet<CArchivo> _archivos;

18

19 /// <summary>

20 /// Identificador de la categoria

21 /// </summary>

22 [PrimaryKey(PrimaryKeyType.Identity, “Id”)]

23 public int ID

24 {

25 get { return _id; }

26 set { _id = value; }

27 }

28

29 [BelongsTo(”padre_id”)]

30 public CCategoria Padre

31 {

32 get { return _padre; }

33 set { _padre = value; }

34 }

35

36 [HasMany(typeof (CCategoria), Table = “categoria”,

37 ColumnKey = “padre_id”, RelationType = RelationType.Set,

38 CustomAccess = “NHibernate.Generics.GenericAccessor, NHibernate.Generics”,

39 OrderBy = “Nombre”)]

40 public EntitySet<CCategoria> SubCategorias

41 {

42 get { return _subCategorias; }

43 //set { _subCategorias = value; }

44 }

45

46 [HasMany(typeof (CArchivo), Table = “archivo”,

47 ColumnKey = “padre_id”, RelationType = RelationType.Set,

48 CustomAccess = “NHibernate.Generics.GenericAccessor, NHibernate.Generics”,

49 OrderBy = “titulo”)]

50 public EntitySet<CArchivo> Archivos

51 {

52 get { return _archivos; }

53 //set { _subCategorias = value; }

54 }

55

56 /// <summary>

57 /// Nombre de la categoria, es obligatorio

58 /// </summary>

59 [Property(”Nombre”)]

60 public string Nombre

61 {

62 get { return _nombre; }

63 set { _nombre = value; }

64 }

65

66 /// <summary>

67 /// Descripcion de la categoria, no es obligatorio

68 /// </summary>

69 [Property(”Descripcion”)]

70 public string Descripcion

71 {

72 get { return _descripcion; }

73 set { _descripcion = value; }

74 }

75

76 #region Constructor

77

78 /// <summary>

79 /// Constructor sin parametros.

80 /// Hay que inicializar valores para que sea valido

81 /// </summary>

82 private CCategoria()

83 {

84 // Valores por defecto:

85 _nombre = “”;

86 _descripcion = “”;

87 _id = 0;

88 _padre = null;

89

90 // Crear la lista donde se guardaran los usuarios

91 _subCategorias = new EntitySet<CCategoria>();

92 _archivos = new EntitySet<CArchivo>();

93 }

94

95 /// <summary>

96 /// Constructor con los minimos parametros

97 /// </summary>

98 /// <param name=”nombre”></param>

99 private CCategoria(string nombre, string descripcion, CCategoria objCategoriaPadre) : this()

100 {

101 _nombre = nombre;

102 _descripcion = descripcion;

103 _padre = objCategoriaPadre;

104 }

105

106 public static CCategoria CreateCategoria(string nombre, string descripcion, CCategoria objCategoriaPadre)

107 {

108 return new CCategoria(nombre, descripcion, objCategoriaPadre);

109 }

110

111 #endregion

112

113 }

Evidentemente, son muchas mas lineas que el equivalente en Ruby on Rails (bastaba especificar: acts_as_tree:). Pero con el codigo anterior me bastaría hacer lo siguiente para tener una categoria raiz y dos hijas de esa categoría:


{\rtf1\ansi\ansicpg\lang1024\noproof1252\uc1 \deff0{\fonttbl{\f0\fnil\fcharset0\fprq1 Courier New;}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0??;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;??\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;??\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;??\red192\green192\blue192;}??\fs20 \cf10 CCategoria\cf0 objCatRaiz = \cf10 CCategoria\cf0 .CreateCategoria(\cf13 “Raiz”\cf0 , \cf13 “”\cf0 , \cf2 null\cf0 );\par ?? objCatRaiz.Save();\par ?? \cf10 CCategoria\cf0 objCat1 = \cf10 CCategoria\cf0 .CreateCategoria(\cf13 “Hija 1″\cf0 , \cf13 “”\cf0 , objCatRaiz);\par ?? objCat1.Save();\par ?? \cf10 CCategoria\cf0 objCat2 = \cf10 CCategoria\cf0 .CreateCategoria(\cf13 “Hija 2″\cf0 , \cf13 “”\cf0 , objCatRaiz);\par ?? objCat2.Save();\par ?? objCatRaiz.SubCategorias.Add(objCat1);\par ?? objCatRaiz.SubCategorias.Add(objCat2);\par ?? objCatRaiz.Save();\par ??}
–>

1 CCategoria objCatRaiz = CCategoria.CreateCategoria(”Raiz”, “”, null);

2 objCatRaiz.Save();

3 CCategoria objCat1 = CCategoria.CreateCategoria(”Hija 1″, “”, objCatRaiz);

4 objCat1.Save();

5 CCategoria objCat2 = CCategoria.CreateCategoria(”Hija 2″, “”, objCatRaiz);

6 objCat2.Save();

Esto es muchisimo más simple que las equivalentes sentencias SQL para mantener los datos persistentes. Ire hablando de otra serie de características incorporadas a ActiveRecord que lo hacen muy potente: jerarquías de objetos usando discriminadores o joins, diversos tipos de mapeados (ManyToMany, HasMany y BelongsTo, etc), Hooks, transacciones, HQL (lenguaje SQL oerintado a objetos de NHibernate), etc.



Escriba un comentario

Dediquele un momento a comentar lo que piensa. Esta permitido usar HTML básico para formatear el escrito.

Comentarios de los lectores

Sea el primero en dejar un comentario.