Dependency Registrar for other assembly - c#

I have a simple question about dependecy registration.
I'm developing a brand new web application that use Engine Context paradigm with Autofac container. For any library on the solution I have one class implementing IDependencyRegistrar that implement a common Register method, due to add one the container some specific implementation of some interfaces and components.
In this way, a base Core library (running at application startup) provide a RegisterDependencies method that lookup on every Executing Assembly to discover all the DDL's used by the application and registering them on Autofac Container.
The code that provide this behavior is:
builder = new ContainerBuilder();
var drTypes = typeFinder.FindClassesOfType<IDependencyRegistrar>();
var drInstances = new List<IDependencyRegistrar>();
foreach (var drType in drTypes)
drInstances.Add((IDependencyRegistrar) Activator.CreateInstance(drType));
//sort
drInstances = drInstances.AsQueryable().OrderBy(t => t.Order).ToList();
foreach (var dependencyRegistrar in drInstances)
dependencyRegistrar.Register(builder, typeFinder, config);
builder.Update(container);
Where the FindClassOfType<IDependencyRegistrar> works thanks to a Method implementation like that:
public virtual IList<Assembly> GetAssemblies()
{
var addedAssemblyNames = new List<string>();
var assemblies = new List<Assembly>();
if (LoadAppDomainAssemblies)
AddAssembliesInAppDomain(addedAssemblyNames, assemblies);
AddConfiguredAssemblies(addedAssemblyNames, assemblies);
return assemblies;
}
And, AddAssemblyInAppDomain is:
private void AddAssembliesInAppDomain(List<string> addedAssemblyNames, List<Assembly> assemblies)
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (Matches(assembly.FullName))
{
if (!addedAssemblyNames.Contains(assembly.FullName))
{
assemblies.Add(assembly);
addedAssemblyNames.Add(assembly.FullName);
}
}
}
}
The problem is: when I end up on adding in mysolution the MVC project (the front-end), I've referenced on it only direct accessing library (service layer and some infrastructure components) but no DataLayer components and some other DLL. Due to the fact that MVC not referencing directly some libraries of deep layers, my Engine Context doesn't see the others sub-components and not registering them on the Autofac container, causing a
'no registered services'
exception when execution make explicit request on them.
The whole system just works if I add reference to any library from the MVC project but, for layered architectured application, this is not a best practice: my MVC need to know nothing about DataLayer or others low-layered services.
However, in this way, no ExecutingAssembly are discovered, so, not dependency are registered anymore.
Wich is the best approch to resolve this situation without referencing all assemblies directly from main MVC project?

What you are trying to do is described in Autofac documentation as Assembly Scanning, take a look here. Basically, to get all assemblies in IIS-hosted application you need this piece of code:
var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>();
EDIT:
Ok, so I understand the situation is like this:
Project Web is a MVC web app.
Project Model is a class library where you have your contracts (interfaces) defined, e.g. for DAL, but also for Web.
Project DAL contains some implementations of contracts from Model.
There might be some additional class libraries, but they all uses Model for contracts.
So to sum up - all projects have reference to Model, but they have no references to each other.
I think for every library (except Model) you should create a module. To do so, create a class implementing Module type from Autofac library and override Load method - put all your module registration in there. Then, in Web app start you should load all assemblies and register their modules. But, as you mentioned, assemblies other than Web are not present in bin directory; you should copy them there "manually", for example in Post-Build action (Project Properties -> Build Events -> Post-Build action). The following command should do the work:
xcopy /Y "$(TargetDir)*.dll" "$(ProjectDir)..\{Your Web App}\bin"
Also, in your solution properties you should set, that Web project "depends" on all other projects. It would assure all other libraries would be build before Web. It does not add any reference between these assemblies.
Then, during application startup, you should search for you assemblies in bin folder and register each assembly module, like this:
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterControllers(typeof(MvcApplication).Assembly);
var libFolder = new DirectoryInfo(HostingEnvironment.MapPath("~/bin"));
var libFiles = libFolder.GetFiles("*.dll", SearchOption.AllDirectories);
foreach (var lib in libFiles)
{
var asm = Assembly.LoadFrom(lib.FullName);
containerBuilder.RegisterAssemblyModules(asm);
}
var container = containerBuilder.Build();
You might want to add some filter to libFolder.GetFiles() to retreive only your assemblies, not all from bin.
If your other assemblies contains Mvc Controllers, you should take a look how to manage the situation here (see Initializer class). Basically, in pre-start of application you would need to add assemblies to BuildManager. Otherwise, the code above should work just fine.

