visual studio net mvc form for asp and c# asp.net-core-mvc unity-container

c# - studio - Unidad con ASP.NET Core y MVC6(Core)



globalization and localization asp net core (2)

Actualización 09.08.2018
La unidad se está desarrollando here pero no he tenido tiempo de probar cómo funciona con el marco Core de ASP.NET.

Actualización 15.03.2018
Esta solución es para el problema específico de usar ASP.NET Core v1 con Unity mientras se usa .NET Framework 4.5.2 NO el .NET Core Framework. Tuve que usar esta configuración ya que necesitaba algunas DLL .Net 4.5.2, pero para cualquiera que comience de nuevo, no recomendaría este enfoque. Además, Unity no se está desarrollando más (a mi entender), por lo que recomendaría usar el Marco de Autofac para nuevos proyectos. Consulte esta Post para obtener más información sobre cómo hacerlo.

Introducción
Estoy construyendo una aplicación web utilizando ASP.NET con MVC. Esta aplicación depende de ciertos servicios (un servicio WCF, un servicio de almacén de datos, etc.). Ahora, para mantener las cosas agradables y desacopladas, quiero usar un marco DI (Dependecy Injection), específicamente Unity.

Investigación inicial
Encontré esta publicación de blog pero lamentablemente no está funcionando. Aunque la idea es bonita.
Básicamente, dice que no debe registrar todos los servicios registrados en ServiceCollection en su propio contenedor, sino que debe hacer referencia al proveedor de servicios predeterminado.
Asi que. si hay que resolver algo, se llama al proveedor de servicios predeterminado y, en caso de que no tenga una resolución, el tipo se resolverá utilizando su UnityContainer personalizado.

Los problemas
MVC siempre intenta resolver el controlador con el proveedor de servicios predeterminado.
Además, noté que incluso si el Controlador se resolviera correctamente, nunca puedo "mezclar" Dependencias. Ahora, si quiero usar uno de mis Servicios pero también una interfaz IOptions de ASP, la clase nunca podrá resolverse porque ninguno de esos dos contenedores tiene resoluciones para ambos tipos.

Lo que necesito
Para resumir necesito las siguientes cosas:

  • Una configuración en la que no necesito copiar las Dependencias de ASP.NET en mi UnityContainer
  • Un contenedor que puede resolver mis controladores MVC
  • Un contenedor que puede resolver Dependencias "mixtas".

EDITAR:
Entonces la pregunta es ¿cómo puedo lograr estos puntos?

Ambiente
proyecto.json:


Entonces, después de algunas investigaciones, encontré las siguientes soluciones a mis problemas:

Usa la unidad con ASP
Para poder usar Unity con ASP necesitaba un IServiceProvider ( Documentación ASP ) personalizado, así que escribí un contenedor para el IUnityContainer que tiene este aspecto

