2011-02-26 4 views
38

sé que pregunta similar se le pidió varias veces (por ejemplo: here, here, here y) pero era para las versiones anteriores de la Unidad, donde la respuesta fue dependiente utilizada LifetimeManager clase.Unidad 2.0 y gastos de tipos IDisposable (especialmente con PerThreadLifetimeManager)

Documentación dice:

Unidad utiliza tipos específicos que heredan de la clase base LifetimeManager (denominados colectivamente como vida manager) para controlar cómo se almacena referencias a instancias de objeto y cómo el contenedor dispone de estas instancias .

Ok, suena bien, así que decidí verificar la implementación de compilación en los administradores de por vida. Mi conclusión:

  • TransientLifetimeManager - sin manejo de eliminación. Container solo resuelve la instancia y no la rastrea. El código de llamada es responsable de eliminar la instancia.
  • ContainerControlledLifetimeManager - descarta la instancia cuando se elimina el administrador de por vida (= cuando el contenedor está dispuesto). Proporciona una instancia singleton compartida entre todos los contenedores en hiearchy.
  • HierarchicalLifetimeManager - deriva el comportamiento de ContainerControlledLifetimeManager. Proporciona una instancia "singleton" por contenedor en hiearchy (subcontenedores).
  • ExternallyControlledLifetimeManager - sin manipulación de eliminación. Corrija el comportamiento porque el contenedor no es el propietario de la instancia.
  • PerResolveLifetimeManager - sin manejo de eliminación. En general es el mismo que TransientLifetimeManager, pero permite reutilizar la instancia para la inyección de dependencia al resolver el gráfico de objetos completos.
  • PerThreadLifetimeManager - no se maneja la eliminación como también se describe en MSDN. ¿Quién es responsable de eliminar?

Implementación de acumulación en PerThreadLifetimeManager es:

public class PerThreadLifetimeManager : LifetimeManager 
{ 
    private readonly Guid key = Guid.NewGuid(); 
    [ThreadStatic] 
    private static Dictionary<Guid, object> values; 

    private static void EnsureValues() 
    { 
     if (values == null) 
     { 
      values = new Dictionary<Guid, object>(); 
     } 
    } 

    public override object GetValue() 
    { 
     object result; 
     EnsureValues(); 
     values.TryGetValue(this.key, out result); 
     return result; 
    } 

    public override void RemoveValue() 
    { } 

    public override void SetValue(object newValue) 
    { 
     EnsureValues(); 
     values[this.key] = newValue; 
    } 
} 

contenedor para desechar casos no dispone desechables creados con este gestor de toda la vida. La finalización del hilo tampoco eliminará esas instancias. Entonces, ¿quién es responsable de liberar instancias?

Intenté eliminar manualmente la instancia resuelta en el código y encontré otro problema. No puedo desmontar el instinto. RemoveValue of lifetime manager está vacío: una vez que se crea la instancia, no es posible eliminarla del thread static dictionary (también sospecho que el método TearDown no hace nada). Por lo tanto, si llama al Resolve después de eliminar la instancia, se eliminará la instancia. Creo que esto puede ser un gran problema cuando se utiliza este administrador de por vida con subprocesos del grupo de subprocesos.

¿Cómo utilizar correctamente este administrador de por vida?

Además, esta implementación a menudo se reutiliza en administradores personalizados de por vida como PerCallContext, PerHttpRequest, PerAspNetSession, PerWcfCall, etc. Sólo el diccionario estático de hilos se reemplaza con algún otro constructo.

También entiendo correctamente que el manejo de objetos desechables depende del administrador de por vida? Entonces, el código de la aplicación depende del administrador de por vida utilizado.

He leído que en otros contenedores IoC que se ocupan de objetos desechables temporales es manejado por subcontenedores, pero no encontré un ejemplo para Unity, probablemente podría manejarse con subcontainer con alcance local y HiearchicalLifetimeManager pero no estoy seguro de cómo hacerlo eso.

+5

Posiblemente se trata de hacer que Unity implemente de forma determinista el comportamiento de eliminación implementando vidas útiles personalizadas, pero es demasiado complicado de explicar aquí. En mi libro uso más de seis páginas solo para explicar cómo hacer esto: http://affiliate.manning.com/idevaffiliate.php?id=1150_236 –

+5

Solo use un contenedor mejor. –

+0

@Mark: Desafortunadamente su libro será publicado en agosto y no me gusta el MEAP. –

Respuesta

2

Al mirar el código fuente de Unity 2.0, huele como los administradores de LifetimeManager se utilizan para mantener los objetos en el alcance de diferentes maneras para que el recolector de basura no se deshace de ellos. Por ejemplo, con PerThreadLifetimeManager, usará ThreadStatic para mantener una referencia en cada objeto con la duración de ese hilo en particular. Sin embargo, no llamará a Dispose hasta que el contenedor esté Disposed.

