autofac: как разрешить коллекцию именованных типов?

#c# #autofac

#c# #autofac

Вопрос:

У меня есть куча экземпляров класса TaskParametes, зарегистрированных в контейнере, например:

 builder.Register(c => [some type instantiation]
    )).Named<TaskParameters>("someTask").InstancePerDependency();

builder.Register(c => [some type instantiation]
    )).Named<TaskParameters>("someOtherTask").InstancePerDependency();
  

Эти классы могут быть зарегистрированы в любом модуле приложения. Я хотел бы получить список доступных именованных экземпляров, чтобы отправить его клиенту, который должен создать экземпляр и выполнить его по имени.

Есть ли возможность получить список имен, фактически не создавая экземпляры типов? В настоящее время я изучаю ComponentRegistry IComponentContext, который я получаю из, var ctx = Container.Resolve<IComponentContext>(); , я на правильном пути?

Ответ №1:

Метаданные в данном случае более уместны, чем именование.

Для строго типизированного варианта определите интерфейс для хранения метаданных:

 public interface ITaskMetadata
{
    string Name { get; }
}
  

Затем свяжите метаданные во время сборки:

 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"));
  

( InstancePerDependency() Опущен, поскольку это поведение по умолчанию.)

Затем компонент, которому необходимо проверить имена, может зависеть от IEnumerable<Lazy<T,TMetadata>> вот так:

 class SomeComponent : ISomeComponent
{
    public SomeComponent(
        IEnumerable<Lazy<TaskParameters,ITaskMetadata>> parameters)
    {
        // Here the names can be examined without causing instantiation.
    }
}
  

При этом используются типы отношений, чтобы избежать необходимости искать что-либо в контейнере.

Обратите внимание, Lazy<,> тип взят из .NET 4. Для получения подробной информации о достижении этого в .NET 3.5 и альтернативном синтаксисе смотрите Autofac wiki.

Комментарии:

1. Проблема с метаданными заключается в том, что я не могу запросить их из контейнера (есть ResolveNamed и ResolveKeyed , но нет отправки по метаданным). Я также не могу найти расширение «Для», оно в каком-то специальном пространстве имен? (AutoFac версии 2.4.4.705)

2. Intellisense (ReSharper?) возникли проблемы с методом WithMetadata — он все равно должен скомпилироваться. Если вы хотите также разрешить эти обработчики из контейнера, пожалуйста, опубликуйте некоторые подробности этого сценария, в вашем вопросе говорится только о создании экземпляров путем делегирования клиентам. Для быстрого и грязного использования Named() может использоваться так же, как и с Metadata(). Приветствия!

Ответ №2:

Если имя службы важно для вашего приложения, возможно, это следует смоделировать в вашем коде. Например, у вас есть TaskParameters ; может быть, вы хотите что-то вроде:

 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(); }
}
  

И затем вы можете зарегистрировать дескрипторы для своих типов. Тогда вы могли бы легко вызвать

 var descriptors = container.Resolve<IEnumerable<Descriptor<TaskParameters>>>();
  

Ответ №3:

Я не нашел никакого решения вместо запроса контекста:

     var ctx = Container.Resolve<IComponentContext>();
    var taskNames = ctx.ComponentRegistry.Registrations
        .Select(c => c.Services.FirstOrDefault() as KeyedService)
        .Where(s => s != null amp;amp; s.ServiceType == typeof (TaskParameters))
        .Select(s => s.ServiceKey).ToList();
  

Кажется, что этот подход ничего не создает и не активирует.