Mvvmcross: Register multiple implementations of an interface

Created on 4 Jul 2019  路  8Comments  路  Source: MvvmCross/MvvmCross

馃殌 Feature Requests

Allow the built-in IoC provider to resolve arrays of interfaces.

Contextualize the feature

It would be a new feature of the IoC provider.

Describe the feature

In some cases, it would be useful to be able to register multiple implementors of an interface, which the IoC provider then could inject into classes who declare an array of such interface type as a parameter.

Example:

Suppose we have an image processing application which has a set of built-in filters which are applied to an image one-by-one. We declare an IImageFilter interface and then write multiple classes which implement this interface. We register these classes in the IoC container for that interface.

Now, suppose we have an ImageProcessing class with constructor as follows:

public ImageProcessing(IImageFilter[] filters)
{
   ...
}

The IoC container would be able to recognize this and inject the array of all registered classes which implement this interface.

Similar functionality is available in other IoC containers as well (off the top of my head Autofac for example) so it would be quite beneficial to have it in the default MvvmCross IoC provider as well.

Platforms affected (mark all that apply)

  • [x] :iphone: iOS
  • [x] :robot: Android
  • [x] :checkered_flag: WPF
  • [x] :earth_americas: UWP
  • [x] :apple: MacOS
  • [x] :tv: tvOS
  • [x] :monkey: Xamarin.Forms
feature

Most helpful comment

@Cheesebaron @MartinZikmund

  1. We could decorate dependency with attribute pointing at implementation that we want to resolve:

c# public ImageProcessing([Resolve("ImplTypeName")]IImageFilter imageFilter) { ... }

All 8 comments

If it is agreed that this feature would be useful, I would love to try implementing it.

I guess it could be a nice addition. However, we would need to think about the following scenarios.

  1. Having registered multiple IImageFilter, if you are not using IImageFilter[] as parameter in the ctor, which value will you get? The first registered, the last, or maybe that is configurable?

  2. How do we remove a specific instance of a IImageFilter from the container? Before you just registered a new one and it was overwritten.

@Cheesebaron

  1. For multi-registration, to match the current behavior of the container it would make sense to apply the last registered, so that it would not introduce a breaking change. Configurable solution makes sense also.

  2. For example Autofac just does not allow this and it is necessary to create a new container. The overwriting will still work for single-interface scenario, but it is quite problematic for the array. One option could be to return some kind of "token" by which it would be possible to unregister specific registration later.

@Cheesebaron @MartinZikmund

  1. We could decorate dependency with attribute pointing at implementation that we want to resolve:

c# public ImageProcessing([Resolve("ImplTypeName")]IImageFilter imageFilter) { ... }

This would be a nice addition. I have an issue with MediatR. Specifically I can't useINotificationHandler<> interface to handle INotification types without hacking. Since MediatR supports multiple types implementing same INotificationHandler<> and expects IEnumerable of INotificationHandler<> to be returned from IoC provider.

Doesn't this to some extent already work?

As @MartinZikmund writes: "We register these classes in the IoC container for that interface." Now if you don't register each implementation one by one but register the array, e.g. with RegisterType<IImageFilter[]>(AllMyFilters), then you get what you asked for, don't you?

This also has the benefit of already having answers for the difficult scenarios:

  1. If you're not using IImageFilter[] but IImageFilter, there's no type registered yet, unless you explicitly do so.
  2. Not really an issue. Redefine AllMyFilters or call RegisterType<IImageFilter[]>(SubsetOfFilters) again.

@dastbayoo you are right, I didn't realize this was possible. But I think it might still not be exactly as powerful - here you need to have all the instance at hand when registering - meaning they will have to be in memory for the whole lifetime of the app and will have to essentially be singletons. They will also be constructed while startup, which might also not be what you want. Finally, to get the concrete instances, you have to do the resolution yourself, instead of leaving it all up to the container for later

@MartinZikmund
It's true that I only made sure this works in the singleton case; however I was using LazyConstructAndRegisterSingleton(Func<TInterface>), which basically just calls RegisterSingleton(Func<TInterface>). I would assume the extension method wouldn't exist if the assumption were wrong that the constructor function is only applied when needed.

I haven't tested what RegisterType(Type t, Func<object> constructor) does, but given the similar signature, I would assume it also waits for construction until requested.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Cheesebaron picture Cheesebaron  路  4Comments

ueman picture ueman  路  4Comments

RonakPatel21 picture RonakPatel21  路  4Comments

ssepulveda picture ssepulveda  路  5Comments

erviem99 picture erviem99  路  3Comments