2011-12-05 10 views
6

tengo un módulo Autofac que tiene la lógica siguiente (recortada) en la anulación de la carga:módulo de parada Autofac registro ya registrados

protected override void Load(ContainerBuilder builder) 
    { 
     foreach (var componentType in allTypesInAllAvailableAssemblies) // Set elsewhere 
     { 
      var handlerInterfaces = componentType.GetInterfaces().Where(i => i.IsClosedTypeOf(typeof(IMessageHandler<>))); 
      if (handlerInterfaces.Any()) 
       builder.RegisterType(componentType).As(handlerInterfaces); 
     } 
    } 

Esto está en busca de cualquier clase que sí declara un controlador de mensajes y lo registra contra todas las interfaces IMessageHandler que implementa.

Lo que quiero hacer es no registrar el componente si ya está registrado. Como beneficio adicional, sería ideal si pudiera actualizar el registro existente para resolverlo frente a la (s) interfaz (es) del manejador de mensajes, si aún no lo está.

Por el bien del argumento se puede suponer que este código se ejecutará después de todos los demás tipos se han registrado (incluyendo posibles candidatos controlador de mensajes)

He usado la anulación AttachToComponentRegistration para la manipulación de la inscripción en el pasado, pero no parece que sea útil en este escenario.

¿Es posible o debería replantear mi diseño y forzar los complementos para declarar explícitamente sus controladores?

+0

¿Ha intentado utilizar la clase AnyConcreteTypeNotAlreadyRegisteredSource?Ver: http://stackoverflow.com/questions/3413660/ –

Respuesta

7
builder.RegisterType(componentType) 
    .As(handlerInterfaces) 
    .PreserveExistingDefaults(); 

Funcionará a menos que empiece a resolver listas de controladores.

+0

Lamentablemente necesito listas de controladores. Terminé cambiando el registro enviado para buscar todas las interfaces de controlador cuando un tipo está explícitamente registrado en lugar de hacer otro barrido de todos los tipos. Saludos por la ayuda: lo acepté porque responde la pregunta tal como se publicó. – JRoughan

1

Lamentablemente, no hay una manera elegante de hacer lo que quiere. El contenedor Autofac y su constructor son "cajas negras" que no le permiten ver bien lo que ya tiene.

No es perjudicial registrar un componente dos veces, A MENOS QUE sus registros dependan de la orden (MALO, MALO, MALO). Registrarse por segunda vez simplemente sobrescribirá el registro anterior con el nuevo.

Pregunto seriamente este código, ya que depende totalmente de cómo se inicializa allTypesInAllAvailableAssemblies. Si realmente es de todos los tipos en su sistema, entonces es una mierda en cuanto a lo que se resolverá como, por ejemplo, un IDisposable. Si tiene varias implementaciones diferentes de, por ejemplo, IConfigurator, tendrá un control limitado sobre cuál se acaba registrando, independientemente de si está comprobando lo que ya está registrado o simplemente dejando que se sobrescriba el registro; depende totalmente de qué clase termine primero (o último) en la lista.

Lo único que podía pensar que hacer es usar un poco de LINQ para asegurarse de que la lista de tipos que está registrando es único:

protected override void Load(ContainerBuilder builder) 
{ 
    foreach (var componentType in allTypesInAllAvailableAssemblies.OfType<Type>().Distinct()) // Set elsewhere 
    { 
     var handlerInterfaces = componentType.GetInterfaces().Where(i => i.IsClosedTypeOf(typeof(IMessageHandler<>))); 
     if (handlerInterfaces.Any()) 
      builder.RegisterType(componentType).As(handlerInterfaces); 
    } 
} 

Esto garantizará que cada instancia de componentType nunca se ha visto por el constructor antes, dentro del alcance de este ciclo foreach. Eso significa que, dado que este es el único módulo utilizado para construir Contenedores, y cada Contenedor solo se construye una vez y nunca se actualiza, cada componente en el sistema se habrá registrado en un Contenedor dado exactamente una vez. Las interfaces comunes, como IDisposable, IEnumerable, IComparable, IComparer, etc. van a ser inútiles para tratar de resolver; se resolverán con una instancia de la última clase que tenía esa interfaz.

Si tiene que verificar que nunca se ha registrado una interfaz, o que este código también funciona al usar un ContainerBuilder para actualizar() un contenedor existente, simplemente detenga lo que está haciendo porque está a punto de crear un lío que nunca podrá mantener adecuadamente.

+0

Esto ha surgido porque tengo un mecanismo de registro de doble envío para los complementos (es decir, se les proporciona una clase que expone los métodos de registro y los agrega internamente al constructor). Esto es agradable y limpio, y se puede usar para registrar manejadores, pero estaba buscando una manera de no forzar a los complementos a registrar explícitamente todos sus manejadores de forma individual (usando este módulo en el shell). Esto también funciona bien, excepto cuando un componente está registrado en el despacho pero también es un manejador de mensajes (especialmente si los tiempos de vida son diferentes). – JRoughan

+0

No hay ningún problema con las múltiples interfaces existentes en el sistema (de hecho, se espera). Tampoco hay actualización de contenedores existentes ni dependencia de pedidos de registros. Probablemente no he explicado el escenario lo mejor que pude, pero no es tan malo como probablemente estés pensando. – JRoughan

+0

Ah, y allTypesInAllAvailableAssemblies existe solo en el ejemplo SO. En el mundo real, los tipos disponibles son proporcionados por otra clase con cierta inteligencia. – JRoughan

Cuestiones relacionadas