简体   繁体   中英

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM