问题
One of our company's products is composed of many small web-applications and a windows service, a.k.a. components, each potentially residing in a different computer. One of them is a WebForms project, which serves as the configuration hub for all the others.
We are designing a feature now to expose general information of a component. Imagine a simple interface like this for instance:
public interface IStatistics
{
Statistics GetStatistics();
}
We want to use this same interface on all components, so this is centralized on a common, shared assembly. The implementation will initially be the same also, so it is in this same assembly, alongside the interface.
The idea then is to expose a Wcf service on each component, using both the implementation and interface on the common assembly. The implementation uses environmental classes that return different things depending on where they are running, like local machine time.
The problem I would like to solve elegantly is how to pass to the webform all implementations of every component, using the same interface.
We currently use Unity, but I suppose I would face the same problem with any other DI solution. I would like to inject 5 implementations of this same interface (one for each component), and be able to differentiate them by component (think a Dictionary<Component, IStatistics>
, where Component
is an Enum
). This is needed because there will be a dropdown on the page to select which component's information is visible, and the page would then call the correct implementation to retrieve the results.
I know I can use named registrations for all implementations and then inject them. Unfortunatelly, this would lead me to:
- Have 5 different parameters on the page, each pointing to a component
- Register each implementation on the container with a different name
- Explicitly register my webform on the container, with a custom InjectionConstructor specifying each registered interface, in the correct order, for the page injection method
I know Unity has a ResolveAll method and thus allows one to receive a IStatistics[]
or IEnumerable<IStatistics>
, but then I won't be able to differentiate them.
I think MEF solves this brilliantly with the concept of Metadata interfaces. Perhaps going MEF on this would be a way to solve this issue? I think it would be inappropriate here because these are wcf proxies we are talking about, I can't see how this would integrate with MEF at all.
Maybe using a factory would be a better strategy, but I fail to see how I'll inject the services on the factory also, so the problem would persist.
回答1:
I'm wondering, whether you could design the whole thing slightly different. I think what you're trying to do is a bit of against what IoC is about. If I understand you correctly you need five instances of the same type which you can somehow relate to five other objects of different types. 'IStatistics' is the interface, defined in a common assembly. The implementation, say Statistics
is in the same assembly. The five instances of different types are defined in their own, 'local', assemblies which are resolved by DI and not directly referenced.
Here is an idea which might be more straightforward and achieves what you need, while remaining maintainable and extendable, especially if you need different implementations for each component type at some point:

DefaultStatisticsProvider
can be used by components to implement the IStatisticsProvider
interface. There is no DI involved here, because the implementation is available in the common project.
public class DefaultStatisticsProvider : IStatisticsProvider
{
public Statistics GetStatistics()
{
var statistics = new Statistics();
// Generate statistics data
return statistics;
}
}
The components implement IStatisticProvider
directly and relay the methods to a private field of type DefaultStatisticsProvider
which they initialize in their constructor:
public class ComponentA : IStatisticsProvider
{
private readonly DefaultStatisticsProvider _statisticsProvider;
public ComponentA()
{
_statisticsProvider = new DefaultStatisticsProvider();
}
Statistics IStatisticsProvider.GetStatistics()
{
// You could change this implementation later to
// use a custom statistics provider
return _statisticsProvider.GetStatistics();
}
}
Now you can register your components as IStatisticsProvider
directly and don't have to maintain some kind of artificial lookup table which relates types to statistic provider instances, because your types are statistic providers, which kind of makes sense to me in a logical way, too. Something like (pseudo code):
Container.Register<ComponentA>().As<IStatisticsProvider>();
Container.Register<ComponentB>().As<IStatisticsProvider>();
so that
Container.ResolveAll<IStatisticsProvider>();
will give you
{ Instance of ComponentA, Instance of ComponentB }
Plus, as mentioned, if you at some point need to have custom implementations for providing statistics, there is no redesign required at all. You just change the implementation of GetStatistics()
in the component.
来源:https://stackoverflow.com/questions/19099933/passing-multiple-implementations-of-the-same-interface-using-di