Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

autofac: How to resolve collection of named types?

Tags:

c#

autofac

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?

like image 836
ikutsin Avatar asked Apr 25 '11 14:04

ikutsin


2 Answers

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.

like image 163
Nicholas Blumhardt Avatar answered Sep 19 '22 15:09

Nicholas Blumhardt


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>>>();
like image 28
Jim Bolla Avatar answered Sep 20 '22 15:09

Jim Bolla