Factory adapters provide the instantiation features of the container to managed components without exposing the container itself to them.
If type T
is registered with the container, Autofac will :doc:`automatically resolve dependencies <../resolve/relationships>` on Func<T>
as factories that create T
instances through the container.
Lifetime scopes are respected using delegate factories as well as when using Func<T>
or the parameterized Func<X,Y,T>
relationships. If you register an object as InstancePerDependency()
and call the delegate factory multiple times, you'll get a new instance each time. However, if you register an object as SingleInstance()
and call the delegate factory to resolve the object more than once, you will get the same object instance every time regardless of the different parameters you pass in. Just passing different parameters will not break the respect for the lifetime scope.
public class Shareholding
{
public delegate Shareholding Factory(string symbol, uint holding);
public Shareholding(string symbol, uint holding)
{
Symbol = symbol;
Holding = holding;
}
public string Symbol { get; private set; }
public uint Holding { get; set; }
}
The Shareholding
class declares a constructor, but also provides a delegate type that can be used to create Shareholdings
indirectly.
Autofac can make use of this to automatically generate a factory that can be accessed through the container:
var builder = new ContainerBuilder();
builder.RegisterType<Shareholding>();
var container = builder.Build();
var shareholdingFactory = container.Resolve<Shareholding.Factory>();
var shareholding = shareholdingFactory.Invoke("ABC", 1234);
The factory is a standard delegate that can be called with Invoke()
, as above, or with the function syntax shareholdingFactory("ABC", 1234)
.
By default, Autofac matches the parameters of the delegate to the parameters of the constructor by name. If you use the generic Func types, Autofac will switch to matching parameters by type.
Other components can use the factory:
public class Portfolio
{
Shareholding.Factory ShareholdingFactory { get; set; }
IList<Shareholding> _holdings = new List<Shareholding>();
public Portfolio(Shareholding.Factory shareholdingFactory)
{
ShareholdingFactory = shareholdingFactory;
}
public void Add(string symbol, uint holding)
{
_holdings.Add(ShareholdingFactory(symbol, holding));
}
}
To wire this up, the Portfolio
class would be registered with the container before building using:
builder.RegisterType<Portfolio>();
The components can be used by requesting an instance of Portfolio
from the container:
var portfolio = container.Resolve<Portfolio>();
portfolio.Add("DEF", 4324);
:doc:`Autofac supports the use <../resolve/relationships>` of Func<T>
delegates in addition to hand-coded delegates. Func<T>
parameters are matched by type rather than by name.
Imagine a remote stock quoting service:
public interface IQuoteService
{
decimal GetQuote(string symbol);
}
We can add a value
member to the Shareholding
class that makes use of the service:
public class Shareholding
{
public delegate Shareholding Factory(string symbol, uint holding);
IQuoteService QuoteService { get; set; }
public Shareholding(string symbol, uint holding, IQuoteService quoteService)
{
QuoteService = quoteService;
...
}
public decimal Value
{
get
{
return QuoteService.GetQuote(Symbol) * Holding;
}
}
// ...
}
An implementor of IQuoteService
can be registered through the container:
builder.RegisterType<WebQuoteService>().As<IQuoteService>();
The Shareholding
instances will now be wired up correctly, but note: the signature of Shareholding.Factory
doesn't change! Autofac will transparently add the extra parameter to the Shareholding
constructor when a factory delegate is called.
This means that Portfolio
can take advantage of the Shareholding.Value
property without knowing that a quote service is involved at all.
public class Portfolio
{
public decimal Value
{
get
{
return _holdings.Aggregate(0m, (a, e) => a + e.Value);
}
}
// ...
}
In a desktop (i.e. stateful) application, when using disposable components, make sure to create nested lifetime scopes for units of work, so that the nested scope can dispose the items created by the factories within it.