public class UnityServiceProvider : IServiceProvider { private IUnityContainer _container; public IUnityContainer UnityContainer => _container; public UnityServiceProvider() { _container = new UnityContainer(); } #region Implementation of IServiceProvider /// <summary>Gets the service object of the specified type.</summary> /// <returns>A service object of type <paramref name="serviceType" />.-or- null if there is no service object of type <paramref name="serviceType" />.</returns> /// <param name="serviceType">An object that specifies the type of service object to get. </param> public object GetService(Type serviceType) { //Delegates the GetService to the Containers Resolve method return _container.Resolve(serviceType); } #endregion }

También tuve que cambiar la firma del método ConfigureServices en mi clase de inicio de esto:

public void ConfigureServices(IServiceCollection services)

a esto:

public IServiceProvider ConfigureServices(IServiceCollection services)

Ahora puedo devolver mi IServiceProvider personalizado y se usará en lugar del predeterminado.
El método completo de ConfigureServices se muestra en la sección Conexión en la parte inferior.

Controladores de resolución
Encontré esta publicación de blog . De ahí aprendí que MVC usa una interfaz IControllerActivator para manejar la instanciación del controlador. Así que escribí mi propia que se parece a esto:

public class UnityControllerActivator : IControllerActivator { private IUnityContainer _unityContainer; public UnityControllerActivator(IUnityContainer container) { _unityContainer = container; } #region Implementation of IControllerActivator public object Create(ControllerContext context) { return _unityContainer.Resolve(context.ActionDescriptor.ControllerTypeInfo.AsType()); } public void Release(ControllerContext context, object controller) { //ignored } #endregion }

Ahora, si se activa una clase de Controlador, se pondrá en duda mi UnityContainer. Por lo tanto, mi UnityContainer debe saber cómo resolver cualquier controlador.

Problema siguiente: usar el proveedor de servicios de ISP predeterminado
Ahora, si registro servicios como Mvc en ASP.NET, normalmente lo haría así:

services.AddMvc();

Ahora, si uso un UnityContainer, todas las Dependencias de MVC no podrían ser Resueltas porque no están Registradas. Así que puedo registrarlos (como AutoFac) o puedo crear una extensión de UnityContainer. Opté por la Extensión y se me ocurrieron las siguientes dos clases:
UnityFallbackProviderExtension

public class UnityFallbackProviderExtension : UnityContainerExtension { #region Const ///Used for Resolving the Default Container inside the UnityFallbackProviderStrategy class public const string FALLBACK_PROVIDER_NAME = "UnityFallbackProvider"; #endregion #region Vars // The default Service Provider so I can Register it to the IUnityContainer private IServiceProvider _defaultServiceProvider; #endregion #region Constructors /// <summary> /// Creates a new instance of the UnityFallbackProviderExtension class /// </summary> /// <param name="defaultServiceProvider">The default Provider used to fall back to</param> public UnityFallbackProviderExtension(IServiceProvider defaultServiceProvider) { _defaultServiceProvider = defaultServiceProvider; } #endregion #region Overrides of UnityContainerExtension /// <summary> /// Initializes the container with this extension''s functionality. /// </summary> /// <remarks> /// When overridden in a derived class, this method will modify the given /// <see cref="T:Microsoft.Practices.Unity.ExtensionContext" /> by adding strategies, policies, etc. to /// install it''s functions into the container.</remarks> protected override void Initialize() { // Register the default IServiceProvider with a name. // Now the UnityFallbackProviderStrategy can Resolve the default Provider if needed Context.Container.RegisterInstance(FALLBACK_PROVIDER_NAME, _defaultServiceProvider); // Create the UnityFallbackProviderStrategy with our UnityContainer var strategy = new UnityFallbackProviderStrategy(Context.Container); // Adding the UnityFallbackProviderStrategy to be executed with the PreCreation LifeCycleHook // PreCreation because if it isnt registerd with the IUnityContainer there will be an Exception // Now if the IUnityContainer "magically" gets a Instance of a Type it will accept it and move on Context.Strategies.Add(strategy, UnityBuildStage.PreCreation); } #endregion }


UnityFallbackProviderStrategy :

public class UnityFallbackProviderStrategy : BuilderStrategy { private IUnityContainer _container; public UnityFallbackProviderStrategy(IUnityContainer container) { _container = container; } #region Overrides of BuilderStrategy /// <summary> /// Called during the chain of responsibility for a build operation. The /// PreBuildUp method is called when the chain is being executed in the /// forward direction. /// </summary> /// <param name="context">Context of the build operation.</param> public override void PreBuildUp(IBuilderContext context) { NamedTypeBuildKey key = context.OriginalBuildKey; // Checking if the Type we are resolving is registered with the Container if (!_container.IsRegistered(key.Type)) { // If not we first get our default IServiceProvider and then try to resolve the type with it // Then we save the Type in the Existing Property of IBuilderContext to tell Unity // that it doesnt need to resolve the Type context.Existing = _container.Resolve<IServiceProvider>(UnityFallbackProviderExtension.FALLBACK_PROVIDER_NAME).GetService(key.Type); } // Otherwise we do the default stuff base.PreBuildUp(context); } #endregion }

Ahora, si mi UnityContainer no tiene Registro para algo, solo debe solicitarlo al Proveedor predeterminado.
Aprendí todo esto de varios artículos diferentes.

Lo bueno de este enfoque es que también puedo "mezclar" Dependencias ahora. Si necesito alguno de mis servicios y una interfaz de IOptions de ASP, ¡UnityContainer resolverá todas estas dependencias y las inyectará en mi controlador!
Lo único que debo recordar es que si uso alguna de mis propias Dependencias tengo que registrar mi clase de Controlador en Unity porque el IServiceProvider predeterminado ya no puede Resolver mis Dependencias de los Controladores.

Por último: cablear
Ahora en mi proyecto utilizo diferentes servicios (Opciones ASP, MVC con opciones). Para hacer que todo funcione, mi Método de Configurar Servicios se ve así ahora:

public IServiceProvider ConfigureServices(IServiceCollection services) { // Add all the ASP services here // #region ASP services.AddOptions(); services.Configure<WcfOptions>(Configuration.GetSection("wcfOptions")); var globalAuthFilter = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .Build(); services.AddMvc(options => { options.Filters.Add(new AuthorizeFilter(globalAuthFilter)); }) .AddJsonOptions ( options => options.SerializerSettings.ContractResolver = new DefaultContractResolver() ); // #endregion ASP // Creating the UnityServiceProvider var unityServiceProvider = new UnityServiceProvider(); IUnityContainer container = unityServiceProvider.UnityContainer; // Adding the Controller Activator // Caution!!! Do this before you Build the ServiceProvider !!! services.AddSingleton<IControllerActivator>(new UnityControllerActivator(container)); //Now build the Service Provider var defaultProvider = services.BuildServiceProvider(); // Configure UnityContainer // #region Unity //Add the Fallback extension with the default provider container.AddExtension(new UnityFallbackProviderExtension(defaultProvider)); // Register custom Types here container.RegisterType<ITest, Test>(); container.RegisterType<HomeController>(); container.RegisterType<AuthController>(); // #endregion Unity return unityServiceProvider; }

Desde que aprendí la mayor parte de lo que sé sobre DI en la semana pasada, espero no haber roto ningún patrón / patrón grande, si es así, ¡dígame!


Para ASP.Net Core 2.0, 2.1, 2.2 y Unity, hay una solución oficial disponible de los autores de Unity como paquete NuGet aquí: NuGetPackage

Aquí está el repositorio de Git con ejemplos: Git repo

El uso es muy simple (desde la página de inicio de Git):

public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseUnityServiceProvider() <---- Add this line .UseStartup<Startup>() .Build();

Y here hay un ejemplo con Unity DI para ASP.Net Core.

Estoy usando esta solución en mi aplicación ASP.Net Core y funciona bien.