I have couple of class libraries in my project and all are using Ninject IoC container. I wanted to load all the modules in a StandardKernel at one go wherever an INinjectModule is found. So I used:
var kernel = new StandardKernel();
kernel.Load(AppDomain.CurrentDomain.GetAssemblies())
But this doesn't work for some reason. Can anyone help?
Well, this often happens when bindings are declared but other modules are loaded where that module tries to resolve a binding which has not loaded yet. This happens because List<INinjectModule> may not in the right order.
If you think this is the case. Follow this resolution.
The idea is we will have a bootstapper for each assembly, where the bootstrapper will be responsible to load the modules in its logical order.
Let us consider an interface for bootstrapper (this we will use to find the bootstrapper in an assembly)
public interface INinjectModuleBootstrapper
{
IList<INinjectModule> GetModules();
}
Now consider for your DataAccess assembly, implement the INinjectModuleBootstrapper:
public class DataAccessBootstrapper : INinjectModuleBootstrapper
{
public IList<INinjectModule> GetModules()
{
//this is where you will be considering priority of your modules.
return new List<INinjectModule>()
{
new DataObjectModule(),
new RepositoryModule(),
new DbConnectionModule()
};
//RepositoryModule cannot be loaded until DataObjectModule is loaded
//as it is depended on DataObjectModule and DbConnectionModule has
//dependency on RepositoryModule
}
}
This is how you defne the Bootstrapper for all your assembly. Now, from your program startup, we need the StandardKernel where all the modules are loaded. We will write something like this:
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
return BootstrapHelper.LoadNinjectKernel(assemblies);
And our BootstrapperHelper class is:
public static class BootstrapHelper
{
public static StandardKernel LoadNinjectKernel(IEnumerable<Assembly> assemblies)
{
var standardKernel = new StandardKernel();
foreach (var assembly in assemblies)
{
assembly
.GetTypes()
.Where(t =>
t.GetInterfaces()
.Any(i =>
i.Name == typeof(INinjectModuleBootstrapper).Name))
.ToList()
.ForEach(t =>
{
var ninjectModuleBootstrapper =
(INinjectModuleBootstrapper)Activator.CreateInstance(t);
standardKernel.Load(ninjectModuleBootstrapper.GetModules());
});
}
return standardKernel;
}
}
Another thing you should check is if the class that extends NinjectModule is public, otherwise it wont be visible in the Assembly.
I think that is not a good idea to use CurrentDomain.GetAllAssemblies() because not all project assemblies can be loaded on program startup ( some assemblies can be loaded on user actions for example or other events). In this case you will have null-reference exceptions for dependencies.
You can use reflection to find and instantiate the Ninject modules:
BuildManager.GetReferencedAssemblies()
.Cast<Assembly>()
.SelectMany(a => a.DefinedTypes)
.Where(t => typeof(INinjectModule).IsAssignableFrom(t))
.Select(t => (INinjectModule)Activator.CreateInstance(t))
Related
I'm building a web application, where I would like separate concerns, i.e. having abstractions and implementations in different projects.
To achieve this, I've tried to implement a composition root concept, where all implementation must have an instance of ICompositionRootComposer to register services, types etc.
public interface ICompositionRootComposer
{
void Compose(ICompositionConfigurator configurator);
}
In projects that are referred directly in the build hierarchy, implementations of ICompositionRootComposer are called, and services are registered correct in the underlying IoC container.
The problem arises when I'm trying to register services in a project, where I've set up a post build task that copies the built dll to the web project's debug folder:
cp -R $(TargetDir)"assembly and symbol name"* $(SolutionDir)src/"webproject path"/bin/Debug/netcoreapp1.1
I'm loading the assembly with: (Inspiration: How to load assemblies located in a folder in .net core console app)
internal class AssemblyLoader : AssemblyLoadContext
{
private string folderPath;
internal AssemblyLoader(string folderPath)
{
this.folderPath = Path.GetDirectoryName(folderPath);
}
internal Assembly Load(string filePath)
{
FileInfo fileInfo = new FileInfo(filePath);
AssemblyName assemblyName = new AssemblyName(fileInfo.Name.Replace(fileInfo.Extension, string.Empty));
return this.Load(assemblyName);
}
protected override Assembly Load(AssemblyName assemblyName)
{
var dependencyContext = DependencyContext.Default;
var ressource = dependencyContext.CompileLibraries.FirstOrDefault(r => r.Name.Contains(assemblyName.Name));
if(ressource != null)
{
return Assembly.Load(new AssemblyName(ressource.Name));
}
var fileInfo = this.LoadFileInfo(assemblyName.Name);
if(File.Exists(fileInfo.FullName))
{
Assembly assembly = null;
if(this.TryGetAssemblyFromAssemblyName(assemblyName, out assembly))
{
return assembly;
}
return this.LoadFromAssemblyPath(fileInfo.FullName);
}
return Assembly.Load(assemblyName);
}
private FileInfo LoadFileInfo(string assemblyName)
{
string fullPath = Path.Combine(this.folderPath, $"{assemblyName}.dll");
return new FileInfo(fullPath);
}
private bool TryGetAssemblyFromAssemblyName(AssemblyName assemblyName, out Assembly assembly)
{
try
{
assembly = Default.LoadFromAssemblyName(assemblyName);
return true;
}
catch
{
assembly = null;
return false;
}
}
}
With this I'm able to load the assembly and call the projects ICompositionRootComposer implementation.
But the problem is that it doesn't seem to recognize any of my types.
When calling my configurator with
configurator.RegisterTransiantService<IFoo, Foo>();
it should register IFoo and Foo in the IoC.
But when debugging I'm not able to get info of the types, i.e via typeof(Foo) in the debug console in Visual Studio Code.
Necromancing.
You can create a wrapper class for the old Assembly.LoadFile to do that.
This has the added benefit that you can stay backward-compatible with dotnet-none-core by applying search-and-replace changes in old code-bases.
namespace System.Reflection
{
public class Assembly2
{
public static System.Reflection.Assembly LoadFile(string path)
{
System.Reflection.Assembly assembly = null;
#if NET_CORE
// Requires nuget - System.Runtime.Loader
assembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
#else
assembly = System.Reflection. Assembly.LoadFile(path);
#endif
return assembly;
}
}
}
You'll need to add System.Runtime.Loader via NuGet.
I found a solution to my problem.
It turned out, that I had the property PreserveCompilationContext set to true, and that's why the debugger wouldn't register my manually copied assembly.
When I removed the property from the web project csproj file, everything worked.
Are you aware that ASP.NET Core has it's own, built-in Dependency Injection mechanism? It can be easily switched to other IoC container for your needs, I don't think that you need to reinvent it.
What you need to do here is use a reflection to make a generic method and call after that, something like this:
public void ConfigureServices(IServiceCollection services)
{
var myAssembly = LoadAssembly();
// find first interface
var firstInterfaceType = myAssembly.DefinedTypes.FirstOrDefault(t => t.IsInterface).GetType();
// find it's implementation
var firstInterfaceImplementationType = myAssembly.DefinedTypes.Where(t => t.ImplementedInterfaces.Contains(firstInterfaceType)).GetType();
// get general method info
MethodInfo method = typeof(IServiceCollection).GetMethod("AddTransient");
// provide types to generic method
MethodInfo generic = method.MakeGenericMethod(firstInterfaceType, firstInterfaceImplementationType);
// register your types
generic.Invoke(services, null);
}
I have several (eventually 100+) small DLL projects all based on MediatR. This means that the interfaces in use are just the IMediatR interfaces (IRequest<TResult>, IRequestHandler<IRequest<TResult>, TResult>). Since a lot of these do not have a UI and are called via orchestration from another DLL I was thinking I could create an Autofac Container project (DLL), register all the micro-services, then resolve what I need at runtime in another app that consumes my container. So far, so good.
Where I am running into problems is the registration of each and every CQRS handler. Right now, while everything is small in scope, they are being defined inline like this:
namespace My.Core.Container
{
public class CoreDependencies
{
#region Properties
public IMediator Mediator { get; private set; }
public IContainer Container { get; private set; }
private static ContainerBuilder _builder;
#endregion
#region Constructor
public CoreDependencies()
{
_builder = new ContainerBuilder();
// register MediatR types...
_builder.RegisterSource(new ContravariantRegistrationSource());
_builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly).AsImplementedInterfaces();
_builder.Register<SingleInstanceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t => c.Resolve(t);
});
_builder.Register<MultiInstanceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t => (IEnumerable<object>)c.Resolve(typeof(IEnumerable<>).MakeGenericType(t));
});
// ################################################## //
// register EntityTree micro services...
_builder.RegisterAssemblyTypes(typeof(My.Core.EntityTree.GetChildren).GetTypeInfo().Assembly).AsImplementedInterfaces();
_builder.RegisterAssemblyTypes(typeof(My.Core.EntityTree.DeleteEntity).GetTypeInfo().Assembly).AsImplementedInterfaces();
_builder.RegisterAssemblyTypes(typeof(My.Core.EntityTree.AddEntity).GetTypeInfo().Assembly).AsImplementedInterfaces();
// register next micro services...
// register next micro services...
// ad naseum...
// ################################################## //
// Now build it...
Container = _builder.Build();
Mediator = Container.Resolve<IMediator>();
}
#endregion
}
}
So, my question is: How do I correctly do this registration? Right now, 2 or 3 "cores" (micro-services) but next month, 20, next year, 200, etc...
TIA
Instead of manually register assembly by assembly, you could do Assembly Scanning to retrieve all assemblies you need, and then, register them in simple loop (code not tested):
var assemblies = /* get assemblies based on project type (web/desktop) */;
foreach(var assembly in assemblies)
{
container.RegisterAssemblyTypes(assembly).AsImplementedInterfaces();
}
Btw, you should not expose your container as public property, unless you have very strong arguments to do so (service locator anti-pattern). Also SingleInstanceFactory and MultiInstanceFactory look pretty suspicious for me...
I'm trying to automate registration of AutoMapper profiles with Ninject. For this I need to instantiate objects of types that inherit from AutoMapper.Profile, for example:
public class WordMapping : Profile
{
protected override void Configure()
{
Mapper.CreateMap<Datacontext.Entities.Word, Domain.Entities.Word>();
Mapper.CreateMap<Domain.Entities.Word, Datacontext.Entities.Word>();
}
}
First I tried to do this only by means of reflection and it worked like a charm:
var profileType = typeof(AutoMapper.Profile);
var profiles = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(t => profileType.IsAssignableFrom(t) && t.GetConstructor(Type.EmptyTypes) != null)
.Select(Activator.CreateInstance)
.Cast<Profile>();
foreach (var profile in profiles)
{
Mapper.AddProfile(profile);
}
But when I try to do the same thing with Ninject:
public class MappingModules : NinjectModule
{
public override void Load()
{
Kernel.Bind(scanner =>
{
scanner.FromAssembliesMatching("*")
.SelectAllClasses()
.InheritedFrom<AutoMapper.Profile>();
});
var profiles = Kernel.GetAll<Profile>();
foreach (var profile in profiles)
{
Mapper.AddProfile(profile);
}
}
}
I get the error:
SetUp : Ninject.ActivationException : Error activating Profile using
implicit self-binding of Profile No constructor was available to
create an instance of the implementation type.
Activation path: 1) Request for Profile
Suggestions: 1) Ensure that the implementation type has a public
constructor. 2) If you have implemented the Singleton pattern, use a
binding with InSingletonScope() instead.
I suspect that this happens because of Ninject trying to instantiate Mapper.Profile itself but it doesn't have a public constructor. But when I change .InheritedFrom<AutoMapper.Profile>() to .Where(p => p.IsAssignableFrom(typeof(AutoMapper.Profile)) && p != typeof(AutoMapper.Profile)) it doesn't help either.
It's worth to mention that Ninject's composition root, Ninject' modules and classes inherited from AutoMapper.Profile are located in 3 different assemblies.
Help me please to figure out what I am missing.
Your convention based binding is missing.... the binding!
Add a .BindBase() to the end of the conditional binding.
Kernel.Bind(scanner =>
{
scanner.FromAssembliesMatching("*")
.SelectAllClasses()
.InheritedFrom<AutoMapper.Profile>()
.BindBase();
});
BindBase() will bind the class to it's immediate base class, which in your example (WordMapping : Profile) is Profile. If your inheritance hierarchy would be WordMapping : MyMapping : Profile it would bind to MyMapping (which would not be okay for you).
So if you need it to be bound to a different type, have a look at https://github.com/ninject/ninject.extensions.conventions/wiki/Projecting-Services-to-Bind
When creating a StructureMap container, I typically do something like this:
var container = new Container(registry => {
registry.AddRegistry<MyRegistry>();
});
Where
public class MyRegistry : Registry {
public MyRegistry() {
Scan(x => {
x.Assembly("My.Assembly.Name");
x.RegisterConcreteTypesAgainstTheFirstInterface();
}
}
}
However, this has resulted in quite many registry.AddRegistry lines in a bootstrapper file, which is then copied into many projects. I would like to be able to call a method taking a constructed container and adding a registry to it, so I can modularize libraries.
I came up with this:
public static void Setup(ref Container container) {
container.PluginGraph.ImportRegistry(typeof(MyRegistry));
}
This works, in that the registry is properly added to the container (seen by calling container.WhatDoIHave() before and after), but it seems the actual mappings are not done - ie the interface IFoo is not registered to concrete class Foo that both are defined in My.Assembly.Name.
What is the difference in doing ImportRegistry and AddRegistry? Can my Setup method be fixed?
Did you try Container.Configure()? It exists to configure a container after it has already been initialized.
I have an application which might needs to connect to multiple databases. But each module will only connect to one db. So I though it might make sense to isolate the db into each module so each module will get its own db auto resolved and I don't need to bother with named registration. But to my astonishment, it seems that Autofac's module is more a code module than a logical module (am I wrong here?): IA
[Test]
public void test_module_can_act_as_scope_container()
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterModule(new Module1());
IContainer c = builder.Build();
var o = c.ResolveNamed<CB>("One");
Assert.That(o.A.Name, Is.EqualTo("One"));
builder = new ContainerBuilder();
builder.RegisterModule(new Module1());
builder.RegisterModule(new Module2());
c = builder.Build();
var t = c.ResolveNamed<CB>("One");
Assert.That(t.A.Name, Is.EqualTo("Two"));
}
And the interfaces/Modules used:
public interface IA
{
string Name { get; set; }
}
public class CA : IA
{
public string Name { get; set; }
}
public class CB
{
public CB(IA a)
{
A = a;
}
public IA A { get; private set; }
}
public class Module1 : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register(c => new CA() { Name = "One" }).As<IA>();
builder.RegisterType<CB>().Named("One", typeof(CB));
}
}
public class Module2 : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register(c => new CA() { Name = "Two" }).As<IA>();
builder.RegisterType<CB>().Named("Two", typeof(CB));
}
}
Yes, you're kind of correct.
Modules serve only for splitting configuration into somewhat independent parts. They do not scope configuration in any way. Having modules is actually the same as if you merged all modules' Load methods' code into a single configuration method and then built the container.
In your case your Module2 actually overrides the registration for IA interface from Module1.
I've also been interested in finding a solution to the problem. I've come to the following approach:
Keyed service
var key = new object();
builder.Register(c => new CA() { Name = "Two" }).Keyed<IA>(key);
builder.RegisterType<CB>().Named("Two", typeof(CB))
.WithParameter(new ResolvedParameter(
(pi, ctx) => pi.Type == typeof(IA),
(pi, ctx) => ctx.ResolveKeyed<IA>(key)
));
Pros:
You can control which IA instances will be injected in each module.
Contras:
It's quite a lot of code
It does not make IA component 'internal' to the module - other modules can still resolve it using simple Resolve<IA>. Modules aren't isolated.
Hope this helps
UPDATE
In some cases it may be easier, and frankly more correct from design point of view, to make it this way:
Delegate registration
builder.Register(ctx => new CB(new CA { Name = "Two" }))
.Named("Two", typeof(CB));
Pros:
You don't expose your module-specific CA to other modules
Contras:
If CA and CB have complex dependencies and a lot of constructor parameters, you'll end up with mess of constructing code
If you need to use CA in several places inside a module, you'll have to find a way to avoid copy-pasting
Nested Container instances
And yet another option is having an independent Container inside each module. This way all modules will be able to have their private container configurations. However, AFAIK, Autofac doesn't provide any built-in means to somehow link several Container instances. Although I suppose implementing this should not be very difficult.
You could use nestet lifetime scopes. These form a hierarchy in which subscopes can resolve services registered in superscopes. Additionally, you can register unique services in each subscope, like this:
var cb = new ContainerBuilder();
cb.RegisterModule<CommonModule>();
var master = cb.Build();
var subscope1 = master.BeginLifetimeScope(cb2 => cb2.RegisterModule<Module1>());
var subscope2 = master.BeginLifetimeScope(cb2 => cb2.RegisterModule<Module2>());
With this setup, services in Module1 will only be available to instances resolved from subscope1.