injection - ¿Cómo se comparan los principales marcos C#DI/IoC?
unity dependency injection (7)
A riesgo de entrar en el territorio de la guerra santa, ¿cuáles son las fortalezas y debilidades de estos marcos populares de DI / IoC, y podría uno fácilmente ser considerado el mejor? ..:
- Ninject
- Unidad
- Castillo.Windsor
- Autofac
- Mapa de estructura
¿Hay otros marcos de trabajo DI / IoC para C # que no haya enumerado aquí?
En el contexto de mi caso de uso, estoy creando una aplicación WPF cliente y una infraestructura de servicios WCF / SQL, la facilidad de uso (especialmente en términos de sintaxis clara y concisa), documentación coherente, buen soporte de la comunidad y rendimiento son todos factores importantes en mi eleccion
Actualizar:
Los recursos y las preguntas duplicadas que se citan parecen estar desactualizados, ¿puede alguien con conocimiento de todos estos marcos presentar y proporcionar una visión real?
Me doy cuenta de que es probable que la mayoría de las opiniones sobre este tema estén sesgadas, pero espero que alguien se haya tomado el tiempo de estudiar todos estos marcos y tenga al menos una comparación generalmente objetiva.
Estoy bastante dispuesto a hacer mis propias investigaciones si esto no se ha hecho antes, pero asumí que esto era algo que al menos algunas personas ya habían hecho.
Segunda actualización:
Si tiene experiencia con más de un contenedor DI / IoC, clasifique y resuma los pros y los contras de estos, gracias. Este no es un ejercicio para descubrir todos los pequeños contenedores oscuros que la gente ha hecho, estoy buscando comparaciones entre los marcos populares (y activos).
Bueno, después de mirar alrededor la mejor comparación que he encontrado hasta ahora es:
http://www.sturmnet.org/blog/2010/03/04/poll-ioc-containers-for-net
http://www.sturmnet.org/blog/2010/03/04/poll-results-ioc-containers-for-net
Fue una encuesta realizada en marzo de 2010.
Un punto de interés para mí es que a las personas que han utilizado un marco de DI / IoC y les ha gustado / disgustado, StructureMap parece sobresalir.
También de la encuesta, parece que Castle.Windsor y StructureMap parecen ser los más favorecidos.
Curiosamente, Unity y Spring.Net parecen ser las opciones populares que más a Spring.Net no les gustan. (Estaba considerando a Unity por pereza (y la insignia / soporte de Microsoft), pero ahora estaré observando más de cerca a Castle Windsor y StructureMap).
Por supuesto, esto probablemente (?) No se aplica a Unity 2.0, que se lanzó en mayo de 2010.
Esperemos que alguien más pueda proporcionar una comparación basada en la experiencia directa.
En realidad hay toneladas de marcos de IoC. Parece que cada programador intenta escribir uno en algún momento de su carrera. Tal vez no para publicarlo, sino para aprender el funcionamiento interno.
Personalmente prefiero autofac ya que es bastante flexible y tengo la sintaxis que me conviene (aunque realmente odio que todos los métodos de registro sean métodos de extensión).
Algunos otros marcos:
Me encontré con otra comparison rendimiento (última actualización 10 de abril de 2014). Se compara lo siguiente:
- AutoFac
- LightCore (el sitio es alemán)
- LinFu
- Ninject
- Petite
- Inyector simple (el más rápido de todos los concursantes)
- Spring.NET
- StructureMap
- Unity
- Windsor
- Hiro
Aquí hay un breve resumen de la publicación:
Conclusión
Ninject es definitivamente el contenedor más lento.
MEF, LinFu y Spring.NET son más rápidos que Ninject, pero siguen siendo bastante lentos. AutoFac, Catel y Windsor son los siguientes, seguidos de StructureMap, Unity y LightCore. Una desventaja de Spring.NET es que solo se puede configurar con XML.
SimpleInjector, Hiro, Funq, Munq y Dynamo ofrecen el mejor rendimiento, son extremadamente rápidos. ¡Dales una oportunidad!
Especialmente Simple Injector parece ser una buena opción. Es muy rápido, tiene una buena documentación y también admite escenarios avanzados como intercepción y decoradores genéricos.
También puede intentar usar la Biblioteca de Selector de Servicios Comunes y, con suerte, probar múltiples opciones y ver qué funciona mejor para usted.
Alguna información sobre la Biblioteca de Selector de Servicios Comunes del sitio:
La biblioteca proporciona una abstracción sobre contenedores de IoC y localizadores de servicio. El uso de la biblioteca permite que una aplicación acceda indirectamente a las capacidades sin depender de referencias duras. La esperanza es que al usar esta biblioteca, las aplicaciones y los marcos de terceros puedan comenzar a aprovechar IoC / Service Location sin atarse a una implementación específica.
Actualizar
13.09.2011: Funq y Munq se agregaron a la lista de participantes. Los gráficos también se actualizaron y Spring.NET se eliminó debido a su bajo rendimiento.
04.11.2011: "agregó Simple Injector , el rendimiento es el mejor de todos los participantes".
Mira para una comparación de net-ioc-frameworks en el código de google, incluyendo linfu y spring.net que no están en tu lista mientras escribo este texto.
Trabajé con spring.net: tiene muchas características (AOP, bibliotecas, docu, ...) y hay mucha experiencia con él en dotnet y java-world. Las características están modularizadas para que no tenga que tomar todas las características. Las características son abstracciones de problemas comunes como la base de datos de datos, el registro y la acción. sin embargo, es difícil hacer y depurar la configuración IoC.
Por lo que he leído hasta ahora: si tuviera que elegir para un proyecto pequeño o mediano, usaría ninject ya que ioc-configuration está hecho y se puede depurar en c #. Pero no he trabajado con eso todavía. para el sistema modular grande me quedaría con spring.net debido a las bibliotecas de abstracción.
Si bien una respuesta completa a esta pregunta ocupa cientos de páginas de mi libro , aquí hay una tabla de comparación rápida en la que todavía estoy trabajando:
Solo lea este gran blog de comparación de contenedores .Net DI por Philip Mat.
Él hace algunas pruebas de comparación de rendimiento a fondo;
Él recomienda AutoFac ya que es pequeño, rápido y fácil de usar ... Estoy de acuerdo. Parece que Unity y Ninject son los más lentos en sus pruebas.
Descargo de responsabilidad: desde principios de 2015, hay una gran comparación de las características de IoC Container de Jimmy Bogard , aquí hay un resumen:
Contenedores Comparados:
- Autofac
- Ninject
- Inyector simple
- Mapa de estructura
- Unidad
- Windsor
El escenario es el siguiente: tengo una interfaz, IMediator, en la que puedo enviar una solicitud / respuesta única o una notificación a varios destinatarios:
public interface IMediator
{
TResponse Send<TResponse>(IRequest<TResponse> request);
Task<TResponse> SendAsync<TResponse>(IAsyncRequest<TResponse> request);
void Publish<TNotification>(TNotification notification)
where TNotification : INotification;
Task PublishAsync<TNotification>(TNotification notification)
where TNotification : IAsyncNotification;
}
Luego creé un conjunto base de solicitudes / respuestas / notificaciones:
public class Ping : IRequest<Pong>
{
public string Message { get; set; }
}
public class Pong
{
public string Message { get; set; }
}
public class PingAsync : IAsyncRequest<Pong>
{
public string Message { get; set; }
}
public class Pinged : INotification { }
public class PingedAsync : IAsyncNotification { }
Me interesaba ver algunas cosas con respecto al soporte de contenedores para genéricos:
- Configuración para genéricos abiertos (registro de IRequestHandler <,> fácilmente)
- Configuración para múltiples registros de genéricos abiertos (dos o más INotificationHandlers)
Configuración para la varianza genérica (registro de controladores para INotification base / creación de tuberías de solicitud) Mis controladores son bastante sencillos, simplemente se envían a la consola:
public class PingHandler : IRequestHandler<Ping, Pong> { /* Impl */ }
public class PingAsyncHandler : IAsyncRequestHandler<PingAsync, Pong> { /* Impl */ }
public class PingedHandler : INotificationHandler<Pinged> { /* Impl */ }
public class PingedAlsoHandler : INotificationHandler<Pinged> { /* Impl */ }
public class GenericHandler : INotificationHandler<INotification> { /* Impl */ }
public class PingedAsyncHandler : IAsyncNotificationHandler<PingedAsync> { /* Impl */ }
public class PingedAlsoAsyncHandler : IAsyncNotificationHandler<PingedAsync> { /* Impl */ }
Autofac
var builder = new ContainerBuilder();
builder.RegisterSource(new ContravariantRegistrationSource());
builder.RegisterAssemblyTypes(typeof (IMediator).Assembly).AsImplementedInterfaces();
builder.RegisterAssemblyTypes(typeof (Ping).Assembly).AsImplementedInterfaces();
- Genéricos abiertos: sí, implícitamente
- Múltiples genéricos abiertos: sí, implícitamente.
- Contravarianza genérica: sí, explícitamente
Ninject
var kernel = new StandardKernel();
kernel.Components.Add<IBindingResolver, ContravariantBindingResolver>();
kernel.Bind(scan => scan.FromAssemblyContaining<IMediator>()
.SelectAllClasses()
.BindDefaultInterface());
kernel.Bind(scan => scan.FromAssemblyContaining<Ping>()
.SelectAllClasses()
.BindAllInterfaces());
kernel.Bind<TextWriter>().ToConstant(Console.Out);
- Genéricos abiertos: sí, implícitamente
- Múltiples genéricos abiertos: sí, implícitamente.
- Contravarianza genérica: sí, con extensiones construidas por el usuario
Inyector simple
var container = new Container();
var assemblies = GetAssemblies().ToArray();
container.Register<IMediator, Mediator>();
container.Register(typeof(IRequestHandler<,>), assemblies);
container.Register(typeof(IAsyncRequestHandler<,>), assemblies);
container.RegisterCollection(typeof(INotificationHandler<>), assemblies);
container.RegisterCollection(typeof(IAsyncNotificationHandler<>), assemblies);
- Genéricos abiertos: sí, explícitamente
- Múltiples genéricos abiertos: sí, explícitamente
- Contravarianza genérica: sí, implícitamente (con actualización 3.0)
Mapa de estructura
var container = new Container(cfg =>
{
cfg.Scan(scanner =>
{
scanner.AssemblyContainingType<Ping>();
scanner.AssemblyContainingType<IMediator>();
scanner.WithDefaultConventions();
scanner.AddAllTypesOf(typeof(IRequestHandler<,>));
scanner.AddAllTypesOf(typeof(IAsyncRequestHandler<,>));
scanner.AddAllTypesOf(typeof(INotificationHandler<>));
scanner.AddAllTypesOf(typeof(IAsyncNotificationHandler<>));
});
});
- Genéricos abiertos: sí, explícitamente
- Múltiples genéricos abiertos: sí, explícitamente
- Contravarianza genérica: sí, implícitamente
Unidad
container.RegisterTypes(AllClasses.FromAssemblies(typeof(Ping).Assembly),
WithMappings.FromAllInterfaces,
GetName,
GetLifetimeManager);
/* later down */
static bool IsNotificationHandler(Type type)
{
return type.GetInterfaces().Any(x => x.IsGenericType && (x.GetGenericTypeDefinition() == typeof(INotificationHandler<>) || x.GetGenericTypeDefinition() == typeof(IAsyncNotificationHandler<>)));
}
static LifetimeManager GetLifetimeManager(Type type)
{
return IsNotificationHandler(type) ? new ContainerControlledLifetimeManager() : null;
}
static string GetName(Type type)
{
return IsNotificationHandler(type) ? string.Format("HandlerFor" + type.Name) : string.Empty;
}
- Genéricos abiertos: sí, implícitamente
- Múltiples genéricos abiertos: sí, con extensión construida por el usuario
- Contravarianza genérica: derp
Windsor
var container = new WindsorContainer();
container.Register(Classes.FromAssemblyContaining<IMediator>().Pick().WithServiceAllInterfaces());
container.Register(Classes.FromAssemblyContaining<Ping>().Pick().WithServiceAllInterfaces());
container.Kernel.AddHandlersFilter(new ContravariantFilter());
- Genéricos abiertos: sí, implícitamente
- Múltiples genéricos abiertos: sí, implícitamente.
- Contravarianza genérica: sí, con extensión construida por el usuario