SimpleInjector 在 .Net MVC 站点启动时抛出间歇性错误

SimpleInjector throwing intermittent error on .Net MVC Site start up

我有一个 ASP.NET MVC,它在 Application_Start 方法中初始化简单注入器。

该错误在本地正常使用站点时间歇性出现,在Visual Studio重建站点时总是会消失。在部署时,错误会立即出现,但可以通过在服务器上重新启动应用程序来使其消失。过了一会儿它又出现了。

启动代码如下:

public class CmsApplication : System.Web.HttpApplication
{
    private Container _container;
    private IQueryProcessor queryProcessor;

    private static ISiteManager _manager;
    public static ISiteManager SiteManager { get { return _manager; } }

    protected virtual void Application_Start()
    {
        // Mapping using AutoMapper
        CMS.UI.Client.Mapping.MappingConfig.RegisterMappings();

        // Initialize SimpleInjector
        InitializeIoC();

        AutoMapper.Mapper.AssertConfigurationIsValid();

        // Get query processor - this is used for handling database calls.
        queryProcessor = _container.GetInstance<IQueryProcessor>();

        // Get configuration
        IConfiguration configuration = CMS.Configuration.ConfigurationFactory.Get();

        // MVC Stuff
        AreaRegistration.RegisterAllAreas();
        RouteConfig routeConfig = new RouteConfig(queryProcessor);
        routeConfig.RegisterRoutes(RouteTable.Routes);

        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

        // Regular MVC startup
        AreaRegistration.RegisterAllAreas();

        // Global SiteManager property. THIS COULD BE CAUSING THE ISSUE!
        _manager = _container.GetInstance<ISiteManager>();

    }

    /// <summary>
    /// Method to initialize the IoC Container
    /// </summary>
    protected void InitializeIoC()
    {
        // Initialize DI container
        _container = new Container();

        // Allows container to inject into filter constructors
        _container.RegisterMvcIntegratedFilterProvider();

        // Mapping
        _container.Register<AutoMapper.IMappingEngine>(() => (AutoMapper.IMappingEngine)AutoMapper.Mapper.Engine, Lifestyle.Singleton);
        _container.Register<IMapProcessor, MapProcessor>(); // Map Processor

        // Configuration
        _container.Register<IConfiguration>(() => (CMS.Configuration.Configuration)System.Configuration.ConfigurationManager.GetSection("pipeline"), Lifestyle.Singleton);

        // Site Manager
        _container.Register<ISiteManager, SiteManager>(Lifestyle.Singleton);

        // Context and UnitOfWork Factories
        _container.RegisterPerWebRequest<IContext>(() => new CrmContext("Name=CrmContext"));

        // Context and UnitOfWork     
        _container.RegisterPerWebRequest<Pipeline.Core.Data.UnitOfWork.IUnitOfWork, Crm.UnitOfWork.EFUnitOfWork>();

        // Repositories
        _container.Register<ISiteRepository, SiteRepository>();
        _container.Register<IFolderRepository, FolderRepository>();

        // Sitemap
        _container.Register<ISitemapGenerator, SitemapGenerator>();

        // Robots
        _container.Register<IRobotsGenerator, RobotsGenerator>();

        // Command Handlers
        _container.RegisterOpenGeneric(
            typeof(IBatchCommandHandler<>), typeof(BatchCommandHandler<>));

        _container.RegisterManyForOpenGeneric(
            typeof(ICommandHandler<>),
            AppDomain.CurrentDomain.GetAssemblies());

        _container.RegisterManyForOpenGeneric(
            typeof(ICommandHandler<,>),
            AppDomain.CurrentDomain.GetAssemblies());

        // Validators
        _container.RegisterManyForOpenGeneric(
            typeof(IValidator<>),
            AppDomain.CurrentDomain.GetAssemblies());

        // Query Handlers
        _container.RegisterManyForOpenGeneric(
            typeof(IQueryHandler<,>),
            AppDomain.CurrentDomain.GetAssemblies());

        // Query Processor
        _container.Register<IQueryProcessor>(() => new QueryProcessor(_container));

        // Validation Decorators
        _container.RegisterDecorator(typeof(ICommandHandler<,>),
            typeof(ValidationCommandHandlerDecorator<,>),
            c => typeof(ICommandHandler<,>)
                    .MakeGenericType(c.ServiceType.GetGenericArguments())
                        .IsAssignableFrom(c.ImplementationType));


                    // Verify the container
        _container.Verify();

        //// Register the container as MVC IDependencyResolver
        DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(_container));

    }

InitializeIoC方法就是各种类型的注册,没什么特别的

InitializeIoC方法之后,RouteConfig调用使用通过SimpleInjector解析的类型的数据库调用。这似乎没问题。

当 _manager 字段设置为已解析类型使用 IQueryHandler<SiteQuery, ISiteInfo> 时,似乎会抛出错误,这是错误的来源。 ISiteManager 被 SimpleInjector 设置为单例,我不确定这是否是问题的根源。

具体的处理程序肯定存在(因为它确实有效,错误是间歇性的)。

我对此有点困惑!

这是错误和堆栈跟踪:

Error Page