If you are working on a non-web project then my answer might help?
To your Ioc class add a method i.e:
public static void SetIocForTesting(bool forUnitTesting)
{
_testContext = forUnitTesting;
}
Sample container set-up code, delegate out the responsibility of getting the assemblies to load into the builder. i.e GetModules():
public static IContainer Container
{
get
{
if (_container != null)
{
return _container;
}
var builder = new ContainerBuilder();
foreach (var lib in GetModules())
{
builder.RegisterAssemblyModules(lib);
}
_container = builder.Build();
return _container;
}
}
When scanning for Assemblies, switch on the testContext variable:
private static IEnumerable<Assembly> GetModules()
{
if (_testContext)
{
return AppDomain.CurrentDomain.GetAssemblies();
}
var currentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (currentPath == null)
{
throw new NullReferenceException("Unable to build the container because currentPath variable is null.");
}
// XXXX = assign a wild card
var libFolder = new DirectoryInfo(currentPath);
var libFiles = libFolder.GetFiles("XXXX.*.dll", SearchOption.TopDirectoryOnly);
return libFiles.Select(lib => Assembly.LoadFrom(lib.FullName)).ToList();
}
When unit testing your IoC provider and a registration:
protected virtual void GivenThat()
{
IocProvider.SetIocForTesting(true);
}
.. you have a method that switches the IoC to ensure it works correctly with all assemblies referenced and loaded by your test project. The above method lives inside an abstract base class I use for BDD style unit testing.
Your test project usually ends up referencing a lot of assemblies which means resolving services have a higher success rate.
Finally, for non UnitTesting code add a static constructor:
static IocProvider()
{
_testContext = false;
}
This will ensure a default work flow for production code.
Feel free to play with the above format to suit your needs; I hope it helps someone in the way the above question and answer helped me.

Related

Autofac in a Multi-Project Solution

