Graham Graham - 3 months ago 42
ASP.NET (C#) Question

SimpleInjector throwing intermittent error on .Net MVC Site start up

I have an ASP.NET MVC which initializes Simple Injector in the Application_Start method.

The error appears intermittently during normal use of the site locally, and always disappears when the site is rebuilt in Visual Studio. On deployment, the error appears immediately but can be made to go away by restarting the app on the server. After a little while it reappears.

The start up code is as follows:

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));

}


The
InitializeIoC
method is just registration of the various types and nothing special.

After the
InitializeIoC
method, the
RouteConfig
call uses a database call using a type resolved through SimpleInjector. This appears to be fine.

The error seems to be being thrown when the _manager field is set as the resolved type uses
IQueryHandler<SiteQuery, ISiteInfo>
which is the source of the error. The ISiteManager is being set as a singleton by SimpleInjector, whether this is the source of the problem, I'm not sure.

The concrete handler is definitely present (as it does actually work, the bug is intermittent).

I'm a bit stumped on this!

Here is the error and stack trace:


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.SingletonLifestyleRegistrationBase
1.CreateInstanceWithNullCheck()
+62 System.Lazy
1.CreateValue() +415 System.Lazy
1.LazyInitValue() +152 System.Lazy
1.get_Value() +75

SimpleInjector.Lifestyles.SingletonLifestyleRegistrationBase
1.BuildExpression()
+16 SimpleInjector.Registration.BuildExpression(InstanceProducer producer) +50

SimpleInjector.InstanceProducer.BuildExpressionInternal() +34

System.Lazy
1.CreateValue() +415 System.Lazy
1.LazyInitValue() +152
System.Lazy
1.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

Answer

My first intuition is that the assembly that contains the query handler implementations isn't loaded yet at the time that AppDomain.CurrentDomain.GetAssemblies()is called during the registration process. Your query handler implementations probably live in a different assembly than the IQueryHandler<,> interface does. Loading of assemblies can be a bit undeterministic in ASP.NET.

Instead of calling AppDomain.CurrentDomain.GetAssemblies(), try specifying the assemblies directly as follows:

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);

One small note about the registration of the ValidationCommandHandlerDecorator decorator. The registered predicate always returns true. The registration can therefore be simplified to the following:

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