We should identify the current performance and the features supported in MvvmCross. After that we can work on improving it.
Mvx.RegisterSingleton<>()Mvx.RegisterType<>()Mvx.RegisterType<>()Mvx.RegisterType<>()[MvxInject]Mvx.RegisterType<>()Mvx.Resolve<IMvxIoCProvider>().CreateChildContainer()Reference:
http://www.palmmedia.de/blog/2011/8/30/ioc-container-benchmark-performance-comparison
Somewhat related to #534
Is this a list of features that are supported? Because if it is, Property Injection is supported.
This is a list of features we could support. I was planning to add the current Mvx implementation to them if it is already supported and then check them off. That way we can track which already are there.
We could also add an unregister feature, it is impossible to Unregister anything currently.
I've researched a little about how other IoC libraries work and given that there are a lot of them that are already optimized, which would be the benefits on adding the features to the Mvx-IoC instead of using one of them e.g. https://github.com/seesharper/LightInject ?
By the way, Complex and Generics (if "generic dependency" means that the object has Generics type parameters) are also supported by the current IoC.
I'd like to be assigned to this issue. I'm adding Mvx's IoC to the benchmarks' project so that we can have a comparison to other IoCs and seeing what each feature exactly means.
About the Generics, it is not supported by the current IoC AFAIK, because it refers to a generic without type specification, e.g. IMyGenericInterface<>.
I've forked the IoCPerformance repository and added MvvmCross to it: fedemkr/IoCPerformance.
I've compared the current IoC against the fastest ones (LightInject, DryIoC and SimpleInjector), some in between (AutoFac, Catel) and the slowest one (NInject) and as it can be shown in the tables MvvmCross supports "Singleton" and "Complex" (Property injection is also supported but not yet added to the comparison) features.
Regarding Singleton Mvx have a much better performance than the slowest ones but it is around 8 times slower than LightInject (the fastest one) in singlethread and 4 times slower in multithread.
Now when talking about Complex Mvx is still faster than NInject and Catel but it's far more slow (around 50 times) than the fastest ones LightInject and DryIoC.
Lastly, in Prepare and register it's great, being almost equal to No container and faster than all the others.
So, I think that we should either see how the fastest containers are implemented and try to improve Mvx's one using those techniques or keep using it unless we have problems on performance and then replace it with another one in our project. In either case, I think we should implement the other two basic features (Transient and Combined) that come in handy in some situations when we want a more controlled lifetime.
What do you think?
We should try to make improvements by looking at the faster projects and implementing it in MvvmCross without making too many breaking changes because Ioc is fundamental for a lot of apps. I agree on that it would be good to add support for Transient and Combined, but also some other commonly used things like Property, Generics, IEnumerable and Conditional.
I might have made a mistake confusing Transient with Scoped lifetime. As soon as I can I'll check the code and update the repo. And after that I'll start implementing the other features or try improving the current ones.
Indeed, I made a mistake. Transient and Combined are also supported by Mvx; Transient being Mvx.RegisterType<...>(...) and Combined the combination of transient and singleton. I've updated the repo and the conclusions are similar to the ones explained before.
I'll start implementing the other features as soon as I can.
I think @fedemkr benchmarks are more complete, but dropping this blog post just in case:
I have an implementation for a Child Container - will prepare a PR
@fedemkr are you able to pick this up again and implement the other missing features?
Linked issues:
https://github.com/MvvmCross/MvvmCross/issues/534
https://github.com/MvvmCross/MvvmCross/issues/2077
Yes, I'll start working on this in the next few days
@fedemkr Maybe you can also update https://github.com/danielpalme/IocPerformance and make a pr to that.
I'd really like support for the IEnumerable feature.
Example (fictional): my code contains 3 services implementing ISocialMediaPoster
They all are registered as the interface ISocialMediaPoster and as lazy singletons.
I'd like to be able to do something like Mvx.Resolve<IEnumerable<ISocialMediaPoster>> and then get a list containing an instance of FacebookPoster, one of TwitterPoster and one of InstagramPoster.
Use case: logging
NB: I don't use the MVX logging framework, I use my own.
I have an interface called ILogServiceListener. The LogService itself just forwards all the logging to all the listeners and the listeners themselves decide how they log something. Similar to serilog and it sinks, but driven by IoC.
As I'm in the process of updating https://github.com/autofac/Autofac.Extras.MvvmCross, I ran into the following issue: https://github.com/autofac/Autofac.Extras.MvvmCross/issues/8
Basically, Autofac is dropping support for adding new registrations to an existing Container. Although MvvmCross should not have to accomodate to any specific IoC provider, I do think it's a cleaner design to separate service registration from service resolution. I.e., it would be better to have a MvvmCross service collection to which we can add service registrations, and build a service provider from that.
That would also follow the design that .NET Core's Dependency Injection uses, in addition to what Autofac seems to do.
Hello!
Actually, I guess it is quite possible to ease-in the feature of resolving an
IEnumerable<TInterface>
in mvvmcross.
We did that by wrapping the MvxIocProvider and adding additional registration methods
RegisterAdditional....
What we do is forwarding the registration call to the original MvxIocProvider.
And additionally, we track all registrations for the same interface type in a
Dictionary<Type, IList<Func<object>>> _iEnumerableFactories
Then we only need to additionally register an IEnumerable
_ioc.RegisterType<IEnumerable<TInterface>>(() => _iEnumerableFactories[t].Select(f => f()).Cast<TInterface>().ToArray());
The full implementation of ExtendedMvxIocProvider looks as follows and works like a charm
/// <summary>
/// Wraps the existing MvxIocProvider and provides additional registration methods, that allow to
/// *not* override existing implementations, but add them alongside
/// so that they can eventually be resolved as IEnumerable<TInterface>
/// </summary>
public class ExtendedMvxIocProvider : IMvxIoCProvider
{
private readonly IMvxIoCProvider _ioc = null;
public ExtendedMvxIocProvider(IMvxIocOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
_ioc = MvxIoCProvider.Initialize(options);
}
private readonly Dictionary<Type, IList<Func<object>>> _iEnumerableFactories = new Dictionary<Type, IList<Func<object>>>();
public void RegisterAdditionalType<TTInterface, TImplementation>() where TTInterface : class where TImplementation : class, TTInterface
{
_ioc.RegisterType<TTInterface, TImplementation>();
var t = typeof(TTInterface);
if (!_iEnumerableFactories.TryGetValue(t, out var factories))
{
factories = new List<Func<object>>();
_iEnumerableFactories.Add(t, factories);
}
// HACK: in order to NOT override an existing service in MvvmCross and still be able to directly resovle the concrete implementation
// and return it in an IEnuemrable<tInterface>, we need to register the implementation as itself and resovle it later on
_ioc.RegisterType<TImplementation, TImplementation>();
factories.Add(Resolve<TImplementation>);
// finally register a factory which uses the cached registrations to actually resolve an IEnumerable of tInterface :-)
_ioc.RegisterType<IEnumerable<TTInterface>>(() => _iEnumerableFactories[t].Select(f => f()).Cast<TTInterface>().ToArray());
}
public void RegisterAdditionalType<TInterface>(Func<TInterface> constructor) where TInterface : class
{
_ioc.RegisterType(constructor);
var t = typeof(TInterface);
if (!_iEnumerableFactories.TryGetValue(t, out var factories))
{
factories = new List<Func<object>>();
_iEnumerableFactories.Add(t, factories);
}
factories.Add(Resolve<TInterface>);
// finally register a factory which uses the cached registrations to actually resolve an IEnumerable of tInterface :-)
_ioc.RegisterType<IEnumerable<TInterface>>(() => _iEnumerableFactories[t].Select(f => f()).Cast<TInterface>().ToArray());
}
public void RegisterAdditionalType(Type tInterface, Func<object> constructor)
{
_ioc.RegisterType(tInterface, constructor);
if (!_iEnumerableFactories.TryGetValue(tInterface, out var factories))
{
factories = new List<Func<object>>();
_iEnumerableFactories.Add(tInterface, factories);
}
factories.Add(constructor);
var enumType = typeof(IEnumerable<>).MakeGenericType(tInterface);
// finally register a factory which uses the cached registrations to actually resolve an IEnumerable of tInterface :-)
_ioc.RegisterType(enumType, () => _iEnumerableFactories[tInterface].Select(f => f()).CreateEnumerable(tInterface));
}
public void RegisterAdditionalType(Type tInterface, Type tImplementation)
{
_ioc.RegisterType(tInterface, tImplementation);
if (!_iEnumerableFactories.TryGetValue(tInterface, out var factories))
{
factories = new List<Func<object>>();
_iEnumerableFactories.Add(tInterface, factories);
}
// HACK: in order to NOT override an existing service in MvvmCross and still be able to directly resovle the concrete implementation
// and return it in an IEnuemrable<tInterface>, we need to register the implementation as itself and resovle it later on
_ioc.RegisterType(tImplementation, tImplementation);
factories.Add(() => Resolve(tImplementation));
var enumType = typeof(IEnumerable<>).MakeGenericType(tInterface);
// finally register a factory which uses the cached registrations to actually resolve an IEnumerable of tInterface :-)
_ioc.RegisterType(enumType, () => _iEnumerableFactories[tInterface].Select(f => f()).CreateEnumerable(tInterface));
}
public void RegisterAdditionalSingleton<TInterface>(TInterface theObject) where TInterface : class
{
_ioc.RegisterSingleton(theObject);
var t = typeof(TInterface);
if (!_iEnumerableFactories.TryGetValue(t, out var factories))
{
factories = new List<Func<object>>();
_iEnumerableFactories.Add(t, factories);
}
factories.Add(() => theObject);
// finally register a factory which uses the cached registrations to actually resolve an IEnumerable of tInterface :-)
_ioc.RegisterType<IEnumerable<TInterface>>(() => _iEnumerableFactories[t].Select(f => f()).Cast<TInterface>().ToArray());
}
public void RegisterAdditionalSingleton(Type tInterface, object theObject)
{
_ioc.RegisterSingleton(tInterface, theObject);
var t = tInterface;
if (!_iEnumerableFactories.TryGetValue(t, out var factories))
{
factories = new List<Func<object>>();
_iEnumerableFactories.Add(t, factories);
}
factories.Add(() => theObject);
var enumType = typeof(IEnumerable<>).MakeGenericType(t);
// finally register a factory which uses the cached registrations to actually resolve an IEnumerable of tInterface :-)
_ioc.RegisterType(enumType, () => _iEnumerableFactories[t].Select(f => f()).CreateEnumerable(tInterface));
}
public void RegisterAdditionalSingleton<TInterface>(Func<TInterface> theConstructor) where TInterface : class
{
_ioc.RegisterSingleton(theConstructor);
var t = typeof(TInterface);
if (!_iEnumerableFactories.TryGetValue(t, out var factories))
{
factories = new List<Func<object>>();
_iEnumerableFactories.Add(t, factories);
}
factories.Add(Resolve<TInterface>);
// finally register a factory which uses the cached registrations to actually resolve an IEnumerable of tInterface :-)
_ioc.RegisterType<IEnumerable<TInterface>>(() => _iEnumerableFactories[t].Select(f => f()).Cast<TInterface>().ToArray());
}
public void RegisterAdditionalSingleton(Type tInterface, Func<object> theConstructor)
{
_ioc.RegisterSingleton(tInterface, theConstructor);
var t = tInterface;
if (!_iEnumerableFactories.TryGetValue(t, out var factories))
{
factories = new List<Func<object>>();
_iEnumerableFactories.Add(t, factories);
}
factories.Add(theConstructor);
var enumType = typeof(IEnumerable<>).MakeGenericType(t);
// finally register a factory which uses the cached registrations to actually resolve an IEnumerable of tInterface :-)
_ioc.RegisterType(enumType, () => _iEnumerableFactories[t].Select(f => f()).CreateEnumerable(tInterface));
}
#region IMvxIocProvider
public bool CanResolve<T>() where T : class
{
return _ioc.CanResolve<T>();
}
public bool CanResolve(Type type)
{
return _ioc.CanResolve(type);
}
public T Resolve<T>() where T : class
{
return _ioc.Resolve<T>();
}
public object Resolve(Type type)
{
return _ioc.Resolve(type);
}
public bool TryResolve<T>(out T resolved) where T : class
{
return _ioc.TryResolve(out resolved);
}
public bool TryResolve(Type type, out object resolved)
{
return _ioc.TryResolve(type, out resolved);
}
public T Create<T>() where T : class
{
return _ioc.Create<T>();
}
public object Create(Type type)
{
return _ioc.Create(type);
}
public T GetSingleton<T>() where T : class
{
return _ioc.GetSingleton<T>();
}
public object GetSingleton(Type type)
{
return _ioc.GetSingleton(type);
}
public void RegisterType<tInterface, TTo>() where tInterface : class where TTo : class, tInterface
{
_ioc.RegisterType<tInterface, TTo>();
}
public void RegisterType<TInterface>(Func<TInterface> constructor) where TInterface : class
{
_ioc.RegisterType(constructor);
}
public void RegisterType(Type t, Func<object> constructor)
{
_ioc.RegisterType(t, constructor);
}
public void RegisterType(Type tInterface, Type tTo)
{
_ioc.RegisterType(tInterface, tTo);
}
public void RegisterSingleton<TInterface>(TInterface theObject) where TInterface : class
{
_ioc.RegisterSingleton(theObject);
}
public void RegisterSingleton(Type tInterface, object theObject)
{
_ioc.RegisterSingleton(tInterface, theObject);
}
public void RegisterSingleton<TInterface>(Func<TInterface> theConstructor) where TInterface : class
{
_ioc.RegisterSingleton(theConstructor);
}
public void RegisterSingleton(Type tInterface, Func<object> theConstructor)
{
_ioc.RegisterSingleton(tInterface, theConstructor);
}
public T IoCConstruct<T>() where T : class
{
return _ioc.IoCConstruct<T>();
}
public T IoCConstruct<T>(IDictionary<string, object> arguments) where T : class
{
return _ioc.IoCConstruct<T>(arguments);
}
public T IoCConstruct<T>(object arguments) where T : class
{
return _ioc.IoCConstruct<T>(arguments);
}
public T IoCConstruct<T>(params object[] arguments) where T : class
{
return _ioc.IoCConstruct<T>(arguments);
}
public object IoCConstruct(Type type)
{
return _ioc.IoCConstruct(type);
}
public object IoCConstruct(Type type, IDictionary<string, object> arguments)
{
return _ioc.IoCConstruct(type, arguments);
}
public object IoCConstruct(Type type, object arguments)
{
return _ioc.IoCConstruct(type, arguments);
}
public object IoCConstruct(Type type, params object[] arguments)
{
return _ioc.IoCConstruct(type, arguments);
}
public void CallbackWhenRegistered<T>(Action action)
{
_ioc.CallbackWhenRegistered<T>(action);
}
public void CallbackWhenRegistered(Type type, Action action)
{
_ioc.CallbackWhenRegistered(type, action);
}
public IMvxIoCProvider CreateChildContainer()
{
return _ioc.CreateChildContainer();
}
#endregion
}
public static class ExtendedMvxIocProviderExtensions
{
public static object CreateEnumerable(this IEnumerable<object> items, Type tInterface)
{
var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(tInterface));
foreach (var item in items)
list.Add(item);
return list;
}
}
What do you think of this?
As I'm in the process of updating https://github.com/autofac/Autofac.Extras.MvvmCross, I ran into the following issue: autofac/Autofac.Extras.MvvmCross#8
Basically, Autofac is dropping support for adding new registrations to an existing Container. Although MvvmCross should not have to accomodate to any specific IoC provider, I do think it's a cleaner design to separate service registration from service resolution. I.e., it would be better to have a MvvmCross service collection to which we can add service registrations, and build a service provider from that.
That would also follow the design that .NET Core's Dependency Injection uses, in addition to what Autofac seems to do.
Same issue when I tried to create an adapter for SimpleInjector.
They lock the container, preventing new registrations from being added after the first call to Resolve.
See https://simpleinjector.readthedocs.io/en/latest/decisions.html#container-is-locked
Would be nice to see MvvmCross complying with these best practises 馃槃
Also, i'm +1 for the support of registering collections. My use case is that I have a set of event handlers that are registered in the container for a specific type of event. Then when an event is to be published, I resolve all event handlers for the type of event and call their Handle method.
Ran into a few other use cases as well where having the ability to resolve an IEnumerable would have been beneficial 馃槂
Most helpful comment
I'd really like support for the IEnumerable feature.
Example (fictional): my code contains 3 services implementing ISocialMediaPoster
They all are registered as the interface ISocialMediaPoster and as lazy singletons.
I'd like to be able to do something like
Mvx.Resolve<IEnumerable<ISocialMediaPoster>>and then get a list containing an instance of FacebookPoster, one of TwitterPoster and one of InstagramPoster.Use case: logging
NB: I don't use the MVX logging framework, I use my own.
I have an interface called
ILogServiceListener. The LogService itself just forwards all the logging to all the listeners and the listeners themselves decide how they log something. Similar to serilog and it sinks, but driven by IoC.