Lazy Initialization

Despues de llevar un tiempo trabajando con MonoRails.ActiveRecord y tras revisar el post: MonoRails o .NET on Rails tengo que advertir de un hecho muy importante y que puede llevar a una aplicación al desastre si no se trata con cuidado y es el tema del Lazy Initialization (Como traducirlo? Inicializaciones perezosas?) de las relaciones Uno a Muchos o Muchos a Muchos.
Trata de como es el comportamiento de los objetos en cuanto a relaciones se refiere. Supongamos un modelo de objetos como el siguiente:

cd1.png>

El objeto CMarca contiene un propiedad de acceso a la colección de modelos dependiente de dicha marca.
Si obtuviesemos de la base de datos un objeto CMarca determinado (por ejmplo con ID = 1):

CMarca objMarca = CMarca.Find(1);

Cuantas consultas habría a la base de datos? Bien, la respuesta depende de si se ha definido la relacion como Lazy. En caso negativo (funcionamiento por defecto de NHibernate) el método Find creara una objeto de tipo Marca y llenará la colección de Modelos, realizando una consulta por cada elemento para generar el correspondiente objeto CModelo a insertar en dicha colección. Ahora imaginaros que no teneis 2 clases solamente, sino un modelo de objetos completo… para obtener un solo elemento, NHibernate obtendría encadenadamente todos los objetos hasta completarlos, de tal manera que siempre que accedamos a las propiedades del objeto no tendría que hacer ninguna busqueda a la base de datos…. pero las habria hecho todas al inicializar el objeto! Aún cuando no las necesitemos!

Lo normal es que el comportamiento deseado sea el de solo consultar lo que de verdad necesito. Si me traigo un objeto de tipo CMarca, pero no quiero saber sus modelos… porque tengo que consultar la coleccion de dichos modelos a la base de datos? Esto se realiza marcando las relaciones como Lazy.
Para marcar la relación como Lazy en el código anterior bastaría con definir la relación HasMany como sigue:

[HasMany (typeof(CModelo), ColumnKey=”marca_id”, RelationType = RelationType.Set, CustomAccess = “NHibernate.Generics.GenericAccessor, NHibernate.Generics”, Lazy=true]
public EntitySet<CModelo> Modelos
{
return _modelos;
}

Creando con el objeto CMarca una variable _modelos de tipo EntitySet<CModelo> y construyendola de la siguiente manera:

// Constructor:
public CMarca
{
_modelos = new EntitySet<CModelo>(
delegate (CModelo objModelo) { objModelo.Marca = this; },
delegate (CModelo objModelo) { objModelo.Marca = null; } )
}

Los dos parametros que se le pasan al constructor del EntitySet son dos delegados (o funciones impersonadas) que serán llamadas cuando se inserte y se borre un modelo de la coleccion. De esta manera se encargará de mantener sincronizado todo el modelo de objetos no solo a nivel de base de datos, sino a nivel de lo que tengamos en la memoria de la aplicación.

El comportamiento en relaciones Muchos a Muchos es un poco más peliagudo ya que hay que definir el lado de la relación que lleva la voz cantante. A ese lado se le pone el atributo Lazy a true y al otro lado de la relación se le pone Inverse = true, indicando que es el otro lado el encargado de mantener la relación.

Finalmente decir que es ActiveRecord/NHibernate quien se encarga de realizar la consulta a la base de datos por nosotros cuando accedemos a una propiedad que esconde una relación:

CMarca objMarca = CMarca.Find(1)
Console.WriteLine ( String.Format (”Marca: {0}”, objMarca.Nombre));
foreach (CModelo objModelo in objMarca.Modelos) // Es en este momento cuando se llama a la BD para buscar los modelos
Console.WriteLine ( String.Format(”- Modelo: {0}”, objModelo.Nombre ));

Con esto hay que tener mucho cuidado ya que hay que mantener siempre una conexión NHibernate abierta, ya que la consulta a la base de datos se puede realizar en cualquier momento. Para hacer esto en ActiveRecord, lo mejor (siempre que sea necesario) es crear un SessionScope:

using(new SessionScope ()) { … }

Podeis encontrar mucha más información en los siguientes posts:

Ayende @ Rahien
ActiveRecord’s Session Scopes and its side effects
ActiveRecord:How to:Enable Session per Request

Todavía no hay comentarios

Enviar un comentario nuevo

El contenido de este campo se mantiene privado y no se mostrará públicamente.
  • Etiquetas HTML permitidas: <a> <blockquote> <br> <cite> <code> <dd> <div> <dl> <dt> <em> <h1> <h2> <h3> <h4> <h5> <h6> <hr> <img> <li> <ol> <p> <pre> <span> <strong> <swf> <table> <tbody> <td> <th> <tr> <ul>
    Allowed Style properties: background-color, background-image, border, border-bottom, border-bottom-color, border-bottom-style, border-bottom-width, border-color, border-left, border-left-color, border-left-style, border-left-width, border-right, border-right-color, border-right-style, border-right-width, border-spacing, border-style, border-top, border-top-color, border-top-style, border-top-width, border-width, color, direction, font, font-family, font-size, font-style, font-variant, font-weight, height, left, line-height, list-style-type, margin, margin-bottom, margin-left, margin-right, margin-top, padding, padding-bottom, padding-left, padding-right, padding-top, right, text-align, text-decoration, top, width
  • Syntax highlight code surrounded by the {syntaxhighlighter OPTIONS}...{/syntaxhighlighter} tags.
  • E-Mail addresses are hidden with reCAPTCHA Mailhide.

Más información sobre opciones de formato

CAPTCHA
Esta pregunta es para comprobar si eres un ser humano y evitar el envío automático desde sistemas de spam