inversion-of-control - castle windsor c#
Gestión de la vida útil de dependencia de Hub propio para SignalR y Castle Windsor (1)
Tengo algunos concentradores SignalR que pueden necesitar acceder a algunas dependencias transitorias e individuales. Enganchar la creación del Hub es fácil y funciona bien, sin embargo SignalR hace su propia llamada Dispose () en el Hub creado en lugar de notificar al resolvedor de dependencias y dejarlo involucrarse en la eliminación.
Esto no es tan importante si las dependencias son singleton registradas, pero si están registradas como transitorias, nunca serán eliminadas (si fuera necesario) y Windsor las mantendrá con vida hasta que se recolecte el contenedor Windsor (cuando el servidor web se está apagando de todos modos).
Veo varias maneras posibles de manejar esto ...
a) Alguien aquí señala una forma de subclase de la clase HubDispatcher de SignalR para que pueda hacer una eliminación adecuada. No es parte de DependencyResolver estándar de SignalR, así que esto podría ser difícil / imposible
b) Alguna otra clase en SignalR, en otra parte de la canalización, se puede anular o reemplazar fácilmente para que podamos subclase HubDispatcher y asegurarnos de que se use la subclase. Por lo que puedo decir, este debería ser el tipo de middleware Owin HubDispatcherMiddleware. ¿Hay alguna manera de obligar a Owin a no registrar esta clase y en su lugar registrar mi propia versión de esto (que a su vez usa mi propio HubDispatcher)?
c) Hay alguna forma de interceptar la llamada a Dispose () hecha por SignalR en mis clases de Hub para que se pueda realizar una devolución a Windsor para asegurar que cualquier dependencia se elimine y se libere correctamente del contenedor
d) Evitar cuidadosamente el uso de dependencias transitorias de estilo de vida y, en cambio, pasar fábricas mecanografiadas para que podamos resolver y liberar cada dependencia a través de la fábrica de tipeo dentro del Hub
Por el momento (d) es el único que sé cómo hacer. (a) o (b) sería genial. (c) está cubierto principalmente por esta publicación http://kozmic.net/2010/01/27/transparently-releasing-components-in-windsor/ , sin embargo, el interceptor requiere que Dispose () se llame a través de IDisposable. La implementación de la distribución Hub Hubter de la clase HubDispather de SignalR es
private static void DisposeHubs(IEnumerable<IHub> hubs)
{
foreach (var hub in hubs)
{
hub.Dispose();
}
}
No se puede enviar contenido a IDisposable allí ... También Dispose () en la clase Hub es virtual y esa entrada de blog implica que un Dispose virtual () podría agregar cierta complejidad (no estoy seguro de cuánto y no sé lo suficiente sobre Los interceptores de Castle y si el elenco faltante para IDisposable se puede solucionar de todos modos).
Aprecio que haya escrito esta pregunta para un público bastante limitado: aquellos que han usado Windsor AND SignalR y se preocupan por algo más que resolver dependencias. Cada ejemplo que he encontrado, incluidos los de StackOverflow, parece ignorar el lanzamiento de dependencias.
¡Gracias!
Tuve un problema similar pero con Unity en lugar de Castle Windsor.
Mis requisitos:
- Quería evitar registros únicos en el contenedor.
- Todos los objetos se resuelven en Hub y deberían eliminarse en la destrucción del Hub.
- Registros reutilizados en Web Api y SignalR.
- La vida útil del objeto es administrada por
HierarchicalLifetimeManager
- los contenedores secundarios resuelven y administran instancias de objetos separadas. Registrado así:
container.RegisterType<IMessageService, MessageService>(new HierarchicalLifetimeManager());
Esta es mi solución:
[HubName("exampleHub")]
public class ExampleHub : Hub
{
IUnityContainer _container;
public CarrierApiHub(IUnityContainer container) // container itself injected in hub
{
_container = container.CreateChildContainer(); // child container derived from the main container.
}
public async Task<int> UnreadMessagesCount()
{
// Here i''m resolving instance of IMessageService which depends on
// other registrations specified on the container. Full object graph
// is constructed and destroyed on hub disposal.
var messageSvc = _container.Resolve<IMessageService>();
return await messageSvc.CountUnreadOf(UserId);
}
protected override void Dispose(bool disposing)
{
_container.Dispose(); // child container destroyed. all resolved objects disposed.
base.Dispose(disposing);
}
private int UserId
{
get
{
// only an example
var claim = ((ClaimsPrincipal)Context.User).GetClaim("user_id");
return int.Parse(claim.Value);
}
}
}
SignalR y configuración de resolución de dependencias:
public static class ConfigureSignalR
{
public static void Initialize(UnityContainer unityContainer, IAppBuilder app)
{
app.Map("/signalr", map =>
{
var resolver = new AppSignalRDependencyResolver(unityContainer);
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableJavaScriptProxies = false,
EnableJSONP = true, // Required for IE 9 (supports only polling)
Resolver = resolver
};
map.RunSignalR(hubConfiguration);
});
}
}
Implementación de resolución de dependencias:
public class AppSignalRDependencyResolver : DefaultDependencyResolver
{
protected IUnityContainer _container;
public AppSignalRDependencyResolver(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
this._container = container.CreateChildContainer();
}
public override object GetService(Type serviceType)
{
try
{
return _container.Resolve(serviceType);
}
catch (ResolutionFailedException)
{
return base.GetService(serviceType);
}
}
public override IEnumerable<object> GetServices(Type serviceType)
{
try
{
return _container.ResolveAll(serviceType).Concat(base.GetServices(serviceType));
}
catch (ResolutionFailedException)
{
return base.GetServices(serviceType);
}
}
protected override void Dispose(bool disposing)
{
_container.Dispose();
base.Dispose(disposing);
}
}