2

In an ASP.NET Web API project, if you are using Simple Injector for dependency injection, it will register all controllers with this line of code:

container.RegisterWebApiControllers(
    System.Web.Http.GlobalConfiguration.Configuration);

If you have Elmah logger in the same project, to access the logger you just use http://yourapp.com/elmah as shown here.

The problem is that Simple Injector thinks elmah is a controller and produces this error:

No registration for type ElmahController could be found.

I thought to configure Simple Injector to avoid construction if the type has elmah but I cannot figure out how.

What do I need to do to fix this?

Here is the full error:

No registration for type ElmahController could be found. Make sure ElmahController is registered, for instance by calling 'Container.Register();' during the registration phase. An implicit registration could not be made because Container.Options.ResolveUnregisteredConcreteTypes is set to 'false', which is now the default setting in v5. This disallows the container to construct this unregistered concrete type. For more information on why resolving unregistered concrete types is now disallowed by default, and what possible fixes you can apply, see https://simpleinjector.org/ructd. 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 ElmahController could be found. Make sure ElmahController is registered, for instance by calling 'Container.Register();' during the registration phase. An implicit registration could not be made because Container.Options.ResolveUnregisteredConcreteTypes is set to 'false', which is now the default setting in v5. This disallows the container to construct this unregistered concrete type. For more information on why resolving unregistered concrete types is now disallowed by default, and what possible fixes you can apply, see https://simpleinjector.org/ructd.

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 ElmahController could be found. Make sure ElmahController is registered, for instance by calling 'Container.Register();' during the registration phase. An implicit registration could not be made because Container.Options.ResolveUnregisteredConcreteTypes is set to 'false', which is now the default setting in v5. This disallows the container to construct this unregistered concrete type. For more information on why resolving unregistered concrete types is now disallowed by default, and what possible fixes you can apply, see https://simpleinjector.org/ructd. ] SimpleInjector.Container.ThrowNotConstructableException(Type concreteType) +138 SimpleInjector.Container.ThrowMissingInstanceProducerException(Type type) +88 SimpleInjector.Container.GetInstanceForRootType(Type serviceType) +186 SimpleInjector.Container.GetInstance(Type serviceType) +82 System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +64

[InvalidOperationException: An error occurred when trying to create a controller of type 'Elmah.Mvc.ElmahController'. Make sure that the controller has a parameterless public constructor.] System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +245 System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory) +267 System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +77 System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +970 System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step) +75 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +158

CodingYoshi
  • 25,467
  • 4
  • 62
  • 64

2 Answers2

2

Ok I think I figured it out.

Some Notes

Since I have configured the container's lifestyle like this:

container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();

I cannot use LifeStyle.Scoped when registering the ElmahController. The 2 other options are LifeStyle.Singleton and LifeStyle.Transient. We don't want LifeStyle.Singleton because numerous instances are needed, thus we have one option left which is LifeStyle.Transcient.

Solution

You need to register it with Simple Injector:

container.Register<Elmah.Mvc.ElmahController>(Lifestyle.Transient);

The line above will result in a different error:

The configuration is invalid. The following diagnostic warnings were reported: -[Disposable Transient Component] ElmahController is registered as transient, but implements IDisposable.

To get rid of that error, I first checked to see if the Dispose method for ElmahController has anything useful. It turns out it simply derives from System.Web.Mvc.Controller and here is the Dispose method:

public void Dispose()
{
    Dispose(true /* disposing */);
    GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
}

Since it does nothing useful, it is fine if it is not called. Thus the following code is enough:

container.GetRegistration(typeof(Elmah.Mvc.ElmahController)).Registration
.SuppressDiagnosticWarning(
    DiagnosticType.DisposableTransientComponent,
    "No need to call dispose because it does nothing useful.");
Steven
  • 166,672
  • 24
  • 332
  • 435
CodingYoshi
  • 25,467
  • 4
  • 62
  • 64
1

The RegisterWebApiControllers extension method uses ASP.NET Web API's IAssembliesResolver abstraction to get the list of assemblies to look for Controller instances.

Apparently the ElmahController lives in an assembly that is not returned by IAssembliesResolver.GetAssemblies(). To fix this you can do two things:

  • Call the RegisterWebApiControllers overload that accepts a list of Assembly instances and pass in the application assemblies that contain your controllers + the ELMAH assembly that contains your assembly
  • Customize Web API's controller discovery mechanism as described here

Especially the first solution is simpler compared to manually registering the controller, because under the covers RegisterWebApiControllers ensures the false-positive diagnostic warning for disposable components is suppressed.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • Thanks this sounds like a better approach than mine. Could you please elaborate, in your answer, what you mean by "registration is suppressed"--especially the "suppressed" part. – CodingYoshi Oct 05 '22 at 20:41
  • @CodingYoshi: By "registration is suppressed" I actually meant " the false-positive diagnostic warning for disposable components is suppressed". I updated my answer to reflect this. – Steven Oct 06 '22 at 07:18