1

I have a C# solution that includes multiple WebAPI projects. One of these projects, let's call it Project A, already uses SimpleInjector successfully. I'm trying to add SimpleInjector to another of these WebAPI projects, Project B, but I'm facing a problem.

I'm trying to create a second container at Project B as I did in Project A, but when I do this and try to build the solution, there is and exception in Project A, which is built after Project B, at the container.Verify() method. It tells me that a interface that is located at Project B (IUserService) is not properly registered at Project A, but Project A doesn't use this interface.

In Project B at Global.asax.cs I have this configuration:

/* Dependency Injection */
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();


container.Register<IUserService>(() => { return new UserService(); }, Lifestyle.Scoped);
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

container.Verify();

GlobalConfiguration.Configuration.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);

In Project A, I have this configuration:

/* Dependency Injection */
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();

container.Register<ILog>(() => { return LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); }, Lifestyle.Scoped);
container.Register<IFundRepository>(() => { return new IporangaFundRepository(dbConnectorMiddle); }, Lifestyle.Scoped);
container.Register<ITradeRepository>(() => { return new IporangaTradeRepository(dbConnectorMiddle, middleReadClient); }, Lifestyle.Scoped);
container.Register<ITradeManager, TradeManager>(Lifestyle.Scoped);

container.Register<ITradeService>(() => new TradeService(container.GetInstance<ITradeManager>()),Lifestyle.Scoped);
container.Register<ISimulationService>(() => new SimulationService(container.GetInstance<ITradeService>()), Lifestyle.Scoped);
container.Register<IBookerService>(() => new BookerService(container.GetInstance<ITradeService>(), container.GetInstance<ISimulationService>()), Lifestyle.Scoped);
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

container.Verify();

GlobalConfiguration.Configuration.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);

The error message:

System.InvalidOperationException: 'The configuration is invalid. Creating the instance for type UserController failed. The constructor of type UserController contains the parameter with name 'userService' and type IUserService that is not registered. Please ensure IUserService is registered, or change the constructor of UserController.'

noen
  • 346
  • 3
  • 10
  • While looking at your configuration, I can't help noticing that you make little use of the container's ability to create instances and automatically have there dependencies injected. You should prefer that approach, because 1. it leads.to simpler code and 2. allows Simple Injector to detect errors for you. – Steven Oct 29 '18 at 21:43
  • Hey @Steven ! Glad to see you here! Would you mind to explain to me how I'd do this approach? I'm new to the DI world – noen Oct 30 '18 at 13:23
  • See my answer for an alternative way of making registrations. – Steven Oct 30 '18 at 14:18

2 Answers2

4

The following is not an answer to your question, but rather a suggestion how to improve and simplify the registrations for Project A.

Instead of using the posted code, I'd suggest using the following code to wire up the Container instance of Project A:

var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();

// For Log4NetAdapter<T>, please see: https://stackoverflow.com/a/25113659
container.RegisterConditional(typeof(ILog),
    c => typeof(Log4NetAdapter<>).MakeGenericType(c.Consumer.ImplementationType),
    Lifestyle.Singleton,
    c => true);

container.RegisterInstance(dbConnectorMiddle);
container.RegisterInstance(middleReadClient);

container.Register<IFundRepository, IporangaFundRepository>(Lifestyle.Scoped);
container.Register<ITradeRepository, IporangaTradeRepository(Lifestyle.Scoped);
container.Register<ITradeManager, TradeManager>(Lifestyle.Scoped);

container.Register<ITradeService, TradeService>(Lifestyle.Scoped);
container.Register<ISimulationService, SimulationService>(Lifestyle.Scoped);
container.Register<IBookerService, BookerService(Lifestyle.Scoped);

container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

container.Verify();

GlobalConfiguration.Configuration.DependencyResolver =
    new SimpleInjectorWebApiDependencyResolver(container);

This is better because:

  • Registrations are simplified because they only specify the mapping between the abstraction (e.g. ITradeService) and the implementation (e.g. TradeService), while letting Simple Injector Auto-Wire the types by inspecting the constructors' dependencies.
  • Not only are the registrations simplified, but now Simple Injector is aware of the structure of the complete dependency graph and can therefore effectively do verification on your behalf. This will, for instance, allow Simple Injector to find any Lifestyle Mismatches.
  • A conditional registration is used for the ILog abstraction. This allows Simple Injector to inject an ILog implementation specific to the consuming type. This allows your logging library to log information about the originating class. This is something that didn't work in your registration, where it would always inject a logger with a type that contains your registrations.
Steven
  • 166,672
  • 24
  • 332
  • 435
3

RegisterWebApiControllers uses reflection under the covers to search for implementations of ApiController.

I guess, based upon the error you get, project B is referenced by project A and the call to container.RegisterWebApiControllers in project A finds and registers the controllers of project B also.

Now when .Verify() is called it will scan the constructors of the ApiControllers in project B, finds a dependency on IUserService and breaks because this registration is actually missing in project A.

The integration package contains another overload for RegisterWebApiControllers which takes an array of assemblies that must be scanned for ApiControllers instead of scanning through all referenced assemblies.

Assuming the assembly of project A contains all ApiControllers that need to be registered this overload can be used in project A like:

container.RegisterWebApiControllers(GlobalConfiguration.Configuration,
         new[] {typeof(MyApiControllerInProjectA).Assembly});
Ric .Net
  • 5,540
  • 1
  • 20
  • 39
  • 1
    So the question now becomes: why is OP referencing project B from project A? That doesn't seem a right design to me. Best is to extract the commonality into a new assembly that both projects csn reference. – Steven Oct 29 '18 at 21:37