Reputation: 2098
Say I have the following registration in AutoFac (simplified):
Object A-->Object B-->Object C-->Object D
Now suppose that I want to create a second Object A (let's call it Object P) that is a virtual clone of Object A, save for one difference:
Object A-->Object B-->Object F-->Object D
Is there a way to take an existing AutoFac object and clone it in this manner? The object graph I'm working with in "real life" is many orders of magnitude more complex than this example, and I'd rather not duplicate a bunch of registration logic if not necessary.
Upvotes: 0
Views: 603
Reputation: 23909
While you could probably get sort of crazy/fancy using component metadata to get this to happen, the most straightforward way to get this working is to use nested lifetime scopes and override registrations.
When you begin a new lifetime scope, you can pass in a lambda expression that lets you add new registrations on the fly just to that scope. It's at this time you'll override the "Object C" registration with "Object F."
Here's a working example. First, let's define some interfaces and implementations. You'll notice the third service in the hierarchy, IThird
, has two implementations - ObjectC
and ObjectF
- to match your question:
public interface IFirst
{
ISecond Child { get; }
void WriteHierarchy();
}
public interface ISecond
{
IThird Child { get; }
}
public interface IThird
{
IFourth Child { get; }
}
public interface IFourth
{
}
public class ObjectA : IFirst
{
public ObjectA(ISecond child)
{
this.Child = child;
}
public ISecond Child { get; private set; }
public void WriteHierarchy()
{
Console.WriteLine("{0} -> {1} -> {2} -> {3}",
this.GetType().Name,
this.Child.GetType().Name,
this.Child.Child.GetType().Name,
this.Child.Child.Child.GetType().Name);
}
}
public class ObjectB : ISecond
{
public ObjectB(IThird child)
{
this.Child = child;
}
public IThird Child { get; private set; }
}
public class ObjectC : IThird
{
public ObjectC(IFourth child)
{
this.Child = child;
}
public IFourth Child { get; private set; }
}
public class ObjectF : IThird
{
public ObjectF(IFourth child)
{
this.Child = child;
}
public IFourth Child { get; private set; }
}
public class ObjectD : IFourth
{
}
With this, you can see that what we'll do is resolve that top-level item, an IFirst
, and we'll write out the hierarchy.
Here's the actual working code that shows the "swap" of the third item in the hierarchy:
var builder = new ContainerBuilder();
builder.RegisterType<ObjectA>().As<IFirst>();
builder.RegisterType<ObjectB>().As<ISecond>();
builder.RegisterType<ObjectC>().As<IThird>();
builder.RegisterType<ObjectD>().As<IFourth>();
var container = builder.Build();
// It's always good to use lifetime scopes and not
// resolve directly from a container. You can see
// here that I'm not doing anything special - this
// lifetime scope will have the "default registrations."
using(var scope = container.BeginLifetimeScope())
{
// This will print:
// ObjectA -> ObjectB -> ObjectC -> ObjectD
var obj = scope.Resolve<IFirst>();
obj.WriteHierarchy();
}
// You can pass override registrations when you begin
// a new lifetime scope. In this case, I'm overriding
// the one service type deep in the hierarchy.
using(var scope = container.BeginLifetimeScope(
b => b.RegisterType<ObjectF>().As<IThird>()))
{
// This will print:
// ObjectA -> ObjectB -> ObjectF -> ObjectD
var obj = scope.Resolve<IFirst>();
obj.WriteHierarchy();
}
Upvotes: 2