Autofac Factories with Named Bindings
I come from an IoC background using Ninject, however, after needing to create portable code between Windows and Xam.Mac I chose to switch my container to AutoFac (mostly due to NancyFx providing an official Bootstrapper).
One of the extensions I relied upon in the Ninject ecosystem was Ninject.Extensions.Factory whereby I could inject an abstract factory to construct different possible concrete instances of a class. An example of what I mean can be found here in the extension documentation.
I understand that Autofac can have such factories implemented as delegates using a slightly different structure, but I have no way to differ the factory construction parameters depending on what I needed.
First of all, is this possible with Autofac? Additionally, I feel as if this practice reintroducing tight-coupling back into the codebase albeit in a slightly roundabout way. Could this "limitation" be a blessing in a disguise in breaking a hidden anti-pattern?
I don't like and i never liked the factory naming convention you referenced here. In a previous project i was working on we replaced part of the Factory extensions to handle attributes instead. For example, you could have an IFactory
with a method Foo Create([Named] name)
. We also had other attributes like [MatchByType]
or [MatchByName]
. I like this approach much better because it is more verbose and requires less previous knowledge to understand what's going on.
I've come to like Autofac's delegate factories. However, my biggest concern is the same as with standard Ninject factories: it's not refactor safe as it's matching delegate parameter names to constructor parameter names (hence why we adapted ninject factories to match by type instead of parameter name by default). Creating a Factory extension for Autofac would not be that complicated - one could basically copy it from Ninjects implementation.
However, the cleanest approach for factories is probably as follows:
public class FooFactory : IFooFactory
{
private readonly IDependency1 dependency1;
private readonly IDependency2 dependency2;
public FooFactory(IDependency1 dependency1, IDependency2 dependency2)
{
this.dependency1 = dependency1;
this.dependency2 = dependency2
}
public IFoo Create(IParameter someParameter)
{
return new Foo(this.dependency1, this.dependency2, someParameter);
}
}
This is refactor safe. Furthermore, if you employ FxCop you'll also be warned if you have unused variables. However, you'll also have to consider whether this results in the correct "lifetime scopes" for the dependencies which are injected into the factory. Keeping things clean, this could introduce additional factories. Alternatively, you might choose to allow a tighter coupling to the DI container here to retrieve all dependencies directly from the container. If you're ever going to use Autofac's ILifetimescope
(or Owned<T>
) instead, you'll have tight coupling with the container anyway.
In the end I personally have chosen to stick with Autofac's delegate factories and use extensive spec testing to get some certainty that everything works as expected / safeguard against incomplete rename-refactorings.
EDIT: just had another issues with the "auto generated factories" today. Not the first time, too. It was an autofac delegate factory where the parameter did not match. I always found it not so nice that DI containers don't warn me if there's parameters which are not used. Usually that means there's an error in the code. So if i could choose, i'd like something like:
public IFoo Create(IParameter someParameter)
{
return new Foo(this.dependency1, this.dependency2, someParameter);
}
I think quite a few DI containers already support this syntax for constructor selection. Why not for late-resolution? An alternative would be to split resolution and binding up:
Bind<Foo>().ToConstructor(c => new Foo(c.Inject, c.Inject, c.FromParameter);
and then if the factory is missing a parameter, it could throw an exception. But it'd still need some kind of matching logic, would still not necessarily be refactor safe,.. etc.
I wanted to actually answer your first question, which I think was "how to request a named registration by a factory" ;-) As far as i know there's no way you can do this based on a (naming) convention or by attributes. Instead, you have to register a factory delegate which specifically does so.
(side-note: with Autofac, the basic-feature is named "keyed" services, with string-typed-key being a special case and called "named services" - see documentation)
Example:
// makes types FooLicious and FooMinable available as IFoo
// similar to ninject's Bind<IFoo>().To<FooLicious>().Named("delicious");
builder.RegisterType<FooLicious>().Named<IFoo>("delicious");
builder.RegisterType<FooMinable>().Named<IFoo>("abominable");
//factory delegate definition
public delegate IFoo FooFactory(string name);
builder.Register<FooFactory>(c =>
{
var context = c.Resolve<IComponentContext>();
return name =>
{
return context.ResolveNamed<IFoo>(name);
};
});
FooFactory factory = container.Resolve<FooFactory>();
IFoo = factory("delicious"); // returns a FooDelicious
Note: i really don't know why one has to retrieve an IComponentContext
from c
- which is an IComponentContext
itself. I think I've read this somewhere but I don't remember where. Maybe it's not needed after all...
Sadly Autofac's syntax needs getting used to and is not as straight-forward as Ninject's syntax...
Alternative: of course you could also use the DI container directly instead of a delegate-factory...
链接地址: http://www.djcxy.com/p/65670.html下一篇: 具有命名绑定的Autofac工厂