How to use Autofac Modules with Muti-Tenant container? - c#

How is it possible to use Autofac's Modules combined with the Multitenant package?
This is my bootstrapping part for Autofac:
var builder = new ContainerBuilder();
// only very basic common registrations here...
// everything else is in modules
// Register the Autofac module for xml configuration as last
// module to allow (emergency) overrides.
builder.RegisterModule(new ConfigurationSettingsReader());
// create container for IoC
this.container = builder.Build();
// check for tenant strategy -> if exists, go multi-tenant
if (this.container.IsRegistered<ITenantIdentificationStrategy>())
{
var tenantIdentificationStrategy = this.container
.Resolve<ITenantIdentificationStrategy>();
this.container = new MultitenantContainer(tenantIdentificationStrategy,
this.container);
// how to use xml modules instead of manually register at this point?
}
else
{ ... }
I don't want to call something like
container.ConfigureTenant('1', b => b.RegisterType<Tenant1Dependency>()
.As<IDependency>()
.InstancePerDependency());
within the bootstrapper (which don't need to know something about this).
Instead I want to do something like this:
public class TenantModule : Module
{
protected override void Load(ContainerBuilder builder)
{
container.ConfigureTenant('1', b => b.RegisterType<Tenant1Dependency>()
.As<IDependency>()
.InstancePerDependency());
container.ConfigureTenant('2', b => b.RegisterType<Tenant2Dependency>()
.As<IDependency>()
.InstancePerDependency());
...
My first idea was to check when registering something for a special tenant:
public class TenantModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register((c, p) => {
var s = c.Resolve<ITenantIdentificationStrategy>();
if (s != null)
{
var id = s.IdentifyTenant<string>();
...
}
});
I could check the id and register only if id fits, but what to return if id is not matching? null? DoNothingObject? Looks strange/horrible in code to me and something like
builder.Register("1", (context, parameters) => ...
feels more natural. But I don't know how to achieve this out of the box with Autofac. Did I miss something? How did you solved that problem?

Missed the point that ConfigureTenant also passes the containerBuilder which allows the registration of a tenant specific ConfigurationSectionReader. So an option is to have an additional customer/tenant specific ConfigurationSection:
mtc.ConfigureTenant("mytenant1",
containerBuilder =>
{
containerBuilder.RegisterModule(new ConfigurationSettingsReader("mytenant1"));
}
);
mtc.ConfigureTenant("mytenant2",
containerBuilder =>
{
containerBuilder.RegisterModule(new ConfigurationSettingsReader("mytenant2"));
}
);
This also leads me to the point that I can resolve the tenant identification strategy first, get the identifier and make only the registrations for the current tenant (like a customer specific bootstrap):
var tenantIdentificationStrategy = container.Resolve<ITenantIdentificationStrategy>();
var tid = tenantIdentificationStrategy.IdentifyTenant<string>();
mtc.ConfigureTenant(tid ,
containerBuilder =>
{
containerBuilder.RegisterModule(new ConfigurationSettingsReader(tid));
// or something like that which identifies the tenant config section
}
);
Or I could put this into a simple foreach to make registrations for all available tenants (but this sounds curious in most scenarios here because only one tenant will be resolved at time at its a bootstrapping part, maybe for other special cases worth to mention).
Any better ideas? I'll vote up for better ideas... ;-)

Related

How to switch services in TestServer using WebApplicationFactory?

I'm using custom WebApplication factory
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup: class {
protected override void ConfigureWebHost(IWebHostBuilder builder) {
builder.ConfigureServices(services => {
// Create a new service provider.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
services.AddDbContext<GrabGoContext>(options => {
options.UseInMemoryDatabase("GrabGoDb");
options.UseInternalServiceProvider(serviceProvider);
});
services.AddSingleton<TestEmailServer>();
services.AddScoped<IEmailProvider, TestEmailProvider>(); // <- HERE
});
base.ConfigureWebHost(builder);
}
I want to switch my default IEmailProvider service called DefaultEmailProvider to my special TestEmailProvider, but the problem is that the method ConfigureWebHost is executed before Startup.ConfigureServices(IServiceCollection services), so my service DefaultEmailProvider is set after TestEmailProvider. Therefore in my ClientController service DeafultEmailProvider is used instead of test service.
My question is:
How can I switch service DefaultEmailProvider with my TestEmailProvider using WebApplicationFactory?
#Update
Ok, I've managed to go deeper. I found that method builder.ConfigureTestServices() overrides other services. But when switch this in my ConfigureWebHost(IWebHostBuilder builder) method and I try to create new HttpClient using CreateClient() it throws:
System.InvalidOperationException: 'A ConfigureServices method that returns an IServiceProvider is not compatible with the use of one or more IStartupConfigureServicesFilter. Use a void returning ConfigureServices method instead or a ConfigureContainer method.'
#Update 28.05.2019
Still looking for better solution, but I've manage to do sort of hack.
In my Startup.cs I've swapped my
services.AddScoped<IEmailProvider, SendGridEmailProvider>();
with TryAdd[Something]
services.TryAddScoped<IEmailProvider, SendGridEmailProvider>();
You should be able to do it in the ConfigureWebHost similar to the below:
public class CustomWebApplicationFactory : WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Remove application IEmailProvider service
var emailProviderDescriptor = services.SingleOrDefault(d => d.ServiceType == typeof(IEmailProvider));
if (emailProviderDescriptor != null)
{
services.Remove(emailProviderDescriptor);
}
// Add new test service(s)
services.AddScoped<IEmailProvider, TestEmailProvider>();
services.AddSingleton<TestEmailServer>(); // May need to remove using the descriptor similar to IEmailProvider
// Remove the app's GrabGoContext registration, add in memory one then seed data.
var dbDescriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<GrabGoContext>));
if (dbDescriptor != null)
{
services.Remove(dbDescriptor);
}
services.AddDbContext<GrabGoContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
});
var serviceProvider = services.BuildServiceProvider();
using var scope = serviceProvider.CreateScope();
var scopedServices = scope.ServiceProvider;
var grabGoContext = scopedServices.GetRequiredService<GrabGoContext>();
// Seed your db here & save..
grabGoContext.SaveChanges();
});
base.ConfigureWebHost(builder);
}
}
You may want to make it easier on yourself and just implement the factory, like the above, rather than keep it generic. I've also added slightly different code for your in-memory database.
It hasn't been compiled, but the syntax shouldn't be far off.
Hope that helps.
I just spent several hours trying to do a similar thing (override the services in my integration tests). Turns out ConfigureServices runs before the SUT (subject under test) service configuration. ConfigureTestServices, however, runs after and is where you would override with your mocks.
The docs are not at all clear on this, though I did, after the fact, find this:
https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.0#inject-mock-services
You can do it based on Environment. This is just code to point you in the right direction.
public IHostingEnvironment HostingEnvironment { get; }
public void ConfigureServices(IServiceCollection services)
{
if (HostingEnvironment.IsDevelopment())
{
services.AddTransient<ITestService, TestService>();
}
else
{
services.AddTransient<IRealService, RealService>();
}
// other services
}
I hope this helps.
You can try calling ConfigureTestServices instead of ConfigureServices:
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder
.UseEnvironment("Test")
.ConfigureTestServices(services =>
{
// Put your logic here
}); ;
}

