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))))
.AsImplementedInterfaces();
The other options is to let Autofac create the instances for you, so change your registration to:
builder.RegisterType<ClassThatNeedsILog>()
.AsImplementedInterfaces();
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>()
.AsImplementedInterfaces()
.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.
Related
How do I configure dependency injection in ASP.NET Core to return a certain instance depending on the type it's being injected into?
Let's say I have a simple interface,
public interface IHello
{
string SayHello();
}
And two different implementations:
public class Hello : IHello
{
public string SayHello() => "Hello...";
}
public class Hey : IHello
{
public string SayHello() => "HEY!";
}
And finally I have a few classes that all depend on an instance of IHello:
public class Class1
{
public Class1(IHello hello)
{
}
}
public class Class2
{
public Class2(IHello hello)
{
}
}
Now, in ConfigureServices I would do something like this:
services.AddSingleton<IHello, Hello>();
to configure any class depending on IHello to always get the same instance of Hello.
BUT: What I really want is for Class1 to always get the same singleton instance of Hey and all other classes should just get an instance of Hello. It could look like this in ConfigureServices (doesn't work, obviously):
services.AddSingleton<IHello, Hello>();
services.AddSingleton<IHello, Hey, Class1>(); // Doesn't work, but would be neat if it did...
Here's a simple approach. It lacks a certain elegance, but it will do what you need:
public static void Register(IServiceCollection serviceCollection)
{
serviceCollection.AddSingleton<Hello>();
serviceCollection.AddSingleton<Hey>();
serviceCollection.AddSingleton<ClassThatDependsOnIHello1>(serviceProvider =>
new ClassThatDependsOnIHello1(serviceProvider.GetService<Hello>()));
serviceCollection.AddSingleton<ClassThatDependsOnIHello2>(serviceProvider =>
new ClassThatDependsOnIHello2(serviceProvider.GetService<Hey>()));
}
There are two classes that depend on IHello. The registration for each of them includes a function. That function resolves either Hello or Hey from the service provider and passes it to the constructor of each respective class. That way you get control over which implementation gets passed to which class.
(It's beside the point that the service provider hasn't been built yet. The function you're providing will be executed later, and the service provider passed to it will be the one that has been built from the service collection.)
A downside to this is that now your DI registration explicitly calls your constructors. That can be a nuisance because if the constructors change (maybe you inject other dependencies) then you'll have to edit this code. That's not great, but it's not uncommon.
Plan B would be to do as Microsoft suggests and use another container.
Autofac
First, add the Autofac.Extensions.DependencyInjection NuGet package. This references Autofac and also provides the extensions needed to add an Autofac container to a service collection.
I've arranged this to focus on the way dependencies get registered with Autofac. It's similar to IServiceCollection and IServiceProvider. You create a ContainerBuilder, register dependencies, and then build a Container from it:
static void RegisterDependencies(this ContainerBuilder containerBuilder)
{
containerBuilder.RegisterType<Hello>().Named<IHello>("Hello");
containerBuilder.RegisterType<Hey>().Named<IHello>("Hey");
containerBuilder.RegisterType<ClassThatDependsOnIHello1>().WithParameter(
new ResolvedParameter((parameter, context) => parameter.ParameterType == typeof(IHello),
(parameter, context) => context.ResolveNamed<IHello>("Hello")
));
containerBuilder.RegisterType<ClassThatDependsOnIHello2>().WithParameter(
new ResolvedParameter((parameter, context) => parameter.ParameterType == typeof(IHello),
(parameter, context) => context.ResolveNamed<IHello>("Hey")
));
}
That's not really pretty either, but it sidesteps the problem of calling the constructors.
First it registers two implementations of IHello and gives them names.
Then it registers the two classes that depend on IHello. WithParameter(new ResolvedParameter()) uses two functions:
The first function determines whether a given parameter is the one we want to resolve. So in each case we're saying, "If the parameter to resolve is IHello, then resolve it using the next function."
It then resolves IHello by specifying which named registration to use.
I'm not excited by how complicated that is, but it does mean that if those classes have other dependencies injected, they'll be resolved normally. You can resolve ClassThatDependsOnIHello1 without actually calling its constructor.
You can also do it without the names:
static void RegisterDependencies(this ContainerBuilder containerBuilder)
{
containerBuilder.RegisterType<Hello>();
containerBuilder.RegisterType<Hey>();
containerBuilder.RegisterType<ClassThatDependsOnIHello1>().WithParameter(
new ResolvedParameter((parameter, context) => parameter.ParameterType == typeof(IHello),
(parameter, context) => context.Resolve<Hello>()
));
containerBuilder.RegisterType<ClassThatDependsOnIHello2>().WithParameter(
new ResolvedParameter((parameter, context) => parameter.ParameterType == typeof(IHello),
(parameter, context) => context.Resolve<Hey>()
));
containerBuilder.RegisterType<SomethingElse>().As<ISomethingElse>();
}
We can clean that up some with an method that simplifies creating that ResolvedParameter because that's so hideous.
public static ResolvedParameter CreateResolvedParameter<TDependency, TImplementation>()
where TDependency : class
where TImplementation : TDependency
{
return new ResolvedParameter((parameter, context) => parameter.ParameterType == typeof(TDependency),
(parameter, context) => context.Resolve<TImplementation>());
}
Now the previous registration becomes:
containerBuilder.RegisterType<ClassThatDependsOnIHello1>().WithParameter(
CreateResolvedParameter<IHello, Hello>());
containerBuilder.RegisterType<ClassThatDependsOnIHello2>().WithParameter(
CreateResolvedParameter<IHello, Hey>());
Better!
That leaves the details of how you integrate it with your application, and that varies with your application. Here's Autofac's documentation which provides more detail.
For testing purposes you can do this:
public static IServiceProvider CreateServiceProvider()
{
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterDependencies();
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
}
I like to write unit tests for this sort of thing. This method will create an IServiceProvider from the Autofac container, and then you can test resolving things from the container to make sure they get resolved as expected.
If you prefer another container, see if it has similar integrations to use it with Microsoft's container. You might find one you like better.
Windsor
Here's a similar example using Castle.Windsor.MsDependencyInjection.
public static class WindsorRegistrations
{
public static IServiceProvider CreateServiceProvider(IServiceCollection serviceCollection)
{
var container = new WindsorContainer();
container.RegisterDependencies();
return WindsorRegistrationHelper.CreateServiceProvider(container, serviceCollection);
}
public static void RegisterDependencies(this IWindsorContainer container)
{
container.Register(
Component.For<Hello>(),
Component.For<Hey>(),
Component.For<ClassThatDependsOnIHello1>()
.DependsOn(Dependency.OnComponent<IHello, Hello>()),
Component.For<ClassThatDependsOnIHello2>()
.DependsOn(Dependency.OnComponent<IHello, Hey>())
);
}
}
There are two things I like about this:
It's so much easier to read! When resolving ClassThatDependsOnIHello2, fulfill the dependency on IHello with Hey. Simple.
This - WindsorRegistrationHelper.CreateServiceProvider(container, serviceCollection) - allows you to register dependencies with the IServiceCollection and include them in the service provider. So if you have existing code that registers lots of dependencies with IServiceCollection you can still use it. You can register other dependencies with Windsor. Then CreateServiceProvider mixes them all together. (Maybe Autofac has a way to do that too. I don't know.)
I'm currently working on migrating an application from Ninject to Autofac 4. We had a logic to set some properties of a resolved class instance before it gets injected into *somewhere^. In Ninject we did it like this.
// Ninject setup example
this.Bind<IServiceContext>()
.To<DefaultServiceContext>()
.InCallScope()
.OnActivation((ctx, instance) =>
{
if (instance.Module == null) {
instance.Module = ctx.Request.Target.Member.DeclaringType.FullName;
}
});
The key in the code is that using ctx.Request.Target.Member we could access the constructor info (in case of constructor injection of course) where the ongoing injection happens. So we could initialize our injected service class by setting its Module property to the injection target type name.
I cannot find anything similar in Autofac. I've tried both the OnActivating and OnActivated hooks, but those seem not to provide this information and seem to mean slightly different thing compared to Ninject's hooks.
Instance can be shared across lifetimescope. To avoid any side effect Autofac doesn't let us know which component requested the activated component.
By the way you can create a custom parameter that will take care of injecting all your IServiceContext. By using a module you can add this custom parameter to every component. This way you will know which type requested your IServiceContext
This module will give you access to the target type when a T is requested.
public class TargetPreparingCallbackModule<T> : Module
{
public TargetPreparingCallbackModule(Func<Type, Parameter> targetPreparing)
{
this._targetPreparing = targetPreparing;
}
private readonly Func<Type, Parameter> _targetPreparing;
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry,
IComponentRegistration registration)
{
registration.Preparing += this.Registration_Preparing;
}
private void Registration_Preparing(object sender, PreparingEventArgs e)
{
var t = e.Component.Activator.LimitType;
e.Parameters = e.Parameters.Union(
new[]
{
new ResolvedParameter(
(p, c) => p.ParameterType == typeof (T),
(p, c) => {
Parameter parameter = this._targetPreparing(t);
T instance = c.Resolve<T>(parameter);
return instance;
})
});
}
}
you can then use it like this :
builder.RegisterModule(
new TargetPreparingCallbackModule<Foo>(targetType => new NamedParameter("module", targetType.FullName)));
You can also use the Activating or Activated event of the target if you want to have access to the target instance.
So far we've used the SimpleInjectorServiceHostFactory in the SimpleInjector.Integration.Wcf for our WCF services.
This allowed us to avoid the typical "No parameterless constructor defined", when we have interfaces as parameters that SimpleInjector should resolve.
In the global.asax:
var container = new Container();
container.Options.DefaultScopedLifestyle = new WcfOperationLifestyle();
container.Register<IOurBusinessService, OurBusinessService>();
container.Verify();
SimpleInjectorServiceHostFactory.SetContainer(container);
To configure/register AutoMapper we would call some code to register it in the Global.asax, like so:
var cfg = new MapperConfigurationExpression();
cfg.CreateMap<SomeObject, SomeObjectDTO>();
Mapper.Initialize(cfg);
Mapper.Configuration.AssertConfigurationIsValid();
However it seems that when our Web-Services are called directly with a net.tcp endpoint, there is sometimes no more AutoMapper registered. This seems to be the case as the code in Global.asax in Application_Start is never executed, when the WCF service is requested directly.
We currently try to derive from ServiceHostFactory and register both AutoMapper and SimpleInjector in our overridden CreateServiceHost method.
This however does give us again the "No parameterless constructor defined" error.
Do you have any solution or best practices?
Is your configuration correct?
You can create the Mapper at startup and then inject it as a singleton dependency:
Create the Mapper (code found here):
var config = new MapperConfiguration(cfg => {
cfg.AddProfile<AppProfile>();
cfg.CreateMap<Source, Dest>();
});
var mapper = config.CreateMapper();
// or
IMapper mapper = new Mapper(config);
Register as singleton:
var container = new Container();
// Registrations
container.RegisterSingleton(typeof(IMapper), mapper);
To inject:
public class MyClass
{
private readonly IMapper mapper;
public MyClass(IMapper mapper)
{
this.mapper = mapper;
}
}
I am not sure if this will solve you "No parameterless constructor" issue, but this is a good way to handle the AutoMapper injection. If It does not solve the problem, let me know.
Let me know if you have questions.
One way to make the above scenario work was to derive from SimpleInjectorServiceHostFactory and do the override there.
public class OurServiceHostFactory : SimpleInjectorServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
// ConfigInit is a static class with a simple check, to see if the configuration was already initialized
// the same method ConfigInig.Configure() is also called in the Global.asax in Application_Start
ConfigInit.Configure();
return base.CreateServiceHost(serviceType, baseAddresses);
}
}
I am configuring AutoMapper to map domain objects from / to the view model ones in a Asp.Net MVC5 application with Autofac 4.3, Autofac.Owin 4, Autofac.Mvc5 4, AutoFac.Mvc5.Owin 4 and AutoMapper 5.2.
I did decide to create one AutoMapper profile per domain entity like the followin leaving only the constructor with no parameters because I want to set the profile's name.
Note: this code is in assembly A.
public partial class DoctorsMappingProfile : Profile
{
#region Constructors
public DoctorsMappingProfile() : base(typeof(DoctorsMappingProfile).FullName)
{
// Code of mapping removed because it is not part of the problem
}
#endregion Constructors
}
To register AutoMapper and the profiles I followed this and this guides well explained by the user mpetito there and that it works as the user has checked here. My code in the Startup partial class is this:
Note1: this code is in assembly B which references assembly A.
Note2: I perfom assembly scanning of registered assemblies, then I pass as paramerter the array of scanned assemblies to the method RegisterAssemblyTypes of ContainerBuilder
public partial class Startup
{
#region Methods
public void ConfigureAutoFacContainer(IAppBuilder app)
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterFilterProvider();
builder.RegisterSource(new ViewRegistrationSource());
// TODO: Logger!
var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();
this.ConfigureAutoMapper(builder, assemblies);
// TODO: Modules!
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
app.UseAutofacMiddleware(container);
app.UseAutofacMvc();
}
private void ConfigureAutoMapper(ContainerBuilder builder, Assembly[] registeredAssemblies)
{
//register your profiles, or skip this if you don't want them in your container
builder.RegisterAssemblyTypes(registeredAssemblies).AssignableTo(typeof(Profile)).As<Profile>(); //.UsingConstructor(typeof(string));
//register your configuration as a single instance
builder.Register(ctx => new MapperConfiguration(cfg =>
{
//add your profiles (either resolve from container or however else you acquire them)
foreach (var profile in ctx.Resolve<IEnumerable<Profile>>())
{
cfg.AddProfile(profile);
}
})).AsSelf().SingleInstance();
//register your mapper
builder.Register(ctx => ctx.Resolve<MapperConfiguration>()
.CreateMapper(ctx.Resolve))
.As<IMapper>()
.InstancePerLifetimeScope();
}
#endregion Methods
}
When de application runs and try to resolve the profile the following exception happens:
Autofac.Core.DependencyResolutionException
None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'AutoMapper.Configuration.MapperConfigurationExpression+NamedProfile' can be invoked with the available services and parameters: Cannot resolve parameter 'System.String profileName' of constructor 'Void .ctor(System.String, System.Action`1[AutoMapper.IProfileExpression])'.
the line of the error is the foreach loop when the context attempts to resolve the Profile:
foreach (var profile in ctx.Resolve<IEnumerable<Profile>>())
I believe it its due to the Autofac default constructor location convention it is looking for the constructor with most parameters, which is in case of the AutoMapper.Profile class:
protected Profile(string profileName, Action<IProfileExpression> configurationAction)
{
}
To fix this I replaced the line that looks for profiles, the first line of code in in ConfigureAutoMapper, forcing it to use the constructor with no parameters:
builder.RegisterAssemblyTypes(registeredAssemblies).AssignableTo(typeof(Profile)).As<Profile>().UsingConstructor();
but still it does not work.
Solutions / workaround I have found:
If I replace the class Profile by my DoctorsMappingProfile in method ConfigureAutoMapper it works but doing this will invalidate the assembly scanning for profiles.
Finally I managed it to work by creating a base abstract class inheriting from AutoMapper.Profile and referencing it in my ConfigureAutoMapper method of the Startup class, this way I can perform assembly scanning for that class' descendants...
Note: this code is in Assembly C
public abstract class G2AutoMapperProfile : Profile
{
#region Constructors
protected G2AutoMapperProfile()
{
}
protected G2AutoMapperProfile(string profileName) : base(profileName)
{
}
#endregion Constructors
}
Note: And this code is in assembly A
public partial class DoctorsMappingProfile : G2AutoMapperProfile //Profile
{
#region Constructors
public DoctorsMappingProfile() : base(typeof(DoctorsMappingProfile).FullName)
{
//removed because it is not part of the problem
}
#endregion Constructors
}
and finally, this is the code of ConfigureAutoMapper in assembly B, that works:
private void ConfigureAutoMapper(ContainerBuilder builder, Assembly[] registeredAssemblies)
{
//register your profiles, or skip this if you don't want them in your container
builder.RegisterAssemblyTypes(registeredAssemblies).AssignableTo(typeof(G2AutoMapperProfile)).As<G2AutoMapperProfile>();
//register your configuration as a single instance
builder.Register(ctx => new MapperConfiguration(cfg =>
{
//add your profiles (either resolve from container or however else you acquire them)
foreach (var profile in ctx.Resolve<IEnumerable<G2AutoMapperProfile>>())
{
cfg.AddProfile(profile);
}
})).AsSelf().SingleInstance();
//register your mapper
builder.Register(ctx => ctx.Resolve<MapperConfiguration>()
.CreateMapper(ctx.Resolve))
.As<IMapper>()
.InstancePerLifetimeScope();
}
... but still I don't understand why the original code is not working for me.
First, there's no need for you to explicitly call the protected constructor of Profile in your DoctorsMappingProfile. The reason is that the profileName you pass is the one used by default by AutoMapper as we can see here.
Also, regarding the first way you try to register your profiles in the Autofac container:
builder
.RegisterAssemblyTypes(registeredAssemblies)
.AssignableTo(typeof(Profile))
.As<Profile>()
.UsingConstructor(typeof(string));
This means that you explicitly ask Autofac to create instance of your Profile classes by using the constructor that has exactly one parameter that is of type string. The problem with that is Autofac has no idea what string it has to use in the constructor.
It's also wrong since you created a parameter-less constructor which in turn calls the parameterized constructor.
I'm not sure what calling UsingConstructor with no parameters does, but in my opinion, you'd be better off leaving Autofac choose the best constructor for you, especially when the defaut parameter-less one is the one you want it to use.
My feeling is reinforced by looking at the solution/workaround you provided. What you effectively did is:
create a base class that mimics exactly what Profile has, that is, a default parameter-less constructor and a constructor that takes a string
register the profiles in the Autofac container by not using UsingConstructor
So I'm quite sure that by:
getting rid of your custom base class
having your profiles inherit from Profile again
registering your profiles without using UsingConstructor
you should be fine.
is there a way i can merge 2 autoface container into 1?
I mean that all registrations from both containers will be included in the new one
Alternatively it could be upgrade one of them with the other
For example:
public IContainer Merge(IContainer container1, IContainer container2)
{
return ???;
}
I've tried iterating over "container.ComponentRegistry.Registrations" and by using containerBuilder registering components and upgrading the second container but for some reason there were some conflicts
Autofac.Core.DependencyResolutionException : An exception was thrown while executing a resolve operation. See the InnerException for details. ---> The provided instance has already been used in an activation request. Did you combine a provided instance with non-root/single-instance lifetime/sharing? (See inner exception for details.)
----> System.InvalidOperationException : The provided instance has already been used in an activation request. Did you combine a provided instance with non-root/single-instance lifetime/sharing?
at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Container.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, ref Object instance)
at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Type serviceType, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.Resolve(IComponentContext context, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.Resolve(IComponentContext context)
at Bootstrap.Bootstrapper.CreatePersistentTimerAsyncExecuter(IContainer container)
at Bootstrap.Bootstrapper.Monitor(IContainer container)
at Bootstrap.Bootstrapper.Boot(Assembly[] assemblies)
at Soluto.Telemetry.Processing.Core.IntegrationTests.TelemetryProcessingBootstrapperTests.Boot_With2TelemetryHandlersInAssembly_ResolvesSubscriberWithItsHandlers() in TelemetryProcessingBootstrapperTests.cs: line 88
--InvalidOperationException
at Autofac.Core.Activators.ProvidedInstance.ProvidedInstanceActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
at Autofac.Core.Resolving.InstanceLookup.<Execute>b__0()
at Autofac.Core.Lifetime.LifetimeScope.GetOrCreateAndShare(Guid id, Func`1 creator)
at Autofac.Core.Resolving.InstanceLookup.Execute()
at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Resolving.ResolveOperation.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
Any Ideas ?
You can use a custom IRegistrationSource. This interface contains a RegistrationsFor method that return a list of IComponentRegistration for a specific IService
public class CrossContainerRegistrationSource : IRegistrationSource
{
public CrossContainerRegistrationSource(IComponentContext componentContext)
{
this._componentContext = componentContext;
}
private readonly IComponentContext _componentContext;
public Boolean IsAdapterForIndividualComponents
{
get
{
return false;
}
}
public IEnumerable<IComponentRegistration> RegistrationsFor(Service service,
Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
return this._componentContext.ComponentRegistry.RegistrationsFor(service);
}
}
You can use it like this :
container2.ComponentRegistry.AddRegistrationSource(new CrossContainerRegistrationSource(container1));
But be careful with this solution it may introduce issue when you use complex scope scenario.
Yes it is possible.
var existingContainer = new ContainerBuilder();
// Add registrations to existing container.
var newContainerBuilder = new ContainerBuilder();
// Add registrations to new container.
newContainterBuilder.Update(existingContainer);
The update method essentially merges the contents of the existingContainer into the newContainerBuilder.
EDIT - If using an IContainer
If you have already built your ContainerBuilder into an IContainer the Update method of the ContainerBuilder is able to accept an IContainer as input. But in this case the already built container will now contain all the registrations and the new ContainerBuilder can go out of scope.
var builtContainer = existingContainer.Build();
var newContainerBuilder = new ContainerBuilder();
// Add registrations to new container.
newContainterBuilder.Update(builtContainer);
It should be noted that Update() is marked obsolete in newer versions of Autofac and should not be used. See https://github.com/autofac/Autofac/issues/811#issuecomment-270246744 for more clarification.
Some of the recommendations listed to get around this issue is:
Pass Around ContainerBuilder Instead of IContainer
Add Registrations to Child Scopes
Use Lambdas
Use Modules
Use Conditional Registrations (new in 4.4.0)
Basically.. don't end up with 2 containers if possible.
Thanks for all the answers guys,
eventually i did something more specific for my needs:
public void Merge(this IContainer container1, IContainer container2)
{
var newBuilder = new ContainerBuilder();
newBuilder.RegisterInstance(container2.Resolve<ISomeService1>()).AsImplementedInterfaces();
newBuilder.RegisterInstance(container2.Resolve<ISomeService2>()).AsImplementedInterfaces();
newBuilder.Update(container1);
return container1;
}