I'm using Autofac as a dependency injection system in a C# solution that spans several class libraries and several executables. I'm using modules to configure Autofac, but that still leaves me with the issue of building the DI container, which varies depending upon which executable I'm composing it for.
I tried using Autofac's RegisterAssemblyModules, but you have to give it a list of assemblies to scan, and until some Type in a class library assembly is used, the assembly isn't loaded, and hence not available to scan.
Some people recommend loading every assembly in the bin directory which might have an Autofac module definition in it. But that seems to pose the risk of an undesired assembly getting slipped into action.
So what I came up with is this static class, which is defined in a common class library:
public static class Container
{
private static IContainer _instance;
private static Dictionary<string, Assembly> _moduleAssemblies = new Dictionary<string, Assembly>();
public static void RegisterAutofacModuleAssembly<T>()
where T : class
{
var assembly = typeof(T).Assembly;
if( !_moduleAssemblies.ContainsKey( assembly.FullName ) )
_moduleAssemblies.Add( assembly.FullName, assembly );
}
public static IContainer Instance
{
get
{
if( _instance == null )
{
var builder = new ContainerBuilder();
builder.RegisterAssemblyModules( _moduleAssemblies.Select( ma => ma.Value ).ToArray() );
_instance = builder.Build();
}
return _instance;
}
}
}
You use this by including lines like this in the startup code of an application:
public static void Main(string[] args)
{
AutoFacRegistrar.Container.RegisterAutofacModuleAssembly<ScannerApp>();
AutoFacRegistrar.Container.RegisterAutofacModuleAssembly<AppConfiguration>();
Is this a reasonable solution? If there's a better, more flexible one I'd be interested in learning about it.
Issue in IOptions<> Binding System
In implementing #Nightowl888's suggestion, I ran into a problem with the Microsoft configuration IOptions<> system. Here's how I'm trying to configure Autofac to resolve AppConfiguration objects:
protected override void Load( ContainerBuilder builder )
{
base.Load( builder );
var config = new ConfigurationBuilder()
.AddJsonFile( AppConfiguration.WebJobsConfigFile, false )
.AddUserSecrets<ConfigurationModule>()
.AddEnvironmentVariables()
.Build();
builder.Register<AppConfiguration>( ( c, p ) =>
{
var retVal = new AppConfiguration( c.Resolve<ILogger>() );
config.Bind( retVal );
return retVal;
} )
.SingleInstance();
}
The problem occurs in the call to Bind(). As it traverses and parses the configuration information, it expects to create various objects thru parameterless constructors...which makes it difficult to use constructor injectcion.
If I can't use constructor injection, I need to be able to resolve against the DI container within constructor code. I don't see how I can define a library assembly which doesn't hardwire in a particular DI container's resolution semantics.
Thoughts? Other than "abandon the IOptions<> system", which I've considered, but it provides a number of benefits I'd like to maintain.
Every executable application should have its own unique DI configuration. Libraries and frameworks should be built to be DI-friendly, but not actually reference any DI container.
A composition root is an application's configuration. Sharing it between applications is similar to sharing a .config file between applications - that is, it is not usually done. See Composition Root Reuse.
If you are going to use autofac modules, they should be a part of the application that uses them, not included in the assembly with the components that are being composed. While it may seem like you are gaining something by not having to repeat the configuration code in every application, the main issue with doing this is that it means your application has lost one of the main benefits of DI - that is, it cannot provide an alternative implementation of any given component. The whole point of making a library loosely-coupled is that it allows the decision of how the application will be coupled together to ultimately be made by the application that hosts the components.
Pet peeve: Also note that how many projects or solutions you have has absolutely nothing to do with the runtime behavior of an application (such as how it is composed). Projects and solutions are a way to organize code before it is compiled - once the code is compiled, there is no concept of "project" or "solution", all you are left with are "assemblies" that may depend on other "assemblies". For each application you end up with an executable assembly and 0 or more dependent assemblies. The composition root should only exist in the executable assembly.

Registration of IoC containers from several assemblies

I try to understand what is best practice for registration of the objects into ioc container from different projects of one solution.
I have a solution with 4 projects and I saw a solution to create installers in each of the projects and then in one place call somtehing like this:
_container = new WindsorContainer();
var assemblyNames = new[] {"Dal", "Utils" };
_container.Install(assemblyNames.Select(
x => (IWindsorInstaller)new AssemblyInstaller(Assembly.Load(x),
new InstallerFactory())).ToArray());
But also I saw a solution that in each project, there is a creation of container, and inside there is a registration of the objects that are relevant to this specific project.
My question is: what is the best practice for this situation?
Every executable project should have a container of its own as they are capable of running independently of other executable projects. Library project usually provide dependencies that are consumed in the executable project and as such it is the executable project's responsibility to register those dependencies in its container if it wants to make use of them with the library not having a container of its own.
Having multiple containers could cause a variety of issues for example, if a class is registered as a singleton (one reference shared among all consumers of the dependency) having multiple containers would result in multiple instance of the class being created (one in each container)
It could also cause issue if there are cross-project dependencies as the container would not be able to resolve a dependency registered in another container.
Usually I create one project which contains most of my shared resources such as models, libraries and also IoC configuration which I use in the other projects. By configuring the IoC container from this project I'm saving myself some copy pasting. With this I keep the possibility to override the configuration from the project in which you're using this configuration.
In the end it's all about maintainability. If you use the same dependencies throughout all of your projects it'd be a pain in the arse to configure it time after time for each individual project.
We have an example here : A web solution which consists of many projects : One Core project and many other which reference the core. All of them end up in different folders (or copied after build to the core/bin if you like). At start time we load them dynamically and then load the IWindsorInstallers from them. We load dlls like that :
public static void LoadMoules()
{
string basePath = AppDomain.CurrentDomain.BaseDirectory;
string binPath = Path.Combine(basePath, "bin");
DirectoryInfo binFolder = new DirectoryInfo(binPath);
IEnumerable<FileInfo> binFiles = binFolder.EnumerateFiles("*.dll", SearchOption.AllDirectories);
Assembly[] domainAssemblies = AppDomain.CurrentDomain.GetAssemblies();
try
{
foreach (var binFile in binFiles)
{
AssemblyName assemblyName = AssemblyName.GetAssemblyName(binFile.FullName);
if (domainAssemblies.All(x => x.FullName != assemblyName.FullName))
{
Assembly.Load(assemblyName.FullName);
Debug.WriteLine($"Loading {assemblyName.FullName}");
}
}
}
catch (Exception exception)
{
DynamicModuleLoaderEventSource.Log.FailedToLoadAssembly(exception.Message + exception.StackTrace);
} }
Then we get the registrations :
try
{
foreach(Assembly moduleAssembly in installationModules)
{
// satellite proejct assemblies
Debug.WriteLine(moduleAssembly.FullName);
ContainerRegisterEventSource.Log.InstallAssembly(moduleAssembly.FullName);
container.Install(FromAssembly.Instance(moduleAssembly));
}
container.Install(FromAssembly.This()); // the core assembly
}
catch(Exception exception)
{
ContainerRegisterEventSource.Log.Exception(exception.Message + exception.StackTrace);
Trace.WriteLine("SetupContainerForMigrations error : " + exception.Message + Environment.NewLine + exception.StackTrace);
}
Doing it this way you will end up with one container with all the dependencies.

Dependency Injection in ASP.NET Core

In Autofac you can register your dependencies with RegisterAssemblyTypes
so you will be able todo something like this, is there a way to do the somthing similar in the build in DI for .net Core
builder.RegisterAssemblyTypes(Assembly.Load("SomeProject.Data"))
.Where(t => t.Name.EndsWith("Repository"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
This is what i am trying to register
LeadService.cs
public class LeadService : ILeadService
{
private readonly ILeadTransDetailRepository _leadTransDetailRepository;
public LeadService(ILeadTransDetailRepository leadTransDetailRepository)
{
_leadTransDetailRepository = leadTransDetailRepository;
}
}
LeadTransDetailRepository.cs
public class LeadTransDetailRepository : RepositoryBase<LeadTransDetail>,
ILeadTransDetailRepository
{
public LeadTransDetailRepository(IDatabaseFactory databaseFactory)
: base(databaseFactory) { }
}
public interface ILeadTransDetailRepository : IRepository<LeadTransDetail> { }
This is how i am trying to Regisyer then, but i cant figure out how to register the repositories
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddTransient<ILeadService, LeadService>();
//not sure how to register the repositories
services.Add(new ServiceDescriptor(typeof(ILeadTransDetailRepository),
typeof(IRepository<>), ServiceLifetime.Transient));
services.Add(new ServiceDescriptor(typeof(IDatabaseFactory),
typeof(DatabaseFactory), ServiceLifetime.Transient));
services.AddTransient<DbContext>(_ => new DataContext(
this.Configuration["Data:DefaultConnection:ConnectionString"]));
}
There is no out of the box way to do it with ASP.NET Core Dependency Injection/IoC container, but it's "by design".
ASP.NET IoC Container/DI is meant to be an easy way to add DI functionality and works as a base for other IoC Container frameworks to be built into ASP.NET Core apps.
That being said it supports simple scenarios (registrations, trying to use the first constructor with most parameters that fulfills the dependencies and scoped dependencies), but it lacks advanced scenarios like auto-registration or decorator support.
For this features you'll have to use 3rd party libraries and/or 3rd party IoC container (AutoFac, StructureMap etc.). They can still be plugged into the IServiceCollection your your previous registrations will still work, but you get additional features on top of it.
I think you can register all services by manual scanning. And then register them to Service collection. Here is my example code (.Net core 2.0)
public static void ResolveAllTypes(this IServiceCollection services, string solutionPrefix, params string[] projectSuffixes)
{
//solutionPrefix is my Solution name, to separate with another assemblies of Microsoft,...
//projectSuffixes is my project what i want to scan and register
//Note: To use this code u must reference your project to "projectSuffixes" projects.
var allAssemblies = new List<Assembly>();
var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
foreach (var dll in Directory.GetFiles(path, "*.dll"))
allAssemblies.Add(Assembly.LoadFile(dll));
var types = new List<Type>();
foreach (var assembly in allAssemblies)
{
if (assembly.FullName.StartsWith(solutionPrefix))
{
foreach (var assemblyDefinedType in assembly.DefinedTypes)
{
if (projectSuffixes.Any(x => assemblyDefinedType.Name.EndsWith(x)))
{
types.Add(assemblyDefinedType.AsType());
}
}
}
}
var implementTypes = types.Where(x => x.IsClass).ToList();
foreach (var implementType in implementTypes)
{
//I default "AService" always implement "IAService", You can custom it
var interfaceType = implementType.GetInterface("I" + implementType.Name);
if (interfaceType != null)
{
services.Add(new ServiceDescriptor(interfaceType, implementType,
ServiceLifetime.Scoped));
}
}
}
I also wanted to give the built-in a try, got annoyed with the lack of auto-register and built an open-source project for that, take a look:
https://github.com/SharpTools/SharpDiAutoRegister
Just add the nuget package SharpDiAutoRegister and add your conventions in the ConfigureServices method:
services.ForInterfacesMatching("^I[a-zA-z]+Repository$")
.OfAssemblies(Assembly.GetExecutingAssembly())
.AddSingletons();
services.ForInterfacesMatching("^IRepository")
.OfAssemblies(Assembly.GetExecutingAssembly())
.AddTransients();
//and so on...
ASP.NET Core is designed from the ground up to support and leverage dependency injection.
ASP.NET Core applications can leverage built-in framework services by having them injected into methods in the Startup class, and application services can be configured for injection as well.
The default services container provided by ASP.NET Core provides a minimal feature set and is
not intended to replace other containers.
Source: https://docs.asp.net/en/latest/fundamentals/dependency-injection.html
The default ASP.NET container is
simple and does not offer the robust configuration and performance
options that are available with other containers.
Fortunately, you can swap out the default container with one of the community-created full featured ones that is already available as a NuGet package.
Autofac (http://autofac.org/ ) is one that is already available for ASP.NET Core, and you can add it to your project by referencing both the
Autofac and
Autofac.Extensions.DependencyInjection packages.
Source: https://blogs.msdn.microsoft.com/webdev/2016/03/28/dependency-injection-in-asp-net-core/

Ninject to load different implementations

I have a web service (let's call it WebSite) that uses an Interface (IDataService). the webservice project implements a "fake" service with hardcoded objects (DesignDataService) that I use to develop the websit while I wait for my colleagues to build the real implementation (BreadDataService).
My NinjectWebCommon is currently like this:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IDataService>().To<DesignDataService>();
}
What I want is to be able to provide my colleague a way to test the BreadDataService on my WebService, while I can go on using DesignDataService. I can't use the BreadDataService on my machine because it requires some assemblies that I don't have (+ the database).
So, what is the approach here? The current dependency tree is like that:
ServiceCore (defines IDataService)
WebSite uses ServiceCore
BreadDataService uses ServiceCore
I don't want to reference the BreadDataService project inside the WebSite Project, I was maybe thinking about a folder in WebSite where they can put the BreadDataService dll and ninject takes it depending on some configuration in web.config.
Hints?
Something like this will do the trick
load external assemblies
search for an implementation
default to your design time version if none are found
Here's the basic code
IEnumerable<Assembly> assemblies = this.LoadAssemblies(#"C:\Temp");
Type implementation = FindImplementation(assemblies, typeof(IDataService));
IKernel kernel = new StandardKernel();
kernel.Bind<IDataService>().To(implementation ?? typeof(DesignDataService));
This method will load external assemblies (such as plugins) from a specific folder
private IEnumerable<Assembly> LoadAssemblies(string folder)
{
IEnumerable<string> dlls =
from file in new DirectoryInfo(folder).GetFiles()
where file.Extension == ".dll"
select file.FullName;
IList<Assembly> assemblies = new List<Assembly>();
foreach (string dll in dlls)
{
try
{
assemblies.Add(Assembly.LoadFile(dll));
}
catch
{
}
}
return assemblies;
}
And this method will search a set of assemblies for an implementation. Please note that I have specifically used SingleOrDefault() so that this will fail if there is more than one implementation.
private Type FindImplementation(
IEnumerable<Assembly> assemblies,
Type serviceType)
{
var implementationType = (
from dll in assemblies
from type in dll.GetExportedTypes()
where serviceType.IsAssignableFrom(type)
where !type.IsAbstract
where !type.IsGenericTypeDefinition
select type)
.SingleOrDefault();
return implementationType;
}
I used qujck approach to build these extension methods.
The main difference is that it relies on Ninject.Extensions.Conventions' FromAssembliesInPath method

Instantiating a class dynamically c#

I am working on developing a plug and play framework in ASP.Net MVC whereby I can define modules as separate projects from the Main project. So, a developer can create as many modules as they want.
What I need is that to be able to update settings of any of such modules. For that, in the main project, I defined a base class for some common settings plus each module has its own custom settings. When there is any edit on a module, I have to instantiate instance of that module in the main project. But, main project has no knowledge of any modules.
How do I achieve this?
Thanks!
You can use dependency injection and inject those modules to your application at composition root. As per configuration you can use code or xml (configuration file). You can do auto wiring, late binding etc depending on what you really need.
You can also have initializers at each module so whenever you register a module, it should initialize your registered modules and inject dependencies etc.
Depending on your need, you would have to create a solution that relies on interfaces.
Essentially, the application exposes an API dll with an interface called IModule. IModule has one method called Run(). Your main application will load up the module's assembly, look for something that implements IModule, makes one of those objects and calls Run() on it.
Here is an old article describing how to host a sandbox to run modules inside.
http://msdn.microsoft.com/en-us/magazine/cc163701.aspx
namespace MyApplication.Api
{
public interface IModule
{
void Run();
}
}
The developer would create something like this
public class MyObject : MarshalByRefObject, IModule
{
public void Run()
{
// do something here
}
}
The application will load it up with some kind of Reflection.
public void LoadModule()
{
var asm = System.Reflection.Assembly.Load(/* Get the developer module name from somewhere*/);
var types = asm.GetExportedTypes();
foreach(var t in types)
{
foreach(var i = t.GetInterfaces())
{
if(i == typeof(IModule))
{
var iModule = System.Activator.CreateInstance(t);
iModule.Run();
}
}
}
}
It would be best if you run the code in another appDomain, but it adds a lot of complexity.
public void LoadModuleInAppDomain()
{
// Spin up a new AppDomain
// Load the assembly into the app domain
// Get the object
// Call the Run Method
}

Categories