autofac: How to resolve collection of named types?
I have a bunch of TaskParametes class instances registered in container, like:
builder.Register(c => [some type instantiation]
)).Named<TaskParameters>("someTask").InstancePerDependency();
builder.Register(c => [some type instantiation]
)).Named<TaskParameters>("someOtherTask").InstancePerDependency();
These classes can be registered in any module of the application. I'd like to get the list of available 开发者_如何学编程named instances to sent it to client, which should instantiate and execute it by name.
Is there an option to get the list of the names, without actually instantiating types?
Currently I'm digging ComponentRegistry of IComponentContext, which I get from, var ctx = Container.Resolve<IComponentContext>();
, am I on the right direction?
Metadata is more appropriate than naming in this case.
For the strongly-typed variant, define an interface to hold the metadata:
public interface ITaskMetadata
{
string Name { get; }
}
Then associate the metadata at build time:
builder.Register(c => [some type instantiation]))
.As<TaskParameters>()
.WithMetadata<ITaskMetadata>(m =>
m.For(tm => tm.Name, "someTask"));
builder.Register(c => [some type instantiation]))
.As<TaskParameters>()
.WithMetadata<ITaskMetadata>(m =>
m.For(tm => tm.Name, "someOtherTask"));
(The InstancePerDependency()
is omitted because it is the default behaviour.)
Then, the component that needs to examine the names can take a dependency on IEnumerable<Lazy<T,TMetadata>>
like so:
class SomeComponent : ISomeComponent
{
public SomeComponent(
IEnumerable<Lazy<TaskParameters,ITaskMetadata>> parameters)
{
// Here the names can be examined without causing instantiation.
}
}
This uses relationship types to avoid the need to look anything up in the container.
Note, the Lazy<,>
type is from .NET 4. For details on achieving this in .NET 3.5, and alternative syntax, see the Autofac wiki.
If the name of the service is important to your application, maybe that should be modeled into your code. For example, you have TaskParameters
; maybe you want something like:
public class Descriptor<T>
{
private readonly string _description;
private readonly Func<T> _create;
public Descriptor(string description, Func<T> create)
{
_description = description;
_create = create;
}
public string Description { get { return _description; } }
public T Create() { return _create(); }
}
And then you can register descriptors for your types. Then you could easily call
var descriptors = container.Resolve<IEnumerable<Descriptor<TaskParameters>>>();
I did'n find any solution rather than querying the context:
var ctx = Container.Resolve<IComponentContext>();
var taskNames = ctx.ComponentRegistry.Registrations
.Select(c => c.Services.FirstOrDefault() as KeyedService)
.Where(s => s != null && s.ServiceType == typeof (TaskParameters))
.Select(s => s.ServiceKey).ToList();
It seems that this approach doesn't instantiate nor activate anything.
精彩评论