How to inject AutoMapper with Autofac?

What is the proper way to inject AutoMapper to other layers?
I read this blog post , but this code cause exception below
An exception of type 'AutoMapper.AutoMapperMappingException' occurred in AutoMapper.dll but was not handled in user code
when try mapping in service layer.
List<StudentViewModel> list2 = _mapper.Map<List<StudentViewModel>>(list);
My AutoFac configuration like below:
public static class DependencyRegistration
{
public static void Config()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterType<TypeMapFactory>().As<ITypeMapFactory>();
builder.RegisterType<ConfigurationStore>().As<ConfigurationStore>().WithParameter("mappers", MapperRegistry.Mappers).SingleInstance();
builder.Register((ctx, t) => ctx.Resolve<ConfigurationStore>()).As<IConfiguration>().As<IConfigurationProvider>();
builder.RegisterType<MappingEngine>().As<IMappingEngine>();
//...
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
.netcore 3
Autofac 5.1.2
AutoMapper 9.0.0
AutoMapperProfiles -> My profile name
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<AutoMapperProfiles>().As<Profile>();
builder.Register(c => new MapperConfiguration(cfg =>
{
foreach (var profile in c.Resolve<IEnumerable<Profile>>())
{
cfg.AddProfile(profile);
}
})).AsSelf().SingleInstance();
builder.Register(c => c.Resolve<MapperConfiguration>().CreateMapper(c.Resolve)).As<IMapper>().InstancePerLifetimeScope();
}
It seems that you need to use the IConfiguration object that is registered in the container to create the maps like this:
var configuration = container.Resolve<IConfiguration>();
configuration.CreateMap<Student, StudentViewModel>();
I think that you should be doing this at the start of your application.
Here is a better way (IMO) to configure things in the Config method:
public static void Config()
{
var configuration_store = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers);
var mapping_engine = new MappingEngine(configuration_store);
configuration_store.CreateMap<Student, StudentViewModel>();
var builder = new ContainerBuilder();
builder.RegisterInstance(mapping_engine).As<IMappingEngine>();
//...
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
I am assuming in the last example, that your classes need access only to IMappingEngine (and not IConfiguration), since your should already setup all mappings in the Config method (or some other configuration method at application startup).

