Unity DI, design-time configuration in layered application - c#

We have been using Unity container for dependency injection without any problems for a couple of years. Simplified we have three layers Web/Domain/Data.
We use design-time configuration to be able to switch some implementations for different builds.
IMyDataInterface is registered from web.config. Data-layer resolves IMyDataInterface successfully. When trying to resolve IMyDataInterface from Domain-layer it fails with the following message.
InvalidOperationException - The current type, IMyDataInterface, is an interface and cannot be constructed. Are you missing a type mapping?
Each layer has a UnityConfig-class that registers the dependencies.
Web creates IUnityContainer instance A
Calls Web.UnityConfig.RegisterTypes(A)
Registers design-time types from web.config by running A.LoadConfiguration()
(web.config contains registration of IMyDataInterface)
Registers types from Web-layer
Calls Domain.UnityConfig.RegisterTypes(A)
Registers types from Domain-layer
Calls Data.UnityConfig.RegisterTypes(A)
Registers types from Data-layer
public class MyDataClass
{
public MyDataClass(IMyDataInterface dependency)
{
// dependency is resolved
}
}
public class MyDomainClass
{
public MyDomainClass(IMyDataInterface dependency)
{
// dependency FAILS to resolve
}
}
If I register type IMyDataInterface in Data.UnityConfig, everything works but I need to be able to switch implementation at design-time using web.config.

Related

Share Microsoft.Extensions.DependencyInjection.ServiceProvider configuration between projects