Hay un objeto LifetimeContainer que se utiliza para guardar todas las instancias que se crean, luego se disipa cuando se suelta el UnityContainer (que, a su vez, elimina todos los IDisposables en orden cronológico inverso).

EDITAR: después de una inspección más cercana, LifetimeContainer solo contiene LifetimeManagers (de ahí el nombre "Contenedor de por vida"). Entonces, cuando está Dispuesto, solo dispone de los administradores de por vida. (y enfrentamos el problema que ya se discutió).

+1

Por mi investigación solo los gestores de vida controlados por contenedores y jerárquicos 'Dispose' resuelve las instancias: http://www.ladislavmrnka.com/2011/03/unity-build-in-lifetime-managers/ –

+0

@Ladislav tienes razón ... yo eché un vistazo al código y me di cuenta de que tenía una suposición errónea sobre lo que realmente entra en un LifetimeManager. –

1

¿Sería una solución viable utilizar el evento HttpContext.Current.ApplicationInstance.EndRequest para enlazar al final de la solicitud y luego deshacerse del objeto almacenado en este administrador de por vida? de este modo:

public HttpContextLifetimeManager() 
{ 
    HttpContext.Current.ApplicationInstance.EndRequest += (sender, e) => { 
     Dispose(); 
    }; 
} 

public override void RemoveValue() 
{ 
    var value = GetValue(); 
    IDisposable disposableValue = value as IDisposable; 

    if (disposableValue != null) { 
     disposableValue.Dispose(); 
    } 
    HttpContext.Current.Items.Remove(ItemName); 
} 

public void Dispose() 
{ 
    RemoveValue(); 
} 

usted no tiene que utilizar un contenedor secundario como la otra solución y el código utilizado para disponer los objetos se encuentra todavía en el gestor de toda la vida como debería.

0

Me encontré con este problema recientemente cuando estaba instrumentando Unity en mi aplicación. Las soluciones que encontré aquí en Stack Overflow y en otros sitios en línea no parecen abordar el problema de manera satisfactoria, en mi opinión.

Cuando no utilice la unidad, IDisposable casos tienen un patrón de uso bien entendida:

  1. dentro de un ámbito más pequeño que una función, ponerlos en un bloque using para conseguir la eliminación "de forma gratuita".

  2. Cuando se cree para un miembro de instancia de una clase, implemente IDisposable en la clase y limpie en Dispose().

  3. Cuando se pasa al constructor de una clase, no haga nada ya que la instancia IDisposable es propiedad de otra parte.

Unity confunde las cosas porque cuando la inyección de la dependencia se realiza correctamente, el caso # 2 anterior desaparece. Todas las dependencias deben ser inyectadas, lo que significa esencialmente que ninguna clase tendrá propiedad de las instancias IDisposable que se crean. Sin embargo, tampoco proporciona una forma de "obtener" ID IDsposables que se crearon durante una llamada Resolve(), por lo que parece que los bloques using no se pueden usar.¿Qué opción queda?

Mi conclusión es que la interfaz Resolve() es esencialmente incorrecta. Devolver solo el tipo solicitado y los objetos con fugas que necesitan un manejo especial como IDisposable no puede ser correcto.

En respuesta, escribí la extensión IDisposableTrackingExtension para Unity, que rastrea las instancias IDisposable creadas durante una resolución de tipo, y devuelve un objeto envoltorio desechable que contiene una instancia del tipo solicitado y todas las dependencias IDisposable del gráfico objeto.

Con esta extensión, el tipo de resolución se parece a esto (aquí se muestra el uso de una fábrica, ya que sus clases de negocios nunca deben tomar IUnityContainer como una dependencia ):

public class SomeTypeFactory 
{ 
    // ... take IUnityContainer as a dependency and save it 

    IDependencyDisposer<SomeType> Create() 
    { 
    return this.unity.ResolveForDisposal<SomeType>(); 
    } 
} 

public class BusinessClass 
{ 
    // ... take SomeTypeFactory as a dependency and save it 

    public void AfunctionThatCreatesSomeTypeDynamically() 
    { 
    using (var wrapper = this.someTypeFactory.Create()) 
    { 
     SomeType subject = wrapper.Subject; 
     // ... do stuff 
    } 
    } 
} 

Esto reconcilia los patrones de uso IDisposable # 1 y # 3 desde arriba. Las clases normales usan inyección de dependencia; no poseen IDisposables inyectados, por lo que no se deshacen de ellos. Las clases que realizan resolución de tipo (a través de fábricas) porque necesitan objetos creados dinámicamente, esas clases son los propietarios, y esta extensión proporciona la facilidad para administrar los ámbitos de eliminación.

Cuestiones relacionadas