No registration for type IQueryHandler could be found. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: SimpleInjector.ActivationException: No registration for type IQueryHandler could be found.

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[ActivationException: No registration for type IQueryHandler could be found.]
SimpleInjector.Container.ThrowMissingInstanceProducerException(Type serviceType) +136 SimpleInjector.Container.GetInstance(Type serviceType) +76
Pipeline.Crm.Services.QueryProcessor.QueryProcessor.Pipeline.Core.Services.Queries.Interfaces.IQueryProcessor.Process(IQuery query) in c:\Projects\Pipeline CMS\Pipeline.CMS.Services\QueryProcessor\QueryProcessor.cs:29
Pipeline.CMS.UI.Client.Management.SiteManager..ctor(IQueryProcessor queryProcessor, IConfiguration configuration) in c:\Projects\Pipeline CMS\Pipeline.CMS.UI.Client\SiteManager\SiteManager.cs:23
lambda_method(Closure ) +148
SimpleInjector.Lifestyles.SingletonLifestyleRegistrationBase1.CreateInstanceWithNullCheck() +62 System.Lazy1.CreateValue() +415 System.Lazy1.LazyInitValue() +152 System.Lazy1.get_Value() +75
SimpleInjector.Lifestyles.SingletonLifestyleRegistrationBase1.BuildExpression() +16 SimpleInjector.Registration.BuildExpression(InstanceProducer producer) +50<br> SimpleInjector.InstanceProducer.BuildExpressionInternal() +34<br> System.Lazy1.CreateValue() +415 System.Lazy1.LazyInitValue() +152 System.Lazy1.get_Value() +75
SimpleInjector.InstanceProducer.BuildExpression() +55

[ActivationException: The registered delegate for type ISiteManager threw an exception. No registration for type IQueryHandler could be found.]
SimpleInjector.InstanceProducer.BuildExpression() +97
SimpleInjector.InstanceProducer.VerifyExpressionBuilding() +31

[InvalidOperationException: The configuration is invalid. Creating the instance for type ISiteManager failed. The registered delegate for type ISiteManager threw an exception. No registration for type IQueryHandler could be found.]
SimpleInjector.InstanceProducer.VerifyExpressionBuilding() +85
SimpleInjector.Container.VerifyThatAllExpressionsCanBeBuilt(InstanceProducer[] producersToVerify) +28
SimpleInjector.Container.VerifyThatAllExpressionsCanBeBuilt() +127
SimpleInjector.Container.Verify() +109
Pipeline.CMS.UI.Client.CmsApplication.InitializeIoC() in c:\Projects\Pipeline CMS\Pipeline.CMS.UI.Client\CmsApplication.cs:156 Pipeline.CMS.UI.Client.CmsApplication.Application_Start() in c:\Projects\Pipeline CMS\Pipeline.CMS.UI.Client\CmsApplication.cs:47
Pipeline.CMS.Client.MvcApplication.Application_Start() in c:\Projects\Pipeline CMS\Banerjee.Web\Global.asax.cs:11

[HttpException (0x80004005): The configuration is invalid. Creating the instance for type ISiteManager failed. The registered delegate for type ISiteManager threw an exception. No registration for type IQueryHandler could be found.]
System.Web.HttpApplicationFactory.EnsureAppStartCalledForIntegratedMode(HttpContext context, HttpApplication app) +9936825
System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) +118
System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) +172
System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +336
System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +296

[HttpException (0x80004005): The configuration is invalid. Creating the instance for type ISiteManager failed. The registered delegate for type ISiteManager threw an exception. No registration for type IQueryHandler could be found.]
System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +9915364 System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +101 System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +254

我的第一直觉是,在注册过程中调用 AppDomain.CurrentDomain.GetAssemblies() 时,包含查询处理程序实现的程序集尚未加载。您的查询处理程序实现可能位于与 IQueryHandler<,> 接口不同的程序集中。 ASP.NET.

中程序集的加载可能有点不确定

不要调用 AppDomain.CurrentDomain.GetAssemblies(),而是尝试按如下方式直接指定程序集:

var assemblies = new[]
{
    typeof(SomeQueryHandlerImplementation).Assembly,
    typeof(SqlRepository<>).Assembly,
};

// Simple Injector v3.x
_container.Register(typeof(ICommandHandler<>), assemblies);
_container.Register(typeof(ICommandHandler<,>), assemblies);
_container.Register(typeof(IValidator<>), assemblies);
_container.Register(typeof(IQueryHandler<,>), assemblies);

// Simple Injector v2.x
_container.RegisterManyForOpenGeneric(typeof(ICommandHandler<>), assemblies);
_container.RegisterManyForOpenGeneric(typeof(ICommandHandler<,>), assemblies);
_container.RegisterManyForOpenGeneric(typeof(IValidator<>), assemblies);
_container.RegisterManyForOpenGeneric(typeof(IQueryHandler<,>), assemblies);

一个关于ValidationCommandHandlerDecorator装饰器注册的小提示。已注册的谓词始终 returns 为真。因此,注册可以简化为以下内容:

_container.RegisterDecorator(typeof(ICommandHandler<,>),
    typeof(ValidationCommandHandlerDecorator<,>));