I have a solution that has the following projects
Acme.Core
Acme.Domain
Acme.Repositories
Acme.Services
Acme.Web
In the past I've used Unity for DI in full framework projects. I was able to register concrete objects to interface mappings in executable projects (web apps, console app, test apps).
I'm trying to implement the same approach with .NET Core. I wanted to first try using the Microsoft.Extensions.DependencyInjection library. Within the ASP.NET Core application it works great. Unfortunately I've run into an issue when I try to share/reference that instance with the registions to other projects, such as a .NET Standard library.
My idea was to inject the ServiceProvider into the constructor of the service:
public class AddressService : BaseService, IAddressService
{
private readonly IServiceProvider _serviceProvider;
public AddressService(IServiceProvider serviceProvider, string userOrProcessName)
{
_serviceProvider = serviceProvider;
}
public IReadOnlyList<IState> GetAllStates()
{
_serviceProvider.GetService<IAddressRepository>();
// other logic removed
}
}
I tried the following inside the Startup.ConfigureServices():
services.AddTransient<IAddressService>(s => new AddressService(HttpContext.RequestServices, Environment.UserName));
The issue I ran into is that I cannot reference HttpContext.RequestServices outside of a Controller. I haven't been able to figure another way of passing the ServiceProvider instance.
My questions:
How do pass a reference for the current ServiceProvider?
Is there a better design to accomplish my goal sharing the configuration of Microsoft.Extensions.DependencyInjection in multiple libraries?
Prevent injecting IServiceProvider into your application components; that leads to the Service Locator anti-pattern.
Instead, you should build up application components solely using Constructor Injection. This means that your AddressService should require IAddressRepository as constructor argument, not IServiceProvider. For instance:
public class AddressService : IAddressService
{
private readonly IAddressRepository repo;
public AddressService(IAddressRepository repo, IUserContext userContext)
{
this.repo = repo;
}
public IReadOnlyList<IState> GetAllStates()
{
// other logic removed
}
}
Also try to prevent injecting primites into your constructors. It's not a bad practice per se, but it does complicate object graph construction. Instead, either wrap the value into a class, in case its a configuration value, or hide it behind an abstraction (as shown above) in case it's a runtime value.
Both practices simplify both your application code and the Composition Root.
For instance, this will be the result of the previous AddressService redesign:
services.AddTransient<IAddressRepository, SqlAddressRepository>();
services.AddTransient<IAddressService, AddressService>();
services.AddScoped<IUserContext, UserContext>();
services.AddHttpContextAccessor();
Here, UserContext could be defined as follows:
public class UserContext : IUserContext
{
private readonly IHttpContextAccessor accessor;
public UserContext(IHttpContextAccessor accessor) => this.accessor = accessor;
public string UserName => this.accessor.HttpContext.User.Identity.Name;
}
In order to share configuration across multiple projects, you can put the configuration into a shared assembly, and register (not resolve) them in there. Many dependency injection libraries offer that functionality. e.g.
in Autofac you create a module (https://autofaccn.readthedocs.io/en/latest/configuration/modules.html) that takes a container builder to configure:
protected override void Load(ContainerBuilder builder) { ... }
SimpleInjector provides packages: https://simpleinjector.readthedocs.io/en/latest/howto.html#package-registrations
Unity can support something similar: Can I register my types in modules in Unity like I can in Autofac?
Ninject has a similar module feature: What is the intention of Ninject modules?
A similar feature has be created for Microsoft.Extensions.DependencyInjection: https://github.com/aruss/DotNetCore_ModularApplication
At a high level, you create a method that receives the DI container and adds your registrations to that container. If your DI framework doesn't provide hooks you need to manually call the method yourself, but the general concept doesn't change.
Splitting registrations into modules allows you to easily group similar sets of functionality while maintaining the flexibility of incorporating different sets of functionality into different projects. You could of course create a single shared assembly that registered the union of all dependencies for all projects, but that would carry around unnecessary baggage and result in a less reusable implementation.
The key point as Steven points out is that you configure the container and let it inject the dependencies rather than looking from the inside out for the dependencies.

Can't inject a delegate using ASP.NET Core DI

Say I've a MVC Core Controller like this:
public class SomeController
{
public SomeController(IConfiguration appConfig, Func<string> someDelegate)
{
}
}
Also, I'm using AutoFac to resolve injections. Object injections are working flawlessly while adding a delegate injection produces an ASP.NET Core exception which tells that Func<string> can't be injected because there's no component to inject with such type.
When I try to manually resolve SomeController using AutoFac I get the desired behavior.
Is there any way to support this scenario without using AutoFac to resolve controllers?
Controllers are not resolved via DI by default, they are constructed in the DefaultControllerFactory or so.
Update
Microsoft.Extensions.DependencyInjection doesn't support named components, discovery, auto registrations, decorators etc.
It's meant to be simple out of the box IoC and provide the base for DI for basic applications and offer easy way for 3rd party IoC containers (with advanced features such as auto discovery, decorators etc.) to be integrated (basically all they need is process the information in IServiceCollection and return their own implementation of IServiceProvider from Configure method).
Tag helpers, controllers and view components are different in this aspect as they have their own activators (the default one use activation utilities, which at some point further down the pipeline use the service provider). For that reasons AddControllersAsServices exists, because it replaces DefaultControllerActivator (which uses ActivationUtilities, see DefaultControllerActivator.cs) with ServiceBasedActivator (which uses IServiceProvider, see ServiceBasedControllerActivator).
Also see this related answer for details on how to resolve controllers, tag helpers and view components via DI.
var builder = services
.AddMvc()
.AddControllersAsServices() // this one for your case
.AddViewComponentsAsServices()
.AddTagHelpersAsServices();
I was just run into this issue myself so I thought I would share for future reference as I had one case where I wanted to resolve a delegate but including an additional library seemed like overkill.
Given the following defintions:
public interface ISomething { /*...*/ };
public interface ISomeService { /*...*/ }
public class SomeService : ISomeService { /*...*/ }
public class Something
{
public Something(ISomeService service, string key) { /*...*/ }
}
// I prefer using a delegate for readability but you
// don't have to use one
public delegate ISomething CreateSomething(string key);
The delegate can be registered like this:
var builder = services
.AddSingleton<ISomeService, SomeService>()
.AddTrasient<CreateSomething>(provider => key => new Something(provider.GetRequiredService<ISomeService>(), key));

Using Nininject MVC with class libraries

I'm quite new to IoC frameworks so please excuse the terminology.
So what I have is a MVC project with the Nininject MVC references.
I have other class libarys in my project e.g. Domain layer, I would like to be able to use the Ninject framework in there but all of my bindings are in the NinjectWebCommon.cs under the App_Start folder in the MVC project:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IHardwareService>().To<WindowsHardwareService>();
kernel.Bind<IStatusApi>().To<StatusApiController>();
}
Currently in my class library I am using constructor injection but sometime I am having to hardcode the dependencies:
var service = new WindowsHardwareService();
When I would like to be able to do the following:
IKernel kernel = new StandardKernel(.....);
var context = kernel.Get<IHardwareService>();
I have not been doing the following because I do not have any modules?
All of the documentation I have read is mainly aimed at the regular Ninject library and not the MVC version.
What do I need to do, and how can I use the regular Ninject library with the MVC version?
Update
This is what I have tried:
The aim of this is so that each project can load the module and get the current injected interface.
App_Start/NinjectWebCommon.cs (In MVC Project)
private static void RegisterServices(IKernel kernel)
{
var modules = new IoCModules();
var newKernal = modules.GetKernel();
kernel = newKernal;
}
IoCModules.cs (In Project.Ioc project)
public class IoCModules
{
public IKernel GetKernel()
{
var modules = new CoreModule();
return modules.Kernel;
}
}
CoreModule.cs (In Project.IoC.Modules project) <-- This is where all the references to all projects are, this get's around any circular dependency issues.
public class CoreModule : NinjectModule
{
public override void Load()
{
Bind<IHardwareService>().To<WindowsHardwareService>();
Bind<IStatusApi>().To<StatusApiController>();
}
}
But I am currently getting the following:
Error activating IHardwareService
No matching bindings are available, and the type is not self-bindable.
Activation path:
2) Injection of dependency IHardwareService into parameter service of constructor of type DashboardController
1) Request for DashboardController
Suggestions:
1) Ensure that you have defined a binding for IHardwareService.
2) If the binding was defined in a module, ensure that the module has been loaded into the kernel.
3) Ensure you have not accidentally created more than one kernel.
4) If you are using constructor arguments, ensure that the parameter name matches the constructors parameter name.
5) If you are using automatic module loading, ensure the search path and filters are correct.
It seems that you have a lot of questions what needs to be answered here, so I will try to do my best.
Based on your current question I will try to "draw up" a simplified architecture of your current implementation:
Domain layer: The core of your domain, place of your business entities, etc.
Infrastructure layer: This is where your services reside e.g.: WindowsHardwareService
IOC: I tend to call to this as DependencyResolution assembly.
UI: MVC application
Assuming this all above, we can state that your applications Composition Root or Entry point is the UI MVC project. One of the main concepts using a DI Container that is you initalize it in the Composition Root set up/do all your needed bindings and registrations here. The main intention to do it in the entry point is to avoid the Service Locator anti-pattern.
By using a DI Container you don't new() up your class implementations or get the kernel but rather ask for the registered dependency, following the rule of Inversion Of Control or also known as the Hollywood principle.
After the philosphy course, we can finally get to some actual implementation.
Creating an Ninject module: in your IOC assembly, lets call this file as ServiceModule.cs
using Ninject.Modules;
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<IHardwareService>().To<WindowsHardwareService>();
Bind<IStatusApi>().To<StatusApiController>();
}
}
This will be the Ninject module that you will register/load in the Composition Root.
Now about the Composition Root: in UI MVC projects NinjectWebCommon.cs
You can have a method that is responsible loading your modules as below.
private static void RegisterServices(IKernel kernel)
{
var modules = new List<INinjectModule>
{
new ServiceModule()
//, new FooModule()
//, new BarModule()
};
kernel.Load(modules);
}
And finally your DashboardController in UI MVC:
public class DashboardController : Controller
{
private readonly IHardwareService _hardwareService;
public DashboardController(IHardwareService hardwareService)
{
_hardwareService = hardwareService;
}
}
At this point, your ask for the registered implementation of IHardwareService in the controllers constructor. The DI Container will do the dirty job and pass you the instance that you can work with later in your controller.
A note about the interfaces: I tend to put these into an own assembly, where I just store the interfaces, e.g.: Project.Domain.Interfaces or Project.Infrastructure.Interfaces where each of these assemblies contain only domain or infrastructure interfaces.
References between assemblies:
To put all these together the UI only references the IOC assembly and the interfaces assembly that containts the interfaces you bound in your Ninject Module.
Summarizing all of the above:
Your classes and interfaces alone by theirselves are just pieces what are getting glued together by the DI container.
Hope I cleared it up a bit.
EDIT: as some good advice that #AndreySarafanov pointed out in comments, if you need different implementations of an interface you ask for in the constructor, you can use a Ninject Factory. For more information you can refer to this answer.

