none
Ejecutar contenedor IoC antes que OWIN RRS feed

  • Pregunta

  • Buenas,

    Tengo una aplicación MVC 5 en la que estoy utilizando SimpleInjector como contenedor IoC y también se utiliza OWIN. Observo que la clase de Inicio OWIN se ejecuta antes que el Inicializador IoC, sin embargo deseo que que primero se ejecute el contenedor IoC ya que quisiera poder inyectarle un servicio al constructor de la clase Startup de OWIN.

    Actualmente la clase Startup sin el constructor tiene la siguiente forma:

    public class Startup
    {
    	public static IDataProtectionProvider DataProtectionProvider { get; set; }
    
    	public void Configuration(IAppBuilder app)
    	{
    		ConfigureAuth(app);
    	}
    
    	public void ConfigureAuth(IAppBuilder app)
    	{
    		var maestroAppService = DependencyResolver.Current.GetService<IMaestroAppService>();
    
    		app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<UserManager>());
    
    		//app.CreatePerOwinContext<ContextoIdentity>(ContextoIdentity.Crear);
    
    		app.UseCookieAuthentication(new CookieAuthenticationOptions
    		{
    			AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    			//LoginPath = new PathString("/Account/Login"),
    			Provider = new CookieAuthenticationProvider
    			{
    				OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<UserManager, User, int>(
    					validateInterval: TimeSpan.FromMinutes(30),
    					regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
    																   getUserIdCallback: (id) => (id.GetUserId<int>()))
    			},
    			CookieName = ConfigurationManager.AppSettings.Get("CookieSessionName"),
    			SlidingExpiration = true,
    			ExpireTimeSpan = TimeSpan.FromMinutes(10)
    		});
    		app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
    
    		app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(30));
    
    		app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
    	}
    }

    Y clase que configura el contenedor IoC:

    [assembly: PostApplicationStartMethod(typeof(SimpleInjectorInitializer), "Initialize")]
    namespace AppHAPUYO.Mvc.App_Start
    {
        public class SimpleInjectorInitializer
        {
            public static void Initialize()
            {
                try
                {
                    var container = new Container();
                    container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
    
                    InitializeContainer(container);
    
                    container.Register(() =>
                    {
                        if (HttpContext.Current != null && HttpContext.Current.Items["owin.Environment"] == null && container.IsVerifying)
                            return new OwinContext().Authentication;
    
                        return HttpContext.Current.GetOwinContext().Authentication;
                    }, Lifestyle.Scoped);
    
                    container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
    
                    container.Verify();
    
                    DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
    
                    AutoMapperConfig.RegisterMappings();
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
    
            private static void InitializeContainer(Container container)
            {
                Infraestructure.CrossCutting.IoC.BootStrapper.RegisterServices(container);	// Esto configura las interfaces con las clases concretas
                Infraestructure.CrossCutting.Logging.BootStrapper.ConfiguraLog4net();
    
                container.Register(() => new CredentialsPolicy(), Lifestyle.Scoped);
            }
        }
    }

    Lo que pretendo es inyectar una interfaz vía constructor, sin embargo, si agrego el constructor de la siguiente forma a la clase Startup:

    public Startup(IMaestroAppService maestroAppService)
    {
    	this.maestroAppService = maestroAppService;
    }

    Obtengo una excepción:

    No hay constructor sin parámetros definido para este objeto.

    Y como dije, esto es debido a que se crea la instancia de Startup antes que la instancia de SimpleInjectorInitializer.

    Cómo puedo hacer para que sea al contrario?

    Muchas gracias, saludos.

    viernes, 29 de marzo de 2019 2:12

Respuestas

  • Hola cambia

    [assembly: PostApplicationStartMethod(typeof(SimpleInjectorInitializer), "Initialize")]
    
    

    Por

    assembly: PreApplicationStartMethod(typeof(SimpleInjectorInitializer), "Initialize")]

    Cambiar Post por Pre


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    • Marcado como respuesta eduar2083 sábado, 13 de abril de 2019 20:30
    viernes, 29 de marzo de 2019 2:32
    Moderador

Todas las respuestas

  • Hola cambia

    [assembly: PostApplicationStartMethod(typeof(SimpleInjectorInitializer), "Initialize")]
    
    

    Por

    assembly: PreApplicationStartMethod(typeof(SimpleInjectorInitializer), "Initialize")]

    Cambiar Post por Pre


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    • Marcado como respuesta eduar2083 sábado, 13 de abril de 2019 20:30
    viernes, 29 de marzo de 2019 2:32
    Moderador
  • Gracias Sergio,

    Antes de realizar el cambio, y ejecutarse el método SimpleInjectorInitializer.Initialize, el objeto HttpContext es diferente de nulo y se ejecuta la sentencia que indico a continuación:

    Sin embargo, después de realizar el cambio, efectivamente, se ejecuta el método SimpleInjectorInitializer.Initialize pero el objeto HttpContext es nulo y se ejecuta la sentencia que indico a continuación:

    Por tanto me arroja un NullReferenceException.

    viernes, 29 de marzo de 2019 12:56
  • Hola, pero la condición no debería ser así?

    if (HttpContext.Current != null && HttpContext.Current.Items["owin.Environment"] == null && container.IsVerifying)
                            return HttpContext.Current.GetOwinContext().Authentication; 
    
                        return new OwinContext().Authentication;

    O sea, devolver un new cuando no se cumpla


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    viernes, 29 de marzo de 2019 14:24
    Moderador
  • Hola Sergio,

    He realizado el cambio:

    Sin embargo, ahora me tira una Excepción:

    CredentialsPolicyAppService is registered as 'Web Request' lifestyle, but the instance is requested outside the context of an active (Web Request) scope. Please see https://simpleinjector.org/scoped for more information about how to manage scopes.

    El error ocurre en la siguiente línea:

    Como ves, la clase es un atributo de validación personalizado, que no debe tener constructor.

    El registro de la Interfaz/Clase está definido de la siguiente manera:

    container.Register<ICredentialsPolicyAppService, CredentialsPolicyAppService>(Lifestyle.Scoped);


    • Editado eduar2083 sábado, 30 de marzo de 2019 1:19
    sábado, 30 de marzo de 2019 1:18
  • Hola, deberías cambiar el ámbito de Lifetime. Yo probaría un Transient o Singleton para ello. 

    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    sábado, 30 de marzo de 2019 11:47
    Moderador
  • Hola Sergio,

    He realizado el cambio, primero a Transient.

    container.Register<ICredentialsPolicyAppService, CredentialsPolicyAppService>(Lifestyle.Transient);

    Y ahora al resolver la instancia:

    credentialsPolicyAppService = DependencyResolver.Current.GetService<ICredentialsPolicyAppService>();

    Sigue generando la excepción:

    CredentialsPolicyService is registered as 'Web Request' lifestyle, but the instance is requested outside the context of an active (Web Request) scope. Please see https://simpleinjector.org/scoped for more information about how to manage scopes.

    Al establecer como Singleton, me obliga también hacer Singleton las interfaces de las demás capas (Dominio, Datos incluso la interfaz IUnitOfWork que es utilizada por todos los respositorios)

    container.Register<ICredentialsPolicyAppService, CredentialsPolicyAppService>(Lifestyle.Singleton);

    container.Register<ICredentialsPolicyService, CredentialsPolicyService>(Lifestyle.Singleton);

    container.Register<ICredentialsPolicyRepository>(() => new CredentialsPolicyRepository(container.GetInstance<IdentityContext>()), Lifestyle.Singleton);

    var f1 = Lifestyle.Singleton.CreateRegistration<IUnitOfWork>(() => new UnitOfWork(container.GetInstance<IdentityContext>()), container);
    var f2 = Lifestyle.Singleton.CreateRegistration<IUnitOfWork>(() => new UnitOfWork(container.GetInstance<HapuyoContext>()), container);

    container.RegisterConditional(typeof(IUnitOfWork), f1, InjectedInto<UnitOfWork>);
    container.RegisterConditional(typeof(IUnitOfWork), f2, InjectedInto2<UnitOfWork>);

    Pero con esto, el DbContext está cerrado al realizar una consulta:

    The operation cannot be completed because the DbContext has been disposed.

    Quisiera que se mantenga como Scoped pero no puedo hacerlo simplemente porque la clase atributo no debe tener constructor.



    • Editado eduar2083 lunes, 1 de abril de 2019 20:07
    lunes, 1 de abril de 2019 20:02
  • Hola. Puedes mostrar el código de

    CredentialsPolicyAppService

    Y de 

    CredentialsPolicyService

    Hay que revisar las dependencias de dichas clases 


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    lunes, 1 de abril de 2019 22:11
    Moderador
  • Sergio,

    CredentialsPolicyAppService (Capa de Aplicación)

    using Application.Core.Services.Base;
    using Application.Identity.Contracts.Services;
    using Domain.Identity.Contracts.Services;
    using Infraestructure.CrossCutting.Identity.Models;
    
    namespace Application.Identity.Services
    {
        public class CredentialsPolicyAppService : AppGenericService<CredentialsPolicy>, ICredentialsPolicyAppService
        {
            private readonly ICredentialsPolicyService credentialsPolicyService;
    
            public CredentialsPolicyAppService(ICredentialsPolicyService credentialsPolicyService) : base(credentialsPolicyService)
            {
                this.credentialsPolicyService = credentialsPolicyService;
            }
        }
    }

    CredentialsPolicyService (Capa de Dominio)

    using Domain.Core.Contracts.Repositories.Base;
    using Domain.Core.Services.Base;
    using Domain.Identity.Contracts.Services;
    using Infraestructure.CrossCutting.Identity.Contracts.Repositories;
    using Infraestructure.CrossCutting.Identity.Models;
    using Infraestructure.CrossCutting.Utilities.Attributes;
    
    namespace Domain.Identity.Services
    {
        public class CredentialsPolicyService : GenericService<CredentialsPolicy>, ICredentialsPolicyService
        {
            private readonly IUnitOfWork unitOfWork;
            private readonly ICredentialsPolicyRepository credentialsPolicyRepository;
    
            public CredentialsPolicyService([Named("IdentityDbContext")] IUnitOfWork unitOfWork, ICredentialsPolicyRepository credentialsPolicyRepository) : base(unitOfWork, credentialsPolicyRepository)
            {
                this.unitOfWork = unitOfWork;
                this.credentialsPolicyRepository = credentialsPolicyRepository;
            }
        }
    }

    Tanto la clase AppGenericService como GenericService son clases que implementan interfaces genéricas:

    using Application.Core.Contracts.Base;
    using Domain.Core.Contracts.Services.Base;
    using Domain.Core.Specification;
    using System;
    using System.Collections.Generic;
    using System.Linq.Expressions;
    
    namespace Application.Core.Services.Base
    {
        public class AppGenericService<TEntity> : IAppGenericService<TEntity>
            where TEntity : class
        {
            private readonly IGenericService<TEntity> service;
    
            public AppGenericService(IGenericService<TEntity> service)
            {
                this.service = service;
            }
    
            public virtual TEntity Add(TEntity item)
            {
                try
                {
                    return service.Add(item);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
    
            public virtual IEnumerable<TEntity> Add(List<TEntity> items)
            {
                try
                {
                    return service.Add(items);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
    
            public virtual TEntity Update(TEntity item)
            {
                try
                {
                    return service.Update(item);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
    
            public virtual List<TEntity> Update(List<TEntity> items)
            {
                try
                {
                    return service.Update(items);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
    
            public virtual void Delete(TEntity item)
            {
                try
                {
                    service.Delete(item);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
    
            public virtual TEntity GetBySpecification(ISpecification<TEntity> specification)
            {
                try
                {
                    return service.GetBySpecification(specification);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
    
            public virtual TEntity GetBySpecificationWidthTracking(ISpecification<TEntity> specification)
            {
                try
                {
                    return service.GetBySpecificationWidthTracking(specification);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
    
            public virtual IEnumerable<TEntity> ListAll()
            {
                return service.ListAll();
            }
    
            public virtual IEnumerable<TEntity> ListAllWithTracking()
            {
                return service.ListAllWithTracking();
            }
    
            public virtual IEnumerable<TEntity> ListFilteredElements(Expression<Func<TEntity, bool>> filter)
            {
                return service.ListFilteredElements(filter);
            }
    
            public virtual IEnumerable<TEntity> ListPagedElements<S>(int pageIndex, int pageSize, Expression<Func<TEntity, S>> orderByExpression, bool ascending = true)
            {
                try
                {
                    return service.ListPagedElements(pageIndex, pageSize, orderByExpression);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
    
            public virtual IEnumerable<TEntity> ListBySpecification(ISpecification<TEntity> specification)
            {
                return service.ListBySpecification(specification);
            }
        }
    }

    Saludos.


    • Editado eduar2083 martes, 2 de abril de 2019 0:17
    martes, 2 de abril de 2019 0:14
  • Creo que tengo problemas con la implementación del patrón Repositorio y Unidad de Trabajo.

    Tienen algún ejemplo de cómo implementarlo con EF CodeFirst en un aplicación N-Capas?

    Muchas gracias, saludos.


    • Editado eduar2083 viernes, 5 de abril de 2019 1:00
    viernes, 5 de abril de 2019 1:00