How DI frameworks resolve dependency for same Interface with multiple configuration?

左心房为你撑大大i 提交于 2020-01-22 02:02:38

问题


Consider below code sample:

public interface IMyInterface
{
   void SetName(string name);
   string GetName();
}

public class MyInterfaceImplementor1 : IMyInterface
{
   protected string Name { set; get; }

   public void SetName(string name)
   {
      this.Name = name;
   }

   public virtual string GetName()
   {
      return this.Name;
   }
}

public class MyInterfaceImplementor2 : MyInterfaceImplementor1
{
   public override string GetName()
   {
      return String.Format("Hello! {0}", base.Name);
   }
}

And the DI configuration for this: (StructureMap code snippet provided)

ObjectFactory.Configure(x => 
{
   x.For<IMyInterface>()
    .Use<MyInterfaceImplementor1>();
});

ObjectFactory.Configure(x =>
{
   x.For<IMyInterface>()
    .Use<MyInterfaceImplementor2>();
});  

Say, in my code, at some point I am using MyInterfaceImplementor1 and at some other point, I use MyInterfaceImplementor2. My question is, how the DI framework (StructureMap or any other) will resolve the above configuration? Also, how will it determine, where to return an instance of MyInterfaceImplementor1 and when the same of MyInterfaceImplementor2? OR Am I doing something wrong here?


回答1:


In your case StructureMap will resolve MyInterfaceImplementor2. StructureMap stores all registrations but resolves the last one registered with Use() method.

Nomenclature

Plugin type - type that is to be injected, in most of the cases it is an interface but SM resolves also concrete types (even if you haven't registered them)

Plugged/concrete type - implementation of plugin type

c.For<IPluginType>().Use<ConcreteType>();

Generally in SM there are 2 types of manual registration. You can register dependencies with Add() and Use() method.

What is the difference?

  • Add() just registers concrete type in plugin family
  • Use() registers and sets it as default registration to be used when resolving plugin type

For Add() there is a convention that when you have only one registration then it will be resolved. In case when there is more than one registration container would throw StructureMapException when trying to resolve plugin type.

csprabala answer provides good explanation how multiple implementations should be registerd within StructureMap.

When I have multiple implementations of plugin type I always register the with Add() method and if there is a default implementation I register it with Use()




回答2:


How to map same interface to different ConcreteClasses with StructureMap?

Look at the above. Most DI frameworks use hints presented in the form of names/attributes to inject the appropriate concrete classes at run time.

Look at the below to

StructureMap: Choose concrete type of nested dependency




回答3:


In some cases it makes much sense to have multiple implementations of the same interface, but not in all cases. Always make sure that you are not violating the Liskov Substitution Principle (LSP). Here are two cases, one where the use of multiple implementations is okay, and one where it is not.

Say for instance you have an ILogger abstraction with FileLogger and SqlLogger implementations. In most cases you want to log stuff to the database, but in some parts of the system, you explicitly want to log to a file instead, because the log errors in this part of the system are picked up by a different system that has no access to the database. Although you should always ask yourself whether or not you're logging too much, as long as swapping the implementations doesn't affect the consumers of the ILogger abstraction, from perspective of the LSP, you are fine.

However, let's say your application communicates with two databases, that each have their own schema. On top of this you have an IUnitOfWork abstraction, that is used to communicate with the database. Based on this abstraction you implemented the OrdersUnitOfWork for communicating with the Orders database, and the CrmUnitOfWork to communicate with the CRM database. Obviously some parts of the system need to get the OrdersUnitOfWork to work, while others need the CrmUnitOfWork. If this is the case, you are breaking the LSP, since you can't simply swap the implementations without the consumers to notice. No, when supplying a consumer with the wrong implementation it will completely break, because the database schema's are completely different. This is a violation of the LSP.

So in the latter case, the problem is in the design of the application and can be fixed by giving each database its own abstraction. For instance: IOrdersUnitOfWork and ICrmUnitOfWork.

If you do this kind of registration, it completely depends on the DI library you use, whether it returns the first or the last. This is often very unintuitive, and this makes it very easy to make a configuration error because of this.

For that reason, the DI library Simple Injector does not allow these kind of registrations. The designers of Simple Injector find such API a design flaw as we explain here. Instead, Simple Injector forces you to make either a single registration for a given key (IMyInterface in your case) or register a collection of things. This prevents the confusion all together of what the library will return for you.

In case you need to determine the implementation to inject, based on its consumer, Simple Injector allows you to do context based injection. For instance:

container.Register<MyInterfaceImplementor1>();
container.Register<MyInterfaceImplementor2>();

container.RegisterWithContext<IMyInterface>(context =>
    context.ImplementationType.Namespace.Contains("Administrator")
        ? container.GetInstance<MyInterfaceImplementor1>()
        : container.GetInstance<MyInterfaceImplementor2>());

Here which implementation to inject is determined based on the namespace of the consumer. You can think of all kind of rules of course to determine what to inject.




回答4:


StructureMap will blow up with an exception because it will NOT try to guess which one you want if there's more than one registration -- and I stand by that decision to this day. Some of the other IoC containers will use either the first one registered or the last one registered.



来源:https://stackoverflow.com/questions/26544754/how-di-frameworks-resolve-dependency-for-same-interface-with-multiple-configurat

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