How can I access the Ninject kernel from a separate c# project?

I have a WCF application that uses Ninject (along with the NinjectWebCommon file) to take care of my dependency injection needs for the most part (this is done at app start); however, I have a separate project in the same solution that where I would like to use the kernel to resolve some dependencies at run-time. How can I get access to my kernel in this "other" project? Is it even possible?
How can I get access to my kernel in this "other" project?
You shouldn't do this. Only the startup path of your application should reference the container/kernel. This part is called the Composition Root. The kernel shouldn't be referenced outside the Composition Root; that would be an application of the Service Locator anti-pattern, and would cause all sorts of maintainability issues.
The 'trick' here is to define an abstract factory interface in your application. You can implement this factory inside your composition root. This will keep the kernel references only inside the composition root and will therefore not result in the Service Locator anti-pattern.
For instance:
// Defined in a core layer of the application
public interface IItemProcessorFactory {
IItemProcessor GetProcessor(ItemProcessorType type);
}
And inside your composition root (which could be a class or a namespace with multiple classes) you define an implementation:
// A nested type to exaggerate the fact that this is inside your Composition Root
private sealed class NinjectItemProcessorFactory : IItemProcessorFactory {
private readonly Kernel kernel;
public NinjectItemProcessorFactory(Kernel kernel) {
this.kernel = kernel;
}
public IItemProcessor GetProcessor(ItemProcessorType type) {
this.kernel.Get<IItemProcessor>(type.ToString());
}
}
And the factory can be registered as follows:
kernel.Bind<IItemProcessorFactory>().To<NinjectItemProcessorFactory>();