Explicit resolving of ILog in Autofac when using with Log Injection Module

I use the following code in order to register log4net for all the classes that need it.
public class LogInjectionModule : Module
{
private readonly string _configPath;
public LogInjectionModule(string configPath)
{
_configPath = configPath;
}
protected override void AttachToComponentRegistration(IComponentRegistry registry,
IComponentRegistration registration)
{
XmlConfigurator.Configure(new FileInfo(_configPath));
registration.Preparing += OnComponentPreparing;
}
private static void OnComponentPreparing(object sender, PreparingEventArgs e)
{
var t = e.Component.Activator.LimitType;
e.Parameters = e.Parameters.Union(new[]
{
new ResolvedParameter((p, i) => p.ParameterType == typeof (ILog),
(p, i) => LogManager.GetLogger(t))
});
}
}
All the classes are registered using autofac's types scanning:
builder.RegisterAssemblyTypes(typeof (IResourceFinder).Assembly)
.AsImplementedInterfaces();
And it works fine!
One class needs to be registered explicitly tries to resolve ILog and fails
builder.Register(x => new ClassThatNeedsILog(x.Resolve<ILog>())).AsImplementedInterfaces();
Here is that class
public class ClassThatNeedsILog
{
public ClassThatNeedsILog(ILog log)
{
}
}
I am getting the following exception:
Autofac.Core.Registration.ComponentNotRegisteredException : The
requested service 'log4net.ILog' has not been registered. To avoid
this exception, either register a component to provide the service,
check for service registration using IsRegistered(), or use the
ResolveOptional() method to resolve an optional dependency.
Your LogInjectionModule never registers any ILog into the container only supplies paramters for the resolved instances on the preparing face, and it only works for instances created by Autofac.
So when you write builder.Register(x => new ClassThatNeedsILog(x.Resolve<ILog>())) you are creating the ClassThatNeedsILog manually. with new ClassThatNeedsILog(...)
Hence Autofac does not know about your instance creation (so your OnComponentPreparing won't run) and because you haven't really registered any ILog implementation you get the ComponentNotRegisteredException.
You have two options:
register an ILog in the container directly
let Autofac create your ClassThatNeedsILog type.
So you can just register an ILog in the container with:
builder.RegisterInstance(LogManager.GetLogger("Logger")).As<ILog>();
Then your code will work fine.
Or if you anyway creating the ClassThatNeedsILog by hand can just supply directly the ILog there:
builder.Register(x => new
ClassThatNeedsILog(LogManager.GetLogger(typeof(ClassThatNeedsILog))))
.AsImplemen‌​tedInterfaces();
The other options is to let Autofac create the instances for you, so change your registration to:
builder.RegisterType<ClassThatNeedsILog>()
.AsImplemen‌​tedInterfaces();
In this case Autofac will handle the instance creation for you and it will call the OnComponentPreparing method of your module.
If you want to supply additional constructor parameters you can use WithParameter and WithParameters methods:
builder.RegisterType<ClassThatNeedsILog>()
.AsImplemen‌​tedInterfaces()
.WithParameters(new Parameter[] {
ResolvedParameter.ForNamed<IAnotherInterface>("NAME"),
ResolvedParameter.ForNamed<IYetAnotherInterface>("ANOTHERNAME")});
The builder needs to Build a container before it can Resolve.
Try something like this (untested)
builder
.RegisterAssemblyTypes(typeof (IResourceFinder).Assembly)
.AsImplementedInterfaces();
/* process LogInjectionModule */
IContainer container = builder.Build();
var updateBuilder = new ContainerBuilder();
updateBuilder
.Register(x => new ClassThatNeedsILog(x.Resolve<ILog>()))
.AsImplementedInterfaces();
updateBuilder.Update(container);
nemesv's answer resolved the issue for me.
Though, I had to spend some more time getting it to work after using the RegisterType approach, as I was missing the below:
config.Filters.AddRange(config.DependencyResolver.GetServices(typeof(IExceptionFilter)).Select(o => o as IExceptionFilter));
Just adding mentioning it here, if other's have forgotten to configure the filter in the first place.

conditional component registration in autofac

Is it possible to register a component conditionally on an other component's state? Something like:
ContainerBuilder.RegisterConditionally<T>(
Func<IComponentContext, bool>,
Func<IComponentContext, T>);
I've found that prior to V2 of autofac one could use a "Register().OnlyIf()" construction that seemed like the one I'm looking for. I would like such feature to conditionally override a default registration.
class CommonRegistrations
{
public virtual void Register(ContainderBuilder builder)
{
builder.Register(ctx => LoadSettings()).As<ISettings>().SingleInstance();
builder.RegisterType<DefaultFoo>().As<IFoo>();
}
}
class SpecificRegistrations : CommonRegistrations
{
public virtual void Register(ContainerBuilder builder)
{
base.Register(builder);
builder.ConditionalyRegister(
ctx => ctx.Resolve<ISettings>().ReallyUseSpecificFoo,
ctx => new SpecificFoo()).As<IFoo>();
}
}
...
var builder = new ContainerBuilder();
var registrations = new SpecificRegistrations();
registrations.Register(builder);
var container = builder.Build();
IFoo foo = container.Resolve<IFoo>();
The foo will be according to ISettings.ReallyUseSpecificFoo either instance of DefaultFoo or instance of SpecificFoo.
Thank you.
There is no way to perform conditional registration at the container level based on container contents. The trouble is that you would need to resolve something in the container in order to determine what gets registered in the container, which then could technically affect whether you wanted to register the thing in the first place. Chicken/egg circular dependency problem.
You can, however, register things conditionally into nested lifetime scopes. Most integration points (like ASP.NET) resolve out of nested lifetime scopes (like an HTTP-request-length lifetime scope). You can register things on the fly into nested lifetime scopes and that might solve your problem.
var builder = new ContainerBuilder();
builder.Register(ctx => LoadSettings()).As<ISettings>().SingleInstance();
builder.RegisterType<DefaultFoo>().As<IFoo>();
var container = builder.Build();
var settings = container.Resolve<ISettings>();
using(var scope =
container.BeginLifetimeScope(b => {
if(settings.ReallyUseSpecificFoo)
{
b.RegisterType<SpecificFoo>().As<IFoo>();
}
})
{
// Resolve things from the nested lifetime scope - it will
// use the overrides. This will get the SpecificFoo if the
// configuration setting is true.
var foo = scope.Resolve<IFoo>();
}
Another option you have is to make the registration a lambda. It might make the registration itself more complex but it's an option you could consider.
var builder = new ContainerBuilder();
builder.Register(ctx => {
var settings = ctx.Resolve<ISettings>();
if(settings.ReallyUseSpecificFoo)
{
return new SpecificFoo();
}
return new DefaultFoo();
}).As<IFoo>();
If manual construction there isn't appealing, you could pass it through Autofac, too.
var builder = new ContainerBuilder();
// Register the IFoo types - but NOT "As<IFoo>"
builder.RegisterType<DefaultFoo>();
builder.RegisterType<SpecificFoo>();
// In the lambda use Resolve<T> to get the instances.
builder.Register(ctx => {
var settings = ctx.Resolve<ISettings>();
if(settings.ReallyUseSpecificFoo)
{
return ctx.Resolve<SpecificFoo>();
}
return ctx.Resolve<DefaultFoo>();
}).As<IFoo>();
Yet another option is to update an existing container after being built. In this case, you avoid the chicken/egg scenario by actually building the container, using it, and changing registrations after the fact.
var builder = new ContainerBuilder();
builder.Register(ctx => LoadSettings()).As<ISettings>().SingleInstance();
builder.RegisterType<DefaultFoo>().As<IFoo>();
var container = builder.Build();
var settings = container.Resolve<ISettings>();
if(settings.ReallyUseSpecificFoo)
{
var updater = new ContainerBuilder();
updater.RegisterType<SpecificFoo>().As<IFoo>();
updater.Update(container);
}
Finally, you might consider XML configuration. Given the registration is dependent on some sort of configuration setting, you might consider using Autofac's XML configuration support. That way, instead of trying to resolve something out of an un-built container to conditionally register something else, you could just specify the right thing to register using the XML configuration and register the correct thing the first time.

Is Module a binding context for Autofac

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.

Categories

Resources