Unity .NET: List of dependencies

允我心安 提交于 2019-12-04 03:41:56

It is possible but you need to apply some workarounds. First you need to register each IParser with a name in the Unity Container. Second you need to register a mapping from IParser[] to IEnumerable<IParser> in the container. Otherwise the container cannot inject the parsers to the constructor. Here´s how i have done it before.

IUnityContainer container = new UnityContainer();

container.RegisterType<IParser, SuperParser>("SuperParser");
container.RegisterType<IParser, DefaultParser>("DefaultParser");
container.RegisterType<IParser, BasicParser>("BasicParser");
container.RegisterType<IEnumerable<IParser>, IParser[]>();
container.RegisterType<Crawler>();

Crawler crawler = container.Resolve<Crawler>();

I have discarded this solution by introducing a factory, that encapsulates unity to construct the needed types. Here´s how i would do it in your case.

public interface IParserFactory{
  IEnumerable<IParser> BuildParsers();
}

public class UnityParserFactory : IParserFactory {
  private IUnityContainer _container;

  public UnityParserFactory(IUnityContainer container){
    _container = container;
  }

  public IEnumerable<IParser> BuildParsers() {
    return _container.ResolveAll<IParser>();
  }
}

public class Crawler {
  public Crawler(IParserFactory parserFactory) {
     // init here...
  }
}

With this you can register the types as follows:

IUnityContainer container = new UnityContainer();

container.RegisterType<IParser, SuperParser>();
container.RegisterType<IParser, DefaultParser>();
container.RegisterType<IParser, BasicParser>();
container.RegisterType<IParserFactory, UnityParserFactory>();

Crawler crawler = container.Resolve<Crawler>();

Not that I'm saying it's wrong but it seems like you are trying to solve a plugin model that you could easily manage with the use of MEF instead. Hints would be inherited Export from the interface and then do ImportMany when you need the parsers.

There are ways to achieve this in Unity. For example,

http://sharpsnmplib.codeplex.com/SourceControl/changeset/view/5497af31d15e#snmpd%2fapp.config

    <register type="UserRegistry">
      <lifetime type="singleton" />
      <constructor>
        <param name="users" dependencyType="User[]" />
      </constructor>
    </register>

This constructor requires an array of User objects, and all such objects defined in this container are injected by Unity into it when the UserRegistry object is created.

As a matter of fact, I don't know any container that does not support this.

However, as a general advice, you should prevent injecting lists of services into consumers if you can, by wrapping that list in a composite, and inject that composite into consumers. Not wrapping the list in a composite would clutter the application with extra foreach loops or what ever you need to do to process that list of dependencies. While this doesn't seem bad, the consumers of these dependencies shouldn't care. But worse, when we want to change the way the list of services is handled, we will have to go through the complete application, which is a violation of the DRY principle.

This advice does not hold when the consumer (in your case the Crawler) is part of the Composition Root instead of the application itself. Further more, when the application only has a single consumer that takes this dependency, it might not be that big of deal.

yes you can.. you can look at dependency injectors. I am a big fan of the Autofac project.

Another option is Ninject,

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!