What exactly are the steps to auto inject Func<T>?

I'm trying to set up a service class where I would like to inject a simple Func<OtherService> factory method in its constructor. Type OtherService is a class, which should be self-bound (and is indeed when I inject it without factory).
I've installed the latest Ninject core, and Ninject.Extensions.Factory NuGet packages. When I try to retrieve an instance from Ninject, I get an activation exception about not being able to activate IntPtr.
What am I doing wrong? If I understand correctly the documentation of the Factory extension, this type of Func binding should be automatic, without any configuration. Do I need to register the Factory extension anywhere, or create an explicit binding for OtherService? Any tips would be appreciated.
EDIT:
I have Ninject and Ninject.Extensions.Factory installed in a small separate project, where I do some customizations on Ninject. These are the following:
Kernel.Components.Add<IInjectionHeuristic, CustomInjectionHeuristic>();
Kernel.Components.Add<IActivationStrategy, TransientDisposableActivationStrategy>();
where the two custom handlers are for letting me use my custom [Service] attribute for indicating property injection, and for handling disposing of components which use my own IDisposableEx interface (with Disposed notification) for Ninject cache fine tuning.
public class CustomInjectionHeuristic : NinjectComponent, IInjectionHeuristic, INinjectComponent, IDisposable
{
public bool ShouldInject(MemberInfo member)
{
return member.IsDefined(typeof(ServiceAttribute), true);
}
}
public class TransientDisposableActivationStrategy : ActivationStrategy
{
public override void Activate(IContext context, InstanceReference reference)
{
var scope = context.GetScope();
// care about only transient scoped objects
if (scope != null) return;
if (reference.Instance is Component)
reference.Instance.As<Component>().Disposed +=
(sender, args) => context.Kernel.Components.Get<ICache>().Clear(sender);
if (reference.Instance is IDisposableEx)
reference.Instance.As<IDisposableEx>().Disposed +=
(sender, args) => context.Kernel.Components.Get<ICache>().Clear(sender);
}
}
And I also use the Ninject.Extensions.NamedScope extension for InCallScope() bindings.
Any of these customizations could have effect on proper work of the factory?
EDIT2:
I think I got it. The problem seems to be that I have to reference the Factory extension in my startup project, where I define bindings, and not in the class library where I have my basic Ninject setup. So the key is that I have to reference any binding-related extension in that project where I define the bindings. Anyway, sounds logical...
The problem seems to be that I have to reference the Factory extension in my startup project, where I define bindings, and not in the class library where I have my basic Ninject setup. So the key is that I have to reference any binding-related extension in that project where I define the bindings. Anyway, sounds logical...

Categories