Autofac with WebApi & Business Layer - c#

I am very new to AutoFac and am trying to use it for my new project with WebApi and Business Layer with contracts and their respective implementations.
I have written the IocConfiguration for webapi and invoke from global.asax.
However, for my Business Logic how do I setup all my contracts and implementations with autofac?
I did go through some tutorials online but however I could not find anything helpful, If someone has a sample app, links that really helps.
Edit:
AutoMapper profile.
public class CustomProfile : Profile
{
protected override void Configure()
{
CreateMap<MyViewModel, MyModel>()
.ForMember(d => d.Id, s => s.MapFrom(src => src.Id));
}
}
Edit:
After few long hours spent on this I figured out how to setup AutoMapper 4.2.1 with AutoFac. Apparently I was using ConfigurationStore in AutoMapper 3.3.0 but I upgraded to 4.2.1 the profile registration changed a little bit. Below is what worked for me.
public class AutoMapperModule : Module
{
protected override void Load(ContainerBuilder builder)
{
Mapper.Initialize(cfg =>
{
cfg.AddProfile<MyProfile1>();
cfg.AddProfile<MyProfile2>();
});
base.Load(builder);
}
}

If you use constructor injection (and it`s really a good idea).
First you need is add to add reference to Autofac.WebApi2 assembly via Nuget. Lets think that your controllers are in the different Assembly that the host (Service.dll or somethink like this) then you
Services
Project with all our controllers:
public class DependenyInitializer
{
public static readonly DependenyInitializer Instance = new DependenyInitializer();
private DependenyInitializer()
{
var builder = new ContainerBuilder();
builder.RegisterModule<BusinessLayerModule>(); // register all dependencies that has been set up in that module
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
this.Container = builder.Build();
}
public IContainer Container { get; }
}
Buisness Layer
you`ll have to create a module
using System.Reflection;
using Autofac;
using DataAccessLayer;
using Module = Autofac.Module;
public class BusinessLayerModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces(); // that links all clases with the implemented interfaces (it they mapped 1:1 to each other)
}
Hosting (WebApiConfig.cs in Register(HttpConfiguration config))
var container = DependenyInitializer.Instance.Container;
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
Main compexity here is knowing that you need Autofac.WebApi2 and it`s RegisterApiControllers. Try that.

Related

Autofac automate assembly scanning with Interfaces - .net core 3.0 - Register according to Lifetime

I realize this question is asked a lot. But I cannot figure out the solution to this problem.
The thing i am trying to resolve is create three seperate interfaces (Singleton Scoped, InstancePerRequest).
And register all the services under their implemented interface, without having to manually add them to DI container.
Autofac Scanning Assemblies for certain class type
Autofac register assembly types
Autofac assembly scanning - .NET Core
Before you say duplicate hear me out.
I have provided a solution to the question asked. But i would like to search the whole solution and not be restricted to a project. I have addded my autofac module inside the Services project so it registers what exists inside services projects. Please see answer below for a better understanding.
I have tried to implement a multi project scanning see code below. It does not work.
Here is my Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Here is my Configure container in my Startup Class. I have left out all others since this is just a new ASP.NET Core Project.
public void ConfigureContainer(ContainerBuilder builder)
{
//builder.RegisterType<EpisodeServices>().As<IEpisodeService>().InstancePerLifetimeScope(); This works
var executingDirectory = AppDomain.CurrentDomain.BaseDirectory;
//The sth is my Solution's header for example Sth.Core, Sth.Models, Sth.Services all are childs to the Sth Solution
string[] files = Directory.GetFiles(executingDirectory, "Sth.*.dll", SearchOption.AllDirectories);
var listOfAssemblies = new List<Assembly>();
foreach (var file in files)
listOfAssemblies.Add(Assembly.LoadFile(file));
builder
.RegisterAssemblyTypes(listOfAssemblies.ToArray())
.Where(t => t.GetInterfaces().Any(i => i.IsAssignableFrom(typeof(ISthScopedService))))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
}
So far so good.
Here is an example Service so you can fully see my implementation.
public class EpisodeServices : IEpisodeService
{
public IList<Episode> GetEpisodes()
{
return new List<Episode>
{
new Episode { Id = 1, Name = "Some Name", Description = "Some Description" }
};
}
}
And here is the interface:
public interface IEpisodeService : ISthScopedService
{
IList<Episode> GetEpisodes();
}
Here is the injection to the controller
public class EpisodeController : Controller
{
private readonly IEpisodeService _episodeService;
public EpisodeController(IEpisodeService episodeService)
{
_episodeService = episodeService;
}
public IActionResult Index()
{
var data = _episodeService.GetEpisodes();
return Content(data[0].Name);
}
}
If i run this like this i get an Invalid Operation Exception: Unable to resolve service for type namespace.IEpisodeService while attempting to activate EpisodeController.
Could someone provide more implementation details on how to achieve this?
The solution provided does not resolve solution scanning. But it uses assembly scanning. You may as well view this as a step-by-step guide on how to achieve this. Our Solution Looks sth like this.
Sth.Core --> Class Library.
Sth.Services --> Class Library(Assembly scanning will happen here).
Sth.Web --> ASP.NET Core MVC project.
The Sth.Core has our three-lifetime interfaces which our Services need to inherit from.
- ISthInstanceService
- ISthScopedService
- ISthSingletonService
Sth.Web.Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory()) //Add this
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Sth.Web,Startup.cs
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new SthModule()); // This is the autofac Module. We add it later at the Services Project
}
Now for our Services Project.Here we need to create our autofac module. The one asp.net app instantiated.
public class SthModule : Module
{
protected override void Load(ContainerBuilder builder)
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
builder
.RegisterAssemblyTypes(assemblies)
.Where(t => t.GetInterfaces().Any(i => i.IsAssignableFrom(typeof(ISthScopedService))))
.AsImplementedInterfaces()
.InstancePerLifetimeScope(); // Add similar for the other two lifetimes
base.Load(builder);
}
}
The above code will scan all the Services project and add all the classes that inherit from the ISthScopedService and register them to the container.
Sth.Service.EpisodeService.cs
public class EpisodeServices : IEpisodeService
{
public IList<Episode> GetEpisodes()
{
return new List<Episode>
{
new Episode { Id = 1, Name = "Imposter Syndrome", Description = "Imposter syndrome" }
};
}
}
And the interface.
public interface IEpisodeService : ISthCommonService
{
IList<Episode> GetEpisodes();
}
Now we have implemented assembly scanning(auto-registration) for our services project.

Where to register the context with dependency injection

According to this tutorial I should:
Register your context with dependency injection
The Tutorial describes that I should locate the method
ConfigureServices() and put in there the code advised.
Here's my startup.cs:
using Microsoft.Owin;
using Owin;
[assembly: OwinStartupAttribute(typeof(MyProject.Startup))]
namespace MyProject
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}
so I don't know where to correctly put the code.
Because the project isn't compatible with .net core 2.1, it was needed to change Project->Property to .Net Framework 4.6.1
and install packages Microsoft.EntityFrameworkCore.SqlServer and Microsoft.EntityFrameworkCore.Tools
I tried to add the dependency injection to global.asax.cs file as follows:
protected void Application_Start()
{
var services = new ServiceCollection();
ConfigureServices(services);
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
private void ConfigureServices(IServiceCollection services)
{
var connection = #"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;ConnectRetryCount=0";
services.AddDbContext<BloggingContext>(options => options.UseSqlServer(connection));
}
I succeeded with the step and created the controller and it works, but I haven't chosen the right context (BloggingContext), so it created second database. So, I need to create a controller with BloggingContext, do you know how?
The version of the shown startup and the tutorial are in conflict with each other.
If this if for an Asp.Net Core MVC application then you can add the method your self. Startup class is part of the convention.
public partial class Startup {
//...
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//...
}
}
Reference App startup in ASP.NET Core
The ConfigureServices method
The ConfigureServices method is:
Optional.
Called by the host before the Configure method to
configure the app's services.
Where configuration options are set by convention.
If however you are trying to use .Net Core technologies in an Asp.Net MVC 5+ (which is what the GitHub project targets) then you will need to modify your approach to adapt to using .Net Core Dependency Injection with a non core platform.
First you will need a IDependencyResolver which is the DI framework used by that version of Asp.Net MVC, and a way to replace the default resolver with your own.
public sealed class CoreDependencyResolver : System.Web.Mvc.IDependencyResolver {
private readonly System.Web.Mvc.IDependencyResolver mvcInnerResolver;
private readonly IServiceProvider serviceProvider;
public CoreDependencyResolver(IServiceProvider serviceProvider, System.Web.Mvc.IDependencyResolver dependencyResolver) {
this.serviceProvider = serviceProvider;
mvcInnerResolver = dependencyResolver;
}
public object GetService(Type serviceType) {
object result = this.serviceProvider.GetService(serviceType);
if (result == null && mvcInnerResolver != null)
result = mvcInnerResolver.GetService(serviceType);
return result;
}
public IEnumerable<object> GetServices(Type serviceType) {
IEnumerable<object> result = this.serviceProvider.GetServices(serviceType);
if (result == null && mvcInnerResolver != null)
result = mvcInnerResolver.GetServices(serviceType);
return result ?? new object[0];
}
}
With the custom resolver in place, you can now configure the application to use it.
Using your current example as a starting point (review comments)
protected void Application_Start() {
var services = new ServiceCollection();
ConfigureServices(services);
//build service provider
IServiceProvider provider = services.BuildServiceProvider();
//Get the current resolver used by MVC
var current = DependencyResolver.Current;
//use that and the provider to create your custom resolver
var resolver = new CoreDependencyResolver(provider, current);
//now set the MVC framework to use the resolver that wraps the service provider
//that was created from .Net Core Dependency Injection framework.
DependencyResolver.SetResolver(resolver);
//...
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
private void ConfigureServices(IServiceCollection services) {
//... omitted for brevity (register dependencies as normal)
}
Here I am using Oracle, but you could do the same with SQL Server...
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFrameworkOracle()
.AddDbContext<OracleDbContext>(builder => builder.UseOracle(Configuration["Data:OracleDbContext"]),ServiceLifetime.Scoped)
.AddDbContext<AppsDbContext>(option => option.UseOracle(Configuration["Data:AppsDbConnection:ConnectionString"]), ServiceLifetime.Scoped);
Then in my appsettings.json, I include the connection strings...
"Data": {
"OracleDbContext": "your connection string" },
"AppsDbContext": "your connection string" }
}
It appears you're using .Net Framework, rather than .Net Core.
2 simple ideas here:
Injecting DbContext into service layer: this layer will be class Library for .Net Framework or (use .Net Standard Class library if your platform is .Net Core). This thread shows you how to perform it: Injecting DbContext into service layer
use Ninject as dependency injector if you're on .Net Framework platform. This thread shows a good example: How to handle DBContext when using Ninject
Whilst using .NET Core tooling with full framework works quite well, if you have to use MVC5 and full framework I would not try to work it that way round.
There are many .NET 4.6.1 dependency injection frameworks, in this example I will use Autofac.
Install the NuGet packages Autofac and Autofac.Mvc5.
Add an AutofacRegistration.cs class to the App_Start folder
In the Application_Start() method in Global.asax add the line AutofacRegistration.BuildContainer();
Your AutofacRegistration class is where you wire up all your dependencies for dependency injection. The full docs are here https://autofaccn.readthedocs.io/en/latest/integration/mvc.html
public class AutofacRegistration
{
public static void BuildContainer()
{
var builder = new ContainerBuilder();
// Register your MVC controllers
builder.RegisterControllers(typeof(MvcApplication).Assembly);
// Now grab your connection string and wire up your db context
var conn = ConfigurationManager.ConnectionStrings["BloggingContext"];
builder.Register(c => new BloggingContext(conn));
// You can register any other dependencies here
// Set the dependency resolver to be Autofac.
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
This is assuming your BloggingContext has a constructor that takes the connection string as a parameter and passes it to the base class. Something like
public class BloggingContext : DbContext
{
public BloggingContext(string connectionString) : base(connectionString)
{
}
}
There is loads more in the documentation about scope etc that is worth reading but this should be the nuts and bolts of it.

AutoFac / .NET Core - Register DBcontext

I have a new .NET Core Web API project that has the following projects structure:
API -> Business / Domain -> Infrastructure
The API is very thin with only the API methods. The Business / Domain layer has all my business logic. And finally, my Infrastructure layer has my DB classes using EF Core 2.0.
I know using .NET Core built-in Dependency Injection I can add a reference from the API project to the Infrastructure project, then add the following code in the StartUp.cs file:
services.AddDbContext<MyContext>(options => options.UseSqlServer(connectionString));
However, I would like to maintain a more traditional separation of concerns. So far I have added a module in my Infrastructure layer that attempts to make the registration like so:
builder.Register(c =>
{
var config = c.Resolve<IConfiguration>();
var opt = new DbContextOptionsBuilder<MyContext>();
opt.UseSqlServer(config.GetSection("ConnectionStrings:MyConnection:ConnectionString").Value);
return new MyContext(opt.Options);
}).AsImplementedInterfaces().InstancePerLifetimeScope();
The DBContext, however, is not getting registered. Any class that attempts to access the injected DBContext cannot resolve the parameter.
Is there a way to register the DBContext in a separate project using AuftoFac in a .NET Core Web API Project?
I use Autofac to register both HttpContextAccessor and DbContext.
builder
.RegisterType<HttpContextAccessor>()
.As<IHttpContextAccessor>()
.SingleInstance();
builder
.RegisterType<AppDbContext>()
.WithParameter("options", DbContextOptionsFactory.Get())
.InstancePerLifetimeScope();
DbContextOptionsFactory
public class DbContextOptionsFactory
{
public static DbContextOptions<AppDbContext> Get()
{
var configuration = AppConfigurations.Get(
WebContentDirectoryFinder.CalculateContentRootFolder());
var builder = new DbContextOptionsBuilder<AppDbContext>();
DbContextConfigurer.Configure(
builder,
configuration.GetConnectionString(
AppConsts.ConnectionStringName));
return builder.Options;
}
}
DbContextConfigurer
public class DbContextConfigurer
{
public static void Configure(
DbContextOptionsBuilder<AppDbContext> builder,
string connectionString)
{
builder.UseNpgsql(connectionString).UseLazyLoadingProxies();
}
}
I think that the problem is that you're trying to register MyContext() using AsImplementedInterfaces(). This is not how DbContext are getting registered usually. You should register and resolve class itself.
Another simple solution for Autofac version 4.8.1
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddControllersAsServices();
services.AddDbContext<MyContext>(options => options.UseSqlServer(Configuration.GetConnectionString("ConnectionStrings:MyConnection:ConnectionString")));
var builder = new ContainerBuilder();
builder.Populate(services);
//...
// Your interface registration
//...
builder.Build(Autofac.Builder.ContainerBuildOptions.None);
}
Here's an implementation I use - it mimics EF Core 3.1 registration with Autofac 4.9.4. Be sure to adjust scopes per your requirements.
public void RegisterContext<TContext>(ContainerBuilder builder)
where TContext : DbContext
{
builder.Register(componentContext =>
{
var serviceProvider = componentContext.Resolve<IServiceProvider>();
var configuration = componentContext.Resolve<IConfiguration>();
var dbContextOptions = new DbContextOptions<TContext>(new Dictionary<Type, IDbContextOptionsExtension>());
var optionsBuilder = new DbContextOptionsBuilder<TContext>(dbContextOptions)
.UseApplicationServiceProvider(serviceProvider)
.UseSqlServer(configuration.GetConnectionString("MyConnectionString"),
serverOptions => serverOptions.EnableRetryOnFailure(5, TimeSpan.FromSeconds(30), null));
return optionsBuilder.Options;
}).As<DbContextOptions<TContext>>()
.InstancePerLifetimeScope();
builder.Register(context => context.Resolve<DbContextOptions<TContext>>())
.As<DbContextOptions>()
.InstancePerLifetimeScope();
builder.RegisterType<TContext>()
.AsSelf()
.InstancePerLifetimeScope();
}
In the desired project you can create an extension method that adds the context to the collection
public static class MyDataExtensions {
public static IServiceCollection AddMyData(this IServiceCollection services) {
//...
services.AddDbContext<MyContext>(options => options.UseSqlServer(connectionString));
//...
}
}
with that then in your start up it is just a matter of calling the extension exposed from the other project
services.AddMyData();
//...other settings
The API project is the composition root, so it needs to know all the relevant dependencies anyway. At least with this extension you do not have to make direct reference of the used db context,

How to link Autofac to UnitTesting

Within my Web API I have linked Autofac as IoC container, and I do it like this:
Domain level
public class Autofac
{
protected ContainerBuilder Builder { get; set; }
public Autofac()
{
this.Builder = new ContainerBuilder();
}
public virtual IContainer Register()
{
// Register dependencies
SetUpRegistration(this.Builder);
// Build registration.
var container = this.Builder.Build();
// End
return container;
}
private static void SetUpRegistration(ContainerBuilder builder)
{
// === DATALAYER === //
// MyRepository
builder.RegisterType<MyRepository>()
.As<IMyRepository>()
.InstancePerLifetimeScope();
// === DOMAIN === //
// MyManager
builder.RegisterType<MyManager>()
.As<IMyManager>()
.InstancePerLifetimeScope();
}
}
Web API
public class Autofac : Domain.IoC.Autofac
{
public IContainer Register(HttpConfiguration config)
{
// Register your Web API controllers.
base.Builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// OPTIONAL: Register the Autofac filter provider.
base.Builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration);
// Complete registration and get container instance.
var container = base.Register();
// Set the dependency resolver to be Autofac.
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// Done.
return container;
}
}
As you see it inherits from the base class from Domain and sets up Web API specific config.
Usage
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
new IoC.Autofac().Register(GlobalConfiguration.Configuration);
}
Which is at global.asax, as you know.
The question
This works fine for Web API, but I haven't got a clue what I need to do to register all this within a UnitTest project context.
The idea is that I would create a similar implementation to the Autofac class at Web API level, but than with mocks (completely ignoring the base class from Domain).
Any pointers?
Personally I never see the need (and I struggle to comprehend how viable or helpful it would be) to setup my IoC container directly within a unit test.
As a unit test is used to test a logical piece of code that can be quickly built, easily ran and doesn't require much (I'd advocate no) tear-down. It should not require all of your application to be be setup for the test to run.
Remember that your unit test is simply testing the flow of data through the system i.e that your DomainManager is actually going to call a IRepository when you expect that it should. Then you would have separate test classes for all your repositories to determine that they would correctly add to the database etc.
I'm not sure how you use the DBContext class but as an example of a wrapper this is what it would sort of look like.
interface IDBSetWrapper
{
object Add(object entity);
}
interface IDBContextWrapper
{
...
IDBSet Set(Type entityType);
...
}
class DBContextWrapper : IDBContextWrapper
{
private readonly DBContext context;
public DBContextWrapper()
{
context = new DBContext();
}
...
public IDBSet Set(Type entityType)
{
var dbSet = context.Set(entityType);
return new DBSetWrapper(dbSet);
}
...
}
It's not much but I hope that it demonstrates what I mean about a thin wrapper. Basically the wrapper is the DBContext and will contain an instance of it within the class, the actual DBContext will be called when you request the wrapper to do anything.
I have shown what would happen when returning another object (in this case a DBSet), this will also be wrapped in a separate object with an interface. This is so that you can mock the returns from this class easily.
You can add this new wrapper into your IoC a little better now as it provides an interface.
One thing to note is that you won't be able to and probably wouldn't wish to test the wrapper class, there would be very little point as I see it. But previously I've seen colleagues do an integration test on these sort of classes.

Autofac modules with their own dependencies

I'm struggling with how to organize my Autofac component registrations in modules given that some of the modules themselves have dependencies.
I've implemented an abstraction of configuration data (i.e. web.config) in an interface:
interface IConfigurationProvider
{
T GetSection<T>(string sectionName)
where T : System.Configuration.ConfigurationSection;
}
along with implementations for ASP.NET (WebConfigurationProvider) and "desktop" applications (ExeConfigurationProvider).
Some of my autofac modules then require an IConfigurationProvider as a constructor parameter, but some don't:
class DependentModule : Module
{
public DependentModule(IConfigurationProvider config)
{
_config = config;
}
protected override void Load(ContainerBuilder builder)
{
var configSection = _config.GetSection<CustomConfigSection>("customSection");
builder.RegisterType(configSection.TypeFromConfig);
}
private readonly IConfigurationProvider _config;
}
class IndependentModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register(/* other stuff not based on configuration */);
}
}
Since the RegisterType() extension method doesn't accept a registration delegate (Func<IComponentContext, T>), like Register() does, I can't register the IConfigurationProvider up-front and then resolve it when I go to register the type specified in the configuration, something like:
// this would be nice...
builder.RegisterType(c => c.Resolve<IConfigurationProvider>().GetSection<CustomConfigSection>("sectionName").TypeFromConfig);
This means that I need to be able to register modules with and without a dependency on IConfigurationProvider.
It's obvious how to manually instantiate each module and register it:
IConfigurationProvider configProvider = ...;
var builder = new ContainerBuilder();
builder.RegisterModule(new DependentModule(configProvider));
builder.RegisterModule(new IndependentModule());
using (var container = builder.Build())
{
...
}
But I don't want to manually instantiate my modules - I want to scan assemblies for modules and register them automatically (as discussed in this question). So I have to use reflection to scan the assembly for IModule types, and use Activator.CreateInstance to make registerable instances. But how do I know whether or not to pass an IConfigurationProvider as a constructor parameter. And what happens when other modules have additional or different dependencies?
There's got to be a more straightforward way of accomplishing the basic task: register a type specified in some configuration provided via an interface, right? So how do I do that?
You could do something like this:
using System.Collections.Generic;
using System.Linq;
using Autofac;
using Autofac.Core;
using NUnit.Framework;
namespace Yo_dawg
{
[TestFixture]
public class I_heard_you_like_containers
{
[Test]
public void So_we_built_a_container_to_build_your_container()
{
var modules = GetModules();
Assert.That(modules.Length, Is.EqualTo(4));
var builder = new ContainerBuilder();
foreach (var module in modules)
builder.RegisterModule(module);
var container = builder.Build();
}
private IModule[] GetModules()
{
var builder = new ContainerBuilder();
var configurationProvider = new ConfigurationProvider();
builder.RegisterInstance(configurationProvider).AsImplementedInterfaces().ExternallyOwned();
builder.RegisterAssemblyTypes(GetType().Assembly)
.Where(t => t.IsAssignableTo<IModule>())
.AsImplementedInterfaces();
using (var container = builder.Build())
return container.Resolve<IEnumerable<IModule>>().ToArray();
}
}
public class ModuleA : Module
{
public ModuleA(IConfigurationProvider config)
{
}
}
public class ModuleB : Module
{
public ModuleB(IConfigurationProvider config)
{
}
}
public class ModuleC : Module
{
}
public class ModuleD : Module
{
}
public interface IConfigurationProvider
{
}
public class ConfigurationProvider : IConfigurationProvider
{
}
}
For this scenario, Autofac's own XML configuration seems to cover the scenarios you're targeting. Adding a new IConfigurationProvider mechanism seems like reinventing this functionality already provided by the container. The basics are documented at: https://code.google.com/p/autofac/wiki/XmlConfiguration. The configuration syntax has in-built support for modules.
There's a nice alternative by Paul Stovell that allows modules to be registered in code yet receive parameters from config - see: http://www.paulstovell.com/convention-configuration. Hope this helps!

Categories

Resources