From 69073af45ad6a631126ec33162f5d60881c8f30d Mon Sep 17 00:00:00 2001 From: David Britch Date: Tue, 16 Jan 2018 14:06:45 +0000 Subject: [PATCH 1/3] Replaced Autofac with TinyIoC on iOS and Android. --- .../eShopOnContainers.Core/App.xaml.cs | 8 +- .../eShopOnContainers.Core/Helpers/TinyIoC.cs | 4303 +++++++++++++++++ .../ViewModels/Base/ViewModelLocator.cs | 185 +- .../ViewModels/SettingsViewModel.cs | 52 +- .../eShopOnContainers.Core.csproj | 1 - .../eShopOnContainers.Droid.csproj | 3 - .../eShopOnContainers.Droid/packages.config | 1 - .../eShopOnContainers.TestRunner.Droid.csproj | 3 - .../packages.config | 1 - .../eShopOnContainers.TestRunner.iOS.csproj | 3 - .../packages.config | 1 - .../eShopOnContainers.iOS.csproj | 24 +- .../eShopOnContainers.iOS/packages.config | 8 +- 13 files changed, 4454 insertions(+), 139 deletions(-) create mode 100755 src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/TinyIoC.cs diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml.cs index 61b51eb99..5b22b5a6e 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml.cs @@ -14,14 +14,11 @@ namespace eShopOnContainers { public partial class App : Application { - public bool UseMockServices { get; set; } - public App() { InitializeComponent(); InitApp(); - if (Device.RuntimePlatform == Device.UWP) { InitNavigation(); @@ -30,8 +27,9 @@ namespace eShopOnContainers private void InitApp() { - UseMockServices = Settings.UseMocks; - ViewModelLocator.RegisterDependencies(UseMockServices); + bool useMockServices = Settings.UseMocks; + if (!useMockServices) + ViewModelLocator.UpdateDependencies(useMockServices); } private Task InitNavigation() diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/TinyIoC.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/TinyIoC.cs new file mode 100755 index 000000000..b161e42eb --- /dev/null +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/TinyIoC.cs @@ -0,0 +1,4303 @@ +//=============================================================================== +// TinyIoC +// +// An easy to use, hassle free, Inversion of Control Container for small projects +// and beginners alike. +// +// https://github.com/grumpydev/TinyIoC +//=============================================================================== +// Copyright © Steven Robbins. All rights reserved. +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT +// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE. +//=============================================================================== + +#region Preprocessor Directives +// Uncomment this line if you want the container to automatically +// register the TinyMessenger messenger/event aggregator +//#define TINYMESSENGER + +// Uncomment this line if you want to internalize this library +//#define TINYIOC_INTERNAL + +// Uncomment this line if you want to target PCL. +//#define PORTABLE + +// Preprocessor directives for enabling/disabling functionality +// depending on platform features. If the platform has an appropriate +// #DEFINE then these should be set automatically below. +#define EXPRESSIONS + +// Platform supports System.Linq.Expressions +#define COMPILED_EXPRESSIONS // Platform supports compiling expressions +#define APPDOMAIN_GETASSEMBLIES // Platform supports getting all assemblies from the AppDomain object +#define UNBOUND_GENERICS_GETCONSTRUCTORS // Platform supports GetConstructors on unbound generic types +#define GETPARAMETERS_OPEN_GENERICS // Platform supports GetParameters on open generics +#define RESOLVE_OPEN_GENERICS // Platform supports resolving open generics +#define READER_WRITER_LOCK_SLIM // Platform supports ReaderWriterLockSlim +#define SERIALIZABLE // Platform supports SerializableAttribute/SerializationInfo/StreamingContext + +#if PORTABLE +#undef APPDOMAIN_GETASSEMBLIES +#undef COMPILED_EXPRESSIONS +#undef READER_WRITER_LOCK_SLIM +#undef SERIALIZABLE +#endif + +#if NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 +#undef COMPILED_EXPRESSIONS +#undef READER_WRITER_LOCK_SLIM +#endif + +#if NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 +#undef APPDOMAIN_GETASSEMBLIES +#endif + +#if NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 || NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6 +#undef SERIALIZABLE +#endif + +// CompactFramework / Windows Phone 7 +// By default does not support System.Linq.Expressions. +// AppDomain object does not support enumerating all assemblies in the app domain. +#if PocketPC || WINDOWS_PHONE +#undef EXPRESSIONS +#undef COMPILED_EXPRESSIONS +#undef APPDOMAIN_GETASSEMBLIES +#undef UNBOUND_GENERICS_GETCONSTRUCTORS +#endif + +// PocketPC has a bizarre limitation on enumerating parameters on unbound generic methods. +// We need to use a slower workaround in that case. +#if PocketPC +#undef GETPARAMETERS_OPEN_GENERICS +#undef RESOLVE_OPEN_GENERICS +#undef READER_WRITER_LOCK_SLIM +#endif + +#if SILVERLIGHT +#undef APPDOMAIN_GETASSEMBLIES +#endif + +#if NETFX_CORE +#undef APPDOMAIN_GETASSEMBLIES +#undef RESOLVE_OPEN_GENERICS +#endif + +#if COMPILED_EXPRESSIONS +#define USE_OBJECT_CONSTRUCTOR +#endif + +#endregion +#if SERIALIZABLE +using System.Runtime.Serialization; +#endif + +namespace TinyIoC +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Linq; + using System.Reflection; + +#if EXPRESSIONS + using System.Linq.Expressions; + using System.Threading; + +#endif + +#if NETFX_CORE + using System.Threading.Tasks; + using Windows.Storage.Search; + using Windows.Storage; + using Windows.UI.Xaml.Shapes; +#endif + + #region SafeDictionary +#if READER_WRITER_LOCK_SLIM +#if TINYIOC_INTERNAL + internal +#else + public +#endif + class SafeDictionary : IDisposable + { + private readonly ReaderWriterLockSlim _padlock = new ReaderWriterLockSlim(); + private readonly Dictionary _Dictionary = new Dictionary(); + + public TValue this[TKey key] + { + set + { + _padlock.EnterWriteLock(); + + try + { + TValue current; + if (_Dictionary.TryGetValue(key, out current)) + { + var disposable = current as IDisposable; + + if (disposable != null) + disposable.Dispose(); + } + + _Dictionary[key] = value; + } + finally + { + _padlock.ExitWriteLock(); + } + } + } + + public bool TryGetValue(TKey key, out TValue value) + { + _padlock.EnterReadLock(); + try + { + return _Dictionary.TryGetValue(key, out value); + } + finally + { + _padlock.ExitReadLock(); + } + } + + public bool Remove(TKey key) + { + _padlock.EnterWriteLock(); + try + { + return _Dictionary.Remove(key); + } + finally + { + _padlock.ExitWriteLock(); + } + } + + public void Clear() + { + _padlock.EnterWriteLock(); + try + { + _Dictionary.Clear(); + } + finally + { + _padlock.ExitWriteLock(); + } + } + + public IEnumerable Keys + { + get + { + _padlock.EnterReadLock(); + try + { + return new List(_Dictionary.Keys); + } + finally + { + _padlock.ExitReadLock(); + } + } + } + + #region IDisposable Members + + public void Dispose() + { + _padlock.EnterWriteLock(); + + try + { + var disposableItems = from item in _Dictionary.Values + where item is IDisposable + select item as IDisposable; + + foreach (var item in disposableItems) + { + item.Dispose(); + } + } + finally + { + _padlock.ExitWriteLock(); + } + + GC.SuppressFinalize(this); + } + + #endregion + } +#else +#if TINYIOC_INTERNAL + internal +#else + public +#endif + class SafeDictionary : IDisposable + { + private readonly object _Padlock = new object(); + private readonly Dictionary _Dictionary = new Dictionary(); + + public TValue this[TKey key] + { + set + { + lock (_Padlock) + { + TValue current; + if (_Dictionary.TryGetValue(key, out current)) + { + var disposable = current as IDisposable; + + if (disposable != null) + disposable.Dispose(); + } + + _Dictionary[key] = value; + } + } + } + + public bool TryGetValue(TKey key, out TValue value) + { + lock (_Padlock) + { + return _Dictionary.TryGetValue(key, out value); + } + } + + public bool Remove(TKey key) + { + lock (_Padlock) + { + return _Dictionary.Remove(key); + } + } + + public void Clear() + { + lock (_Padlock) + { + _Dictionary.Clear(); + } + } + + public IEnumerable Keys + { + get + { + return _Dictionary.Keys; + } + } + #region IDisposable Members + + public void Dispose() + { + lock (_Padlock) + { + var disposableItems = from item in _Dictionary.Values + where item is IDisposable + select item as IDisposable; + + foreach (var item in disposableItems) + { + item.Dispose(); + } + } + + GC.SuppressFinalize(this); + } + + #endregion + } +#endif + #endregion + + #region Extensions +#if TINYIOC_INTERNAL + internal +#else + public +#endif + static class AssemblyExtensions + { + public static Type[] SafeGetTypes(this Assembly assembly) + { + Type[] assemblies; + + try + { +#if PORTABLE || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 + assemblies = assembly.ExportedTypes.ToArray(); +#else + assemblies = assembly.GetTypes(); +#endif + } + catch (System.IO.FileNotFoundException) + { + assemblies = new Type[] { }; + } + catch (NotSupportedException) + { + assemblies = new Type[] { }; + } +#if !NETFX_CORE + catch (ReflectionTypeLoadException e) + { + assemblies = e.Types.Where(t => t != null).ToArray(); + } +#endif + return assemblies; + } + } + +#if PORTABLE || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 + [Flags] + internal enum BindingFlags { + Default = 0, + IgnoreCase = 1, + DeclaredOnly = 2, + Instance = 4, + Static = 8, + Public = 16, + NonPublic = 32, + FlattenHierarchy = 64, + InvokeMethod = 256, + CreateInstance = 512, + GetField = 1024, + SetField = 2048, + GetProperty = 4096, + SetProperty = 8192, + PutDispProperty = 16384, + ExactBinding = 65536, + PutRefDispProperty = 32768, + SuppressChangeType = 131072, + OptionalParamBinding = 262144, + IgnoreReturn = 16777216 + } +#endif + +#if TINYIOC_INTERNAL + internal +#else + public +#endif + static class TypeExtensions + { + private static SafeDictionary _genericMethodCache; + + static TypeExtensions() + { + _genericMethodCache = new SafeDictionary(); + } + +#if PORTABLE || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 + private static BindingFlags DefaultFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; + + public static ConstructorInfo[] GetConstructors(this Type type) + { + return type.GetConstructors(DefaultFlags); + } + + public static ConstructorInfo[] GetConstructors(this Type type, BindingFlags bindingFlags) + { + return type.GetConstructors(bindingFlags, null); + } + + private static ConstructorInfo[] GetConstructors(this Type type, BindingFlags bindingFlags, IList parameterTypes) + { + return type.GetTypeInfo().DeclaredConstructors.Where( + c => + { + if (!TestAccessibility(c, bindingFlags)) + { + return false; + } + + if (parameterTypes != null && !c.GetParameters().Select(p => p.ParameterType).SequenceEqual(parameterTypes)) + { + return false; + } + + return true; + }).ToArray(); + } + + public static MethodInfo GetGetMethod(this PropertyInfo propertyInfo) { + return propertyInfo.GetGetMethod(false); + } + + public static MethodInfo GetGetMethod(this PropertyInfo propertyInfo, bool nonPublic) { + MethodInfo getMethod = propertyInfo.GetMethod; + if (getMethod != null && (getMethod.IsPublic || nonPublic)) { + return getMethod; + } + + return null; + } + + public static MethodInfo GetSetMethod(this PropertyInfo propertyInfo) { + return propertyInfo.GetSetMethod(false); + } + + public static MethodInfo GetSetMethod(this PropertyInfo propertyInfo, bool nonPublic) { + MethodInfo setMethod = propertyInfo.SetMethod; + if (setMethod != null && (setMethod.IsPublic || nonPublic)) { + return setMethod; + } + + return null; + } + + public static Type[] GetGenericArguments(this Type type) + { + return type.GetTypeInfo().GenericTypeArguments; + } + + public static IEnumerable GetProperties(this Type type) + { + TypeInfo t = type.GetTypeInfo(); + IList properties = new List(); + while (t != null) + { + foreach (PropertyInfo member in t.DeclaredProperties) + { + if (!properties.Any(p => p.Name == member.Name)) + { + properties.Add(member); + } + } + t = (t.BaseType != null) ? t.BaseType.GetTypeInfo() : null; + } + + return properties; + } + + public static IEnumerable GetInterfaces(this Type type) + { + return type.GetTypeInfo().ImplementedInterfaces; + } + + public static MethodInfo GetMethod(this Type type, string name, IList parameterTypes) + { + return type.GetMethod(name, DefaultFlags, null, parameterTypes, null); + } + + public static MethodInfo GetMethod(this Type type, string name, BindingFlags bindingFlags, object placeHolder1, IList parameterTypes, object placeHolder2) + { + return type.GetTypeInfo().DeclaredMethods.Where( + m => + { + if (name != null && m.Name != name) + { + return false; + } + + if (!TestAccessibility(m, bindingFlags)) + { + return false; + } + + return m.GetParameters().Select(p => p.ParameterType).SequenceEqual(parameterTypes); + }).SingleOrDefault(); + } + + public static IEnumerable GetMethods(this Type type, BindingFlags bindingFlags) + { + return type.GetTypeInfo().DeclaredMethods; + } + + public static bool IsAssignableFrom(this Type type, Type c) + { + return type.GetTypeInfo().IsAssignableFrom(c.GetTypeInfo()); + } + + private static bool TestAccessibility(MethodBase member, BindingFlags bindingFlags) + { + bool visibility = (member.IsPublic && bindingFlags.HasFlag(BindingFlags.Public)) || + (!member.IsPublic && bindingFlags.HasFlag(BindingFlags.NonPublic)); + + bool instance = (member.IsStatic && bindingFlags.HasFlag(BindingFlags.Static)) || + (!member.IsStatic && bindingFlags.HasFlag(BindingFlags.Instance)); + + return visibility && instance; + } +#endif + + /// + /// Gets a generic method from a type given the method name, binding flags, generic types and parameter types + /// + /// Source type + /// Binding flags + /// Name of the method + /// Generic types to use to make the method generic + /// Method parameters + /// MethodInfo or null if no matches found + /// + /// + public static MethodInfo GetGenericMethod(this Type sourceType, BindingFlags bindingFlags, string methodName, Type[] genericTypes, Type[] parameterTypes) + { + MethodInfo method; + var cacheKey = new GenericMethodCacheKey(sourceType, methodName, genericTypes, parameterTypes); + + // Shouldn't need any additional locking + // we don't care if we do the method info generation + // more than once before it gets cached. + if (!_genericMethodCache.TryGetValue(cacheKey, out method)) + { + method = GetMethod(sourceType, bindingFlags, methodName, genericTypes, parameterTypes); + _genericMethodCache[cacheKey] = method; + } + + return method; + } + //#endif + +#if NETFX_CORE + private static MethodInfo GetMethod(Type sourceType, BindingFlags flags, string methodName, Type[] genericTypes, Type[] parameterTypes) + { + var methods = + sourceType.GetMethods(flags).Where( + mi => string.Equals(methodName, mi.Name, StringComparison.Ordinal)).Where( + mi => mi.ContainsGenericParameters).Where(mi => mi.GetGenericArguments().Length == genericTypes.Length). + Where(mi => mi.GetParameters().Length == parameterTypes.Length).Select( + mi => mi.MakeGenericMethod(genericTypes)).Where( + mi => mi.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameterTypes)).ToList(); + + if (methods.Count > 1) + { + throw new AmbiguousMatchException(); + } + + return methods.FirstOrDefault(); + } +#else + private static MethodInfo GetMethod(Type sourceType, BindingFlags bindingFlags, string methodName, Type[] genericTypes, Type[] parameterTypes) + { +#if GETPARAMETERS_OPEN_GENERICS + var methods = + sourceType.GetMethods(bindingFlags).Where( + mi => string.Equals(methodName, mi.Name, StringComparison.Ordinal)).Where( + mi => mi.ContainsGenericParameters).Where(mi => mi.GetGenericArguments().Length == genericTypes.Length). + Where(mi => mi.GetParameters().Length == parameterTypes.Length).Select( + mi => mi.MakeGenericMethod(genericTypes)).Where( + mi => mi.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameterTypes)).ToList(); +#else + var validMethods = from method in sourceType.GetMethods(bindingFlags) + where method.Name == methodName + where method.IsGenericMethod + where method.GetGenericArguments().Length == genericTypes.Length + let genericMethod = method.MakeGenericMethod(genericTypes) + where genericMethod.GetParameters().Count() == parameterTypes.Length + where genericMethod.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameterTypes) + select genericMethod; + + var methods = validMethods.ToList(); +#endif + if (methods.Count > 1) + { + throw new AmbiguousMatchException(); + } + + return methods.FirstOrDefault(); + } +#endif + + private sealed class GenericMethodCacheKey + { + private readonly Type _sourceType; + + private readonly string _methodName; + + private readonly Type[] _genericTypes; + + private readonly Type[] _parameterTypes; + + private readonly int _hashCode; + + public GenericMethodCacheKey(Type sourceType, string methodName, Type[] genericTypes, Type[] parameterTypes) + { + _sourceType = sourceType; + _methodName = methodName; + _genericTypes = genericTypes; + _parameterTypes = parameterTypes; + _hashCode = GenerateHashCode(); + } + + public override bool Equals(object obj) + { + var cacheKey = obj as GenericMethodCacheKey; + if (cacheKey == null) + return false; + + if (_sourceType != cacheKey._sourceType) + return false; + + if (!String.Equals(_methodName, cacheKey._methodName, StringComparison.Ordinal)) + return false; + + if (_genericTypes.Length != cacheKey._genericTypes.Length) + return false; + + if (_parameterTypes.Length != cacheKey._parameterTypes.Length) + return false; + + for (int i = 0; i < _genericTypes.Length; ++i) + { + if (_genericTypes[i] != cacheKey._genericTypes[i]) + return false; + } + + for (int i = 0; i < _parameterTypes.Length; ++i) + { + if (_parameterTypes[i] != cacheKey._parameterTypes[i]) + return false; + } + + return true; + } + + public override int GetHashCode() + { + return _hashCode; + } + + private int GenerateHashCode() + { + unchecked + { + var result = _sourceType.GetHashCode(); + + result = (result * 397) ^ _methodName.GetHashCode(); + + for (int i = 0; i < _genericTypes.Length; ++i) + { + result = (result * 397) ^ _genericTypes[i].GetHashCode(); + } + + for (int i = 0; i < _parameterTypes.Length; ++i) + { + result = (result * 397) ^ _parameterTypes[i].GetHashCode(); + } + + return result; + } + } + } + + } + + // @mbrit - 2012-05-22 - shim for ForEach call on List... +#if NETFX_CORE + internal static class ListExtender + { + internal static void ForEach(this List list, Action callback) + { + foreach (T obj in list) + callback(obj); + } + } +#endif + +#endregion + +#region TinyIoC Exception Types +#if SERIALIZABLE + [Serializable] +#endif +#if TINYIOC_INTERNAL + internal +#else + public +#endif + class TinyIoCResolutionException : Exception + { + private const string ERROR_TEXT = "Unable to resolve type: {0}"; + + public TinyIoCResolutionException(Type type) + : base(String.Format(ERROR_TEXT, type.FullName)) + { + } + + public TinyIoCResolutionException(Type type, Exception innerException) + : base(String.Format(ERROR_TEXT, type.FullName), innerException) + { + } +#if SERIALIZABLE + protected TinyIoCResolutionException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +#if SERIALIZABLE + [Serializable] +#endif +#if TINYIOC_INTERNAL + internal +#else + public +#endif + class TinyIoCRegistrationTypeException : Exception + { + private const string REGISTER_ERROR_TEXT = "Cannot register type {0} - abstract classes or interfaces are not valid implementation types for {1}."; + + public TinyIoCRegistrationTypeException(Type type, string factory) + : base(String.Format(REGISTER_ERROR_TEXT, type.FullName, factory)) + { + } + + public TinyIoCRegistrationTypeException(Type type, string factory, Exception innerException) + : base(String.Format(REGISTER_ERROR_TEXT, type.FullName, factory), innerException) + { + } +#if SERIALIZABLE + protected TinyIoCRegistrationTypeException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +#if SERIALIZABLE + [Serializable] +#endif +#if TINYIOC_INTERNAL + internal +#else + public +#endif + class TinyIoCRegistrationException : Exception + { + private const string CONVERT_ERROR_TEXT = "Cannot convert current registration of {0} to {1}"; + private const string GENERIC_CONSTRAINT_ERROR_TEXT = "Type {1} is not valid for a registration of type {0}"; + + public TinyIoCRegistrationException(Type type, string method) + : base(String.Format(CONVERT_ERROR_TEXT, type.FullName, method)) + { + } + + public TinyIoCRegistrationException(Type type, string method, Exception innerException) + : base(String.Format(CONVERT_ERROR_TEXT, type.FullName, method), innerException) + { + } + + public TinyIoCRegistrationException(Type registerType, Type implementationType) + : base(String.Format(GENERIC_CONSTRAINT_ERROR_TEXT, registerType.FullName, implementationType.FullName)) + { + } + + public TinyIoCRegistrationException(Type registerType, Type implementationType, Exception innerException) + : base(String.Format(GENERIC_CONSTRAINT_ERROR_TEXT, registerType.FullName, implementationType.FullName), innerException) + { + } +#if SERIALIZABLE + protected TinyIoCRegistrationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +#if SERIALIZABLE + [Serializable] +#endif +#if TINYIOC_INTERNAL + internal +#else + public +#endif + class TinyIoCWeakReferenceException : Exception + { + private const string ERROR_TEXT = "Unable to instantiate {0} - referenced object has been reclaimed"; + + public TinyIoCWeakReferenceException(Type type) + : base(String.Format(ERROR_TEXT, type.FullName)) + { + } + + public TinyIoCWeakReferenceException(Type type, Exception innerException) + : base(String.Format(ERROR_TEXT, type.FullName), innerException) + { + } +#if SERIALIZABLE + protected TinyIoCWeakReferenceException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +#if SERIALIZABLE + [Serializable] +#endif +#if TINYIOC_INTERNAL + internal +#else + public +#endif + class TinyIoCConstructorResolutionException : Exception + { + private const string ERROR_TEXT = "Unable to resolve constructor for {0} using provided Expression."; + + public TinyIoCConstructorResolutionException(Type type) + : base(String.Format(ERROR_TEXT, type.FullName)) + { + } + + public TinyIoCConstructorResolutionException(Type type, Exception innerException) + : base(String.Format(ERROR_TEXT, type.FullName), innerException) + { + } + + public TinyIoCConstructorResolutionException(string message, Exception innerException) + : base(message, innerException) + { + } + + public TinyIoCConstructorResolutionException(string message) + : base(message) + { + } +#if SERIALIZABLE + protected TinyIoCConstructorResolutionException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +#if SERIALIZABLE + [Serializable] +#endif +#if TINYIOC_INTERNAL + internal +#else + public +#endif + class TinyIoCAutoRegistrationException : Exception + { + private const string ERROR_TEXT = "Duplicate implementation of type {0} found ({1})."; + + public TinyIoCAutoRegistrationException(Type registerType, IEnumerable types) + : base(String.Format(ERROR_TEXT, registerType, GetTypesString(types))) + { + } + + public TinyIoCAutoRegistrationException(Type registerType, IEnumerable types, Exception innerException) + : base(String.Format(ERROR_TEXT, registerType, GetTypesString(types)), innerException) + { + } +#if SERIALIZABLE + protected TinyIoCAutoRegistrationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + + private static string GetTypesString(IEnumerable types) + { + var typeNames = from type in types + select type.FullName; + + return string.Join(",", typeNames.ToArray()); + } + } +#endregion + +#region Public Setup / Settings Classes + /// + /// Name/Value pairs for specifying "user" parameters when resolving + /// +#if TINYIOC_INTERNAL + internal +#else + public +#endif + sealed class NamedParameterOverloads : Dictionary + { + public static NamedParameterOverloads FromIDictionary(IDictionary data) + { + return data as NamedParameterOverloads ?? new NamedParameterOverloads(data); + } + + public NamedParameterOverloads() + { + } + + public NamedParameterOverloads(IDictionary data) + : base(data) + { + } + + private static readonly NamedParameterOverloads _Default = new NamedParameterOverloads(); + + public static NamedParameterOverloads Default + { + get + { + return _Default; + } + } + } + +#if TINYIOC_INTERNAL + internal +#else + public +#endif + enum UnregisteredResolutionActions + { + /// + /// Attempt to resolve type, even if the type isn't registered. + /// + /// Registered types/options will always take precedence. + /// + AttemptResolve, + + /// + /// Fail resolution if type not explicitly registered + /// + Fail, + + /// + /// Attempt to resolve unregistered type if requested type is generic + /// and no registration exists for the specific generic parameters used. + /// + /// Registered types/options will always take precedence. + /// + GenericsOnly + } + +#if TINYIOC_INTERNAL + internal +#else + public +#endif + enum NamedResolutionFailureActions + { + AttemptUnnamedResolution, + Fail + } + +#if TINYIOC_INTERNAL + internal +#else + public +#endif + enum DuplicateImplementationActions + { + RegisterSingle, + RegisterMultiple, + Fail + } + + /// + /// Resolution settings + /// +#if TINYIOC_INTERNAL + internal +#else + public +#endif + sealed class ResolveOptions + { + private static readonly ResolveOptions _Default = new ResolveOptions(); + private static readonly ResolveOptions _FailUnregisteredAndNameNotFound = new ResolveOptions() { NamedResolutionFailureAction = NamedResolutionFailureActions.Fail, UnregisteredResolutionAction = UnregisteredResolutionActions.Fail }; + private static readonly ResolveOptions _FailUnregisteredOnly = new ResolveOptions() { NamedResolutionFailureAction = NamedResolutionFailureActions.AttemptUnnamedResolution, UnregisteredResolutionAction = UnregisteredResolutionActions.Fail }; + private static readonly ResolveOptions _FailNameNotFoundOnly = new ResolveOptions() { NamedResolutionFailureAction = NamedResolutionFailureActions.Fail, UnregisteredResolutionAction = UnregisteredResolutionActions.AttemptResolve }; + + private UnregisteredResolutionActions _UnregisteredResolutionAction = UnregisteredResolutionActions.AttemptResolve; + public UnregisteredResolutionActions UnregisteredResolutionAction + { + get { return _UnregisteredResolutionAction; } + set { _UnregisteredResolutionAction = value; } + } + + private NamedResolutionFailureActions _NamedResolutionFailureAction = NamedResolutionFailureActions.Fail; + public NamedResolutionFailureActions NamedResolutionFailureAction + { + get { return _NamedResolutionFailureAction; } + set { _NamedResolutionFailureAction = value; } + } + + /// + /// Gets the default options (attempt resolution of unregistered types, fail on named resolution if name not found) + /// + public static ResolveOptions Default + { + get + { + return _Default; + } + } + + /// + /// Preconfigured option for attempting resolution of unregistered types and failing on named resolution if name not found + /// + public static ResolveOptions FailNameNotFoundOnly + { + get + { + return _FailNameNotFoundOnly; + } + } + + /// + /// Preconfigured option for failing on resolving unregistered types and on named resolution if name not found + /// + public static ResolveOptions FailUnregisteredAndNameNotFound + { + get + { + return _FailUnregisteredAndNameNotFound; + } + } + + /// + /// Preconfigured option for failing on resolving unregistered types, but attempting unnamed resolution if name not found + /// + public static ResolveOptions FailUnregisteredOnly + { + get + { + return _FailUnregisteredOnly; + } + } + } +#endregion + +#if TINYIOC_INTERNAL + internal +#else + public +#endif + sealed partial class TinyIoCContainer : IDisposable + { +#region Fake NETFX_CORE Classes +#if NETFX_CORE + private sealed class MethodAccessException : Exception + { + } + + private sealed class AppDomain + { + public static AppDomain CurrentDomain { get; private set; } + + static AppDomain() + { + CurrentDomain = new AppDomain(); + } + + // @mbrit - 2012-05-30 - in WinRT, this should be done async... + public async Task> GetAssembliesAsync() + { + var folder = Windows.ApplicationModel.Package.Current.InstalledLocation; + + List assemblies = new List(); + + var files = await folder.GetFilesAsync(); + + foreach (StorageFile file in files) + { + if (file.FileType == ".dll" || file.FileType == ".exe") + { + AssemblyName name = new AssemblyName() { Name = System.IO.Path.GetFileNameWithoutExtension(file.Name) }; + try + { + var asm = Assembly.Load(name); + assemblies.Add(asm); + } + catch + { + // ignore exceptions here... + } + } + } + + return assemblies; + } + } +#endif +#endregion + +#region "Fluent" API + /// + /// Registration options for "fluent" API + /// + public sealed class RegisterOptions + { + private TinyIoCContainer _Container; + private TypeRegistration _Registration; + + public RegisterOptions(TinyIoCContainer container, TypeRegistration registration) + { + _Container = container; + _Registration = registration; + } + + /// + /// Make registration a singleton (single instance) if possible + /// + /// RegisterOptions + /// + public RegisterOptions AsSingleton() + { + var currentFactory = _Container.GetCurrentFactory(_Registration); + + if (currentFactory == null) + throw new TinyIoCRegistrationException(_Registration.Type, "singleton"); + + return _Container.AddUpdateRegistration(_Registration, currentFactory.SingletonVariant); + } + + /// + /// Make registration multi-instance if possible + /// + /// RegisterOptions + /// + public RegisterOptions AsMultiInstance() + { + var currentFactory = _Container.GetCurrentFactory(_Registration); + + if (currentFactory == null) + throw new TinyIoCRegistrationException(_Registration.Type, "multi-instance"); + + return _Container.AddUpdateRegistration(_Registration, currentFactory.MultiInstanceVariant); + } + + /// + /// Make registration hold a weak reference if possible + /// + /// RegisterOptions + /// + public RegisterOptions WithWeakReference() + { + var currentFactory = _Container.GetCurrentFactory(_Registration); + + if (currentFactory == null) + throw new TinyIoCRegistrationException(_Registration.Type, "weak reference"); + + return _Container.AddUpdateRegistration(_Registration, currentFactory.WeakReferenceVariant); + } + + /// + /// Make registration hold a strong reference if possible + /// + /// RegisterOptions + /// + public RegisterOptions WithStrongReference() + { + var currentFactory = _Container.GetCurrentFactory(_Registration); + + if (currentFactory == null) + throw new TinyIoCRegistrationException(_Registration.Type, "strong reference"); + + return _Container.AddUpdateRegistration(_Registration, currentFactory.StrongReferenceVariant); + } + +#if EXPRESSIONS + public RegisterOptions UsingConstructor(Expression> constructor) + { + var lambda = constructor as LambdaExpression; + if (lambda == null) + throw new TinyIoCConstructorResolutionException(typeof(RegisterType)); + + var newExpression = lambda.Body as NewExpression; + if (newExpression == null) + throw new TinyIoCConstructorResolutionException(typeof(RegisterType)); + + var constructorInfo = newExpression.Constructor; + if (constructorInfo == null) + throw new TinyIoCConstructorResolutionException(typeof(RegisterType)); + + var currentFactory = _Container.GetCurrentFactory(_Registration); + if (currentFactory == null) + throw new TinyIoCConstructorResolutionException(typeof(RegisterType)); + + currentFactory.SetConstructor(constructorInfo); + + return this; + } +#endif + /// + /// Switches to a custom lifetime manager factory if possible. + /// + /// Usually used for RegisterOptions "To*" extension methods such as the ASP.Net per-request one. + /// + /// RegisterOptions instance + /// Custom lifetime manager + /// Error string to display if switch fails + /// RegisterOptions + public static RegisterOptions ToCustomLifetimeManager(RegisterOptions instance, ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorString) + { + if (instance == null) + throw new ArgumentNullException("instance", "instance is null."); + + if (lifetimeProvider == null) + throw new ArgumentNullException("lifetimeProvider", "lifetimeProvider is null."); + + if (string.IsNullOrEmpty(errorString)) + throw new ArgumentException("errorString is null or empty.", "errorString"); + + var currentFactory = instance._Container.GetCurrentFactory(instance._Registration); + + if (currentFactory == null) + throw new TinyIoCRegistrationException(instance._Registration.Type, errorString); + + return instance._Container.AddUpdateRegistration(instance._Registration, currentFactory.GetCustomObjectLifetimeVariant(lifetimeProvider, errorString)); + } + } + + /// + /// Registration options for "fluent" API when registering multiple implementations + /// + public sealed class MultiRegisterOptions + { + private IEnumerable _RegisterOptions; + + /// + /// Initializes a new instance of the MultiRegisterOptions class. + /// + /// Registration options + public MultiRegisterOptions(IEnumerable registerOptions) + { + _RegisterOptions = registerOptions; + } + + /// + /// Make registration a singleton (single instance) if possible + /// + /// RegisterOptions + /// + public MultiRegisterOptions AsSingleton() + { + _RegisterOptions = ExecuteOnAllRegisterOptions(ro => ro.AsSingleton()); + return this; + } + + /// + /// Make registration multi-instance if possible + /// + /// MultiRegisterOptions + /// + public MultiRegisterOptions AsMultiInstance() + { + _RegisterOptions = ExecuteOnAllRegisterOptions(ro => ro.AsMultiInstance()); + return this; + } + + /// + /// Switches to a custom lifetime manager factory if possible. + /// + /// Usually used for RegisterOptions "To*" extension methods such as the ASP.Net per-request one. + /// + /// MultiRegisterOptions instance + /// Custom lifetime manager + /// Error string to display if switch fails + /// MultiRegisterOptions + public static MultiRegisterOptions ToCustomLifetimeManager( + MultiRegisterOptions instance, + ITinyIoCObjectLifetimeProvider lifetimeProvider, + string errorString) + { + if (instance == null) + throw new ArgumentNullException("instance", "instance is null."); + + if (lifetimeProvider == null) + throw new ArgumentNullException("lifetimeProvider", "lifetimeProvider is null."); + + if (string.IsNullOrEmpty(errorString)) + throw new ArgumentException("errorString is null or empty.", "errorString"); + + instance._RegisterOptions = instance.ExecuteOnAllRegisterOptions(ro => RegisterOptions.ToCustomLifetimeManager(ro, lifetimeProvider, errorString)); + + return instance; + } + + private IEnumerable ExecuteOnAllRegisterOptions(Func action) + { + var newRegisterOptions = new List(); + + foreach (var registerOption in _RegisterOptions) + { + newRegisterOptions.Add(action(registerOption)); + } + + return newRegisterOptions; + } + } +#endregion + +#region Public API +#region Child Containers + public TinyIoCContainer GetChildContainer() + { + return new TinyIoCContainer(this); + } +#endregion + +#region Registration + /// + /// Attempt to automatically register all non-generic classes and interfaces in the current app domain. + /// + /// If more than one class implements an interface then only one implementation will be registered + /// although no error will be thrown. + /// + public void AutoRegister() + { +#if APPDOMAIN_GETASSEMBLIES + AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), DuplicateImplementationActions.RegisterSingle, null); +#else + AutoRegisterInternal(new Assembly[] { this.GetType().Assembly() }, DuplicateImplementationActions.RegisterSingle, null); +#endif + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the current app domain. + /// Types will only be registered if they pass the supplied registration predicate. + /// + /// If more than one class implements an interface then only one implementation will be registered + /// although no error will be thrown. + /// + /// Predicate to determine if a particular type should be registered + public void AutoRegister(Func registrationPredicate) + { +#if APPDOMAIN_GETASSEMBLIES + AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), DuplicateImplementationActions.RegisterSingle, registrationPredicate); +#else + AutoRegisterInternal(new Assembly[] { this.GetType().Assembly() }, DuplicateImplementationActions.RegisterSingle, registrationPredicate); +#endif + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the current app domain. + /// + /// What action to take when encountering duplicate implementations of an interface/base class. + /// + public void AutoRegister(DuplicateImplementationActions duplicateAction) + { +#if APPDOMAIN_GETASSEMBLIES + AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), duplicateAction, null); +#else + AutoRegisterInternal(new Assembly[] { this.GetType().Assembly() }, duplicateAction, null); +#endif + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the current app domain. + /// Types will only be registered if they pass the supplied registration predicate. + /// + /// What action to take when encountering duplicate implementations of an interface/base class. + /// Predicate to determine if a particular type should be registered + /// + public void AutoRegister(DuplicateImplementationActions duplicateAction, Func registrationPredicate) + { +#if APPDOMAIN_GETASSEMBLIES + AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), duplicateAction, registrationPredicate); +#else + AutoRegisterInternal(new Assembly[] { this.GetType().Assembly() }, duplicateAction, registrationPredicate); +#endif + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the specified assemblies + /// + /// If more than one class implements an interface then only one implementation will be registered + /// although no error will be thrown. + /// + /// Assemblies to process + public void AutoRegister(IEnumerable assemblies) + { + AutoRegisterInternal(assemblies, DuplicateImplementationActions.RegisterSingle, null); + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the specified assemblies + /// Types will only be registered if they pass the supplied registration predicate. + /// + /// If more than one class implements an interface then only one implementation will be registered + /// although no error will be thrown. + /// + /// Assemblies to process + /// Predicate to determine if a particular type should be registered + public void AutoRegister(IEnumerable assemblies, Func registrationPredicate) + { + AutoRegisterInternal(assemblies, DuplicateImplementationActions.RegisterSingle, registrationPredicate); + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the specified assemblies + /// + /// Assemblies to process + /// What action to take when encountering duplicate implementations of an interface/base class. + /// + public void AutoRegister(IEnumerable assemblies, DuplicateImplementationActions duplicateAction) + { + AutoRegisterInternal(assemblies, duplicateAction, null); + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the specified assemblies + /// Types will only be registered if they pass the supplied registration predicate. + /// + /// Assemblies to process + /// What action to take when encountering duplicate implementations of an interface/base class. + /// Predicate to determine if a particular type should be registered + /// + public void AutoRegister(IEnumerable assemblies, DuplicateImplementationActions duplicateAction, Func registrationPredicate) + { + AutoRegisterInternal(assemblies, duplicateAction, registrationPredicate); + } + + /// + /// Creates/replaces a container class registration with default options. + /// + /// Type to register + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType) + { + return RegisterInternal(registerType, string.Empty, GetDefaultObjectFactory(registerType, registerType)); + } + + /// + /// Creates/replaces a named container class registration with default options. + /// + /// Type to register + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, string name) + { + return RegisterInternal(registerType, name, GetDefaultObjectFactory(registerType, registerType)); + + } + + /// + /// Creates/replaces a container class registration with a given implementation and default options. + /// + /// Type to register + /// Type to instantiate that implements RegisterType + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, Type registerImplementation) + { + return this.RegisterInternal(registerType, string.Empty, GetDefaultObjectFactory(registerType, registerImplementation)); + } + + /// + /// Creates/replaces a named container class registration with a given implementation and default options. + /// + /// Type to register + /// Type to instantiate that implements RegisterType + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, Type registerImplementation, string name) + { + return this.RegisterInternal(registerType, name, GetDefaultObjectFactory(registerType, registerImplementation)); + } + + /// + /// Creates/replaces a container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Instance of RegisterType to register + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, object instance) + { + return RegisterInternal(registerType, string.Empty, new InstanceFactory(registerType, registerType, instance)); + } + + /// + /// Creates/replaces a named container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Instance of RegisterType to register + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, object instance, string name) + { + return RegisterInternal(registerType, name, new InstanceFactory(registerType, registerType, instance)); + } + + /// + /// Creates/replaces a container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Type of instance to register that implements RegisterType + /// Instance of RegisterImplementation to register + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, Type registerImplementation, object instance) + { + return RegisterInternal(registerType, string.Empty, new InstanceFactory(registerType, registerImplementation, instance)); + } + + /// + /// Creates/replaces a named container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Type of instance to register that implements RegisterType + /// Instance of RegisterImplementation to register + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, Type registerImplementation, object instance, string name) + { + return RegisterInternal(registerType, name, new InstanceFactory(registerType, registerImplementation, instance)); + } + + /// + /// Creates/replaces a container class registration with a user specified factory + /// + /// Type to register + /// Factory/lambda that returns an instance of RegisterType + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, Func factory) + { + return RegisterInternal(registerType, string.Empty, new DelegateFactory(registerType, factory)); + } + + /// + /// Creates/replaces a container class registration with a user specified factory + /// + /// Type to register + /// Factory/lambda that returns an instance of RegisterType + /// Name of registation + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, Func factory, string name) + { + return RegisterInternal(registerType, name, new DelegateFactory(registerType, factory)); + } + + /// + /// Creates/replaces a container class registration with default options. + /// + /// Type to register + /// RegisterOptions for fluent API + public RegisterOptions Register() + where RegisterType : class + { + return this.Register(typeof(RegisterType)); + } + + /// + /// Creates/replaces a named container class registration with default options. + /// + /// Type to register + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(string name) + where RegisterType : class + { + return this.Register(typeof(RegisterType), name); + } + + /// + /// Creates/replaces a container class registration with a given implementation and default options. + /// + /// Type to register + /// Type to instantiate that implements RegisterType + /// RegisterOptions for fluent API + public RegisterOptions Register() + where RegisterType : class + where RegisterImplementation : class, RegisterType + { + return this.Register(typeof(RegisterType), typeof(RegisterImplementation)); + } + + /// + /// Creates/replaces a named container class registration with a given implementation and default options. + /// + /// Type to register + /// Type to instantiate that implements RegisterType + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(string name) + where RegisterType : class + where RegisterImplementation : class, RegisterType + { + return this.Register(typeof(RegisterType), typeof(RegisterImplementation), name); + } + + /// + /// Creates/replaces a container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Instance of RegisterType to register + /// RegisterOptions for fluent API + public RegisterOptions Register(RegisterType instance) + where RegisterType : class + { + return this.Register(typeof(RegisterType), instance); + } + + /// + /// Creates/replaces a named container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Instance of RegisterType to register + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(RegisterType instance, string name) + where RegisterType : class + { + return this.Register(typeof(RegisterType), instance, name); + } + + /// + /// Creates/replaces a container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Type of instance to register that implements RegisterType + /// Instance of RegisterImplementation to register + /// RegisterOptions for fluent API + public RegisterOptions Register(RegisterImplementation instance) + where RegisterType : class + where RegisterImplementation : class, RegisterType + { + return this.Register(typeof(RegisterType), typeof(RegisterImplementation), instance); + } + + /// + /// Creates/replaces a named container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Type of instance to register that implements RegisterType + /// Instance of RegisterImplementation to register + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(RegisterImplementation instance, string name) + where RegisterType : class + where RegisterImplementation : class, RegisterType + { + return this.Register(typeof(RegisterType), typeof(RegisterImplementation), instance, name); + } + + /// + /// Creates/replaces a container class registration with a user specified factory + /// + /// Type to register + /// Factory/lambda that returns an instance of RegisterType + /// RegisterOptions for fluent API + public RegisterOptions Register(Func factory) + where RegisterType : class + { + if (factory == null) + { + throw new ArgumentNullException("factory"); + } + + return this.Register(typeof(RegisterType), (c, o) => factory(c, o)); + } + + /// + /// Creates/replaces a named container class registration with a user specified factory + /// + /// Type to register + /// Factory/lambda that returns an instance of RegisterType + /// Name of registation + /// RegisterOptions for fluent API + public RegisterOptions Register(Func factory, string name) + where RegisterType : class + { + if (factory == null) + { + throw new ArgumentNullException("factory"); + } + + return this.Register(typeof(RegisterType), (c, o) => factory(c, o), name); + } + + /// + /// Register multiple implementations of a type. + /// + /// Internally this registers each implementation using the full name of the class as its registration name. + /// + /// Type that each implementation implements + /// Types that implement RegisterType + /// MultiRegisterOptions for the fluent API + public MultiRegisterOptions RegisterMultiple(IEnumerable implementationTypes) + { + return RegisterMultiple(typeof(RegisterType), implementationTypes); + } + + /// + /// Register multiple implementations of a type. + /// + /// Internally this registers each implementation using the full name of the class as its registration name. + /// + /// Type that each implementation implements + /// Types that implement RegisterType + /// MultiRegisterOptions for the fluent API + public MultiRegisterOptions RegisterMultiple(Type registrationType, IEnumerable implementationTypes) + { + if (implementationTypes == null) + throw new ArgumentNullException("types", "types is null."); + + foreach (var type in implementationTypes) + //#if NETFX_CORE + // if (!registrationType.GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) + //#else + if (!registrationType.IsAssignableFrom(type)) + //#endif + throw new ArgumentException(String.Format("types: The type {0} is not assignable from {1}", registrationType.FullName, type.FullName)); + + if (implementationTypes.Count() != implementationTypes.Distinct().Count()) + { + var queryForDuplicatedTypes = from i in implementationTypes + group i by i + into j + where j.Count() > 1 + select j.Key.FullName; + + var fullNamesOfDuplicatedTypes = string.Join(",\n", queryForDuplicatedTypes.ToArray()); + var multipleRegMessage = string.Format("types: The same implementation type cannot be specified multiple times for {0}\n\n{1}", registrationType.FullName, fullNamesOfDuplicatedTypes); + throw new ArgumentException(multipleRegMessage); + } + + var registerOptions = new List(); + + foreach (var type in implementationTypes) + { + registerOptions.Add(Register(registrationType, type, type.FullName)); + } + + return new MultiRegisterOptions(registerOptions); + } +#endregion + +#region Unregistration + + /// + /// Remove a container class registration. + /// + /// Type to unregister + /// true if the registration is successfully found and removed; otherwise, false. + public bool Unregister() + { + return Unregister(typeof(RegisterType), string.Empty); + } + + /// + /// Remove a named container class registration. + /// + /// Type to unregister + /// Name of registration + /// true if the registration is successfully found and removed; otherwise, false. + public bool Unregister(string name) + { + return Unregister(typeof(RegisterType), name); + } + + /// + /// Remove a container class registration. + /// + /// Type to unregister + /// true if the registration is successfully found and removed; otherwise, false. + public bool Unregister(Type registerType) + { + return Unregister(registerType, string.Empty); + } + + /// + /// Remove a named container class registration. + /// + /// Type to unregister + /// Name of registration + /// true if the registration is successfully found and removed; otherwise, false. + public bool Unregister(Type registerType, string name) + { + var typeRegistration = new TypeRegistration(registerType, name); + + return RemoveRegistration(typeRegistration); + } + +#endregion + +#region Resolution + /// + /// Attempts to resolve a type using default options. + /// + /// Type to resolve + /// Instance of type + /// Unable to resolve the type. + public object Resolve(Type resolveType) + { + return ResolveInternal(new TypeRegistration(resolveType), NamedParameterOverloads.Default, ResolveOptions.Default); + } + + /// + /// Attempts to resolve a type using specified options. + /// + /// Type to resolve + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public object Resolve(Type resolveType, ResolveOptions options) + { + return ResolveInternal(new TypeRegistration(resolveType), NamedParameterOverloads.Default, options); + } + + /// + /// Attempts to resolve a type using default options and the supplied name. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// Name of registration + /// Instance of type + /// Unable to resolve the type. + public object Resolve(Type resolveType, string name) + { + return ResolveInternal(new TypeRegistration(resolveType, name), NamedParameterOverloads.Default, ResolveOptions.Default); + } + + /// + /// Attempts to resolve a type using supplied options and name. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// Name of registration + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public object Resolve(Type resolveType, string name, ResolveOptions options) + { + return ResolveInternal(new TypeRegistration(resolveType, name), NamedParameterOverloads.Default, options); + } + + /// + /// Attempts to resolve a type using default options and the supplied constructor parameters. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// User specified constructor parameters + /// Instance of type + /// Unable to resolve the type. + public object Resolve(Type resolveType, NamedParameterOverloads parameters) + { + return ResolveInternal(new TypeRegistration(resolveType), parameters, ResolveOptions.Default); + } + + /// + /// Attempts to resolve a type using specified options and the supplied constructor parameters. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// User specified constructor parameters + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public object Resolve(Type resolveType, NamedParameterOverloads parameters, ResolveOptions options) + { + return ResolveInternal(new TypeRegistration(resolveType), parameters, options); + } + + /// + /// Attempts to resolve a type using default options and the supplied constructor parameters and name. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// User specified constructor parameters + /// Name of registration + /// Instance of type + /// Unable to resolve the type. + public object Resolve(Type resolveType, string name, NamedParameterOverloads parameters) + { + return ResolveInternal(new TypeRegistration(resolveType, name), parameters, ResolveOptions.Default); + } + + /// + /// Attempts to resolve a named type using specified options and the supplied constructor parameters. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// Name of registration + /// User specified constructor parameters + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public object Resolve(Type resolveType, string name, NamedParameterOverloads parameters, ResolveOptions options) + { + return ResolveInternal(new TypeRegistration(resolveType, name), parameters, options); + } + + /// + /// Attempts to resolve a type using default options. + /// + /// Type to resolve + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve() + where ResolveType : class + { + return (ResolveType)Resolve(typeof(ResolveType)); + } + + /// + /// Attempts to resolve a type using specified options. + /// + /// Type to resolve + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(ResolveOptions options) + where ResolveType : class + { + return (ResolveType)Resolve(typeof(ResolveType), options); + } + + /// + /// Attempts to resolve a type using default options and the supplied name. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// Name of registration + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(string name) + where ResolveType : class + { + return (ResolveType)Resolve(typeof(ResolveType), name); + } + + /// + /// Attempts to resolve a type using supplied options and name. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// Name of registration + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(string name, ResolveOptions options) + where ResolveType : class + { + return (ResolveType)Resolve(typeof(ResolveType), name, options); + } + + /// + /// Attempts to resolve a type using default options and the supplied constructor parameters. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// User specified constructor parameters + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(NamedParameterOverloads parameters) + where ResolveType : class + { + return (ResolveType)Resolve(typeof(ResolveType), parameters); + } + + /// + /// Attempts to resolve a type using specified options and the supplied constructor parameters. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// User specified constructor parameters + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(NamedParameterOverloads parameters, ResolveOptions options) + where ResolveType : class + { + return (ResolveType)Resolve(typeof(ResolveType), parameters, options); + } + + /// + /// Attempts to resolve a type using default options and the supplied constructor parameters and name. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// User specified constructor parameters + /// Name of registration + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(string name, NamedParameterOverloads parameters) + where ResolveType : class + { + return (ResolveType)Resolve(typeof(ResolveType), name, parameters); + } + + /// + /// Attempts to resolve a named type using specified options and the supplied constructor parameters. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// Name of registration + /// User specified constructor parameters + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(string name, NamedParameterOverloads parameters, ResolveOptions options) + where ResolveType : class + { + return (ResolveType)Resolve(typeof(ResolveType), name, parameters, options); + } + + /// + /// Attempts to predict whether a given type can be resolved with default options. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Bool indicating whether the type can be resolved + public bool CanResolve(Type resolveType) + { + return CanResolveInternal(new TypeRegistration(resolveType), NamedParameterOverloads.Default, ResolveOptions.Default); + } + + /// + /// Attempts to predict whether a given named type can be resolved with default options. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// Bool indicating whether the type can be resolved + private bool CanResolve(Type resolveType, string name) + { + return CanResolveInternal(new TypeRegistration(resolveType, name), NamedParameterOverloads.Default, ResolveOptions.Default); + } + + /// + /// Attempts to predict whether a given type can be resolved with the specified options. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(Type resolveType, ResolveOptions options) + { + return CanResolveInternal(new TypeRegistration(resolveType), NamedParameterOverloads.Default, options); + } + + /// + /// Attempts to predict whether a given named type can be resolved with the specified options. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(Type resolveType, string name, ResolveOptions options) + { + return CanResolveInternal(new TypeRegistration(resolveType, name), NamedParameterOverloads.Default, options); + } + + /// + /// Attempts to predict whether a given type can be resolved with the supplied constructor parameters and default options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// User supplied named parameter overloads + /// Bool indicating whether the type can be resolved + public bool CanResolve(Type resolveType, NamedParameterOverloads parameters) + { + return CanResolveInternal(new TypeRegistration(resolveType), parameters, ResolveOptions.Default); + } + + /// + /// Attempts to predict whether a given named type can be resolved with the supplied constructor parameters and default options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// User supplied named parameter overloads + /// Bool indicating whether the type can be resolved + public bool CanResolve(Type resolveType, string name, NamedParameterOverloads parameters) + { + return CanResolveInternal(new TypeRegistration(resolveType, name), parameters, ResolveOptions.Default); + } + + /// + /// Attempts to predict whether a given type can be resolved with the supplied constructor parameters options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// User supplied named parameter overloads + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(Type resolveType, NamedParameterOverloads parameters, ResolveOptions options) + { + return CanResolveInternal(new TypeRegistration(resolveType), parameters, options); + } + + /// + /// Attempts to predict whether a given named type can be resolved with the supplied constructor parameters options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// User supplied named parameter overloads + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(Type resolveType, string name, NamedParameterOverloads parameters, ResolveOptions options) + { + return CanResolveInternal(new TypeRegistration(resolveType, name), parameters, options); + } + + /// + /// Attempts to predict whether a given type can be resolved with default options. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Bool indicating whether the type can be resolved + public bool CanResolve() + where ResolveType : class + { + return CanResolve(typeof(ResolveType)); + } + + /// + /// Attempts to predict whether a given named type can be resolved with default options. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Bool indicating whether the type can be resolved + public bool CanResolve(string name) + where ResolveType : class + { + return CanResolve(typeof(ResolveType), name); + } + + /// + /// Attempts to predict whether a given type can be resolved with the specified options. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(ResolveOptions options) + where ResolveType : class + { + return CanResolve(typeof(ResolveType), options); + } + + /// + /// Attempts to predict whether a given named type can be resolved with the specified options. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(string name, ResolveOptions options) + where ResolveType : class + { + return CanResolve(typeof(ResolveType), name, options); + } + + /// + /// Attempts to predict whether a given type can be resolved with the supplied constructor parameters and default options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// User supplied named parameter overloads + /// Bool indicating whether the type can be resolved + public bool CanResolve(NamedParameterOverloads parameters) + where ResolveType : class + { + return CanResolve(typeof(ResolveType), parameters); + } + + /// + /// Attempts to predict whether a given named type can be resolved with the supplied constructor parameters and default options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// User supplied named parameter overloads + /// Bool indicating whether the type can be resolved + public bool CanResolve(string name, NamedParameterOverloads parameters) + where ResolveType : class + { + return CanResolve(typeof(ResolveType), name, parameters); + } + + /// + /// Attempts to predict whether a given type can be resolved with the supplied constructor parameters options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// User supplied named parameter overloads + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(NamedParameterOverloads parameters, ResolveOptions options) + where ResolveType : class + { + return CanResolve(typeof(ResolveType), parameters, options); + } + + /// + /// Attempts to predict whether a given named type can be resolved with the supplied constructor parameters options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// User supplied named parameter overloads + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(string name, NamedParameterOverloads parameters, ResolveOptions options) + where ResolveType : class + { + return CanResolve(typeof(ResolveType), name, parameters, options); + } + + /// + /// Attemps to resolve a type using the default options + /// + /// Type to resolve + /// Resolved type or default if resolve fails + /// True if resolved sucessfully, false otherwise + public bool TryResolve(Type resolveType, out object resolvedType) + { + try + { + resolvedType = Resolve(resolveType); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = null; + return false; + } + } + + /// + /// Attemps to resolve a type using the given options + /// + /// Type to resolve + /// Resolution options + /// Resolved type or default if resolve fails + /// True if resolved sucessfully, false otherwise + public bool TryResolve(Type resolveType, ResolveOptions options, out object resolvedType) + { + try + { + resolvedType = Resolve(resolveType, options); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = null; + return false; + } + } + + /// + /// Attemps to resolve a type using the default options and given name + /// + /// Type to resolve + /// Name of registration + /// Resolved type or default if resolve fails + /// True if resolved sucessfully, false otherwise + public bool TryResolve(Type resolveType, string name, out object resolvedType) + { + try + { + resolvedType = Resolve(resolveType, name); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = null; + return false; + } + } + + /// + /// Attemps to resolve a type using the given options and name + /// + /// Type to resolve + /// Name of registration + /// Resolution options + /// Resolved type or default if resolve fails + /// True if resolved sucessfully, false otherwise + public bool TryResolve(Type resolveType, string name, ResolveOptions options, out object resolvedType) + { + try + { + resolvedType = Resolve(resolveType, name, options); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = null; + return false; + } + } + + /// + /// Attemps to resolve a type using the default options and supplied constructor parameters + /// + /// Type to resolve + /// User specified constructor parameters + /// Resolved type or default if resolve fails + /// True if resolved sucessfully, false otherwise + public bool TryResolve(Type resolveType, NamedParameterOverloads parameters, out object resolvedType) + { + try + { + resolvedType = Resolve(resolveType, parameters); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = null; + return false; + } + } + + /// + /// Attemps to resolve a type using the default options and supplied name and constructor parameters + /// + /// Type to resolve + /// Name of registration + /// User specified constructor parameters + /// Resolved type or default if resolve fails + /// True if resolved sucessfully, false otherwise + public bool TryResolve(Type resolveType, string name, NamedParameterOverloads parameters, out object resolvedType) + { + try + { + resolvedType = Resolve(resolveType, name, parameters); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = null; + return false; + } + } + + /// + /// Attemps to resolve a type using the supplied options and constructor parameters + /// + /// Type to resolve + /// User specified constructor parameters + /// Resolution options + /// Resolved type or default if resolve fails + /// True if resolved sucessfully, false otherwise + public bool TryResolve(Type resolveType, NamedParameterOverloads parameters, ResolveOptions options, out object resolvedType) + { + try + { + resolvedType = Resolve(resolveType, parameters, options); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = null; + return false; + } + } + + /// + /// Attemps to resolve a type using the supplied name, options and constructor parameters + /// + /// Type to resolve + /// Name of registration + /// User specified constructor parameters + /// Resolution options + /// Resolved type or default if resolve fails + /// True if resolved sucessfully, false otherwise + public bool TryResolve(Type resolveType, string name, NamedParameterOverloads parameters, ResolveOptions options, out object resolvedType) + { + try + { + resolvedType = Resolve(resolveType, name, parameters, options); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = null; + return false; + } + } + + /// + /// Attemps to resolve a type using the default options + /// + /// Type to resolve + /// Resolved type or default if resolve fails + /// True if resolved sucessfully, false otherwise + public bool TryResolve(out ResolveType resolvedType) + where ResolveType : class + { + try + { + resolvedType = Resolve(); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = default(ResolveType); + return false; + } + } + + /// + /// Attemps to resolve a type using the given options + /// + /// Type to resolve + /// Resolution options + /// Resolved type or default if resolve fails + /// True if resolved sucessfully, false otherwise + public bool TryResolve(ResolveOptions options, out ResolveType resolvedType) + where ResolveType : class + { + try + { + resolvedType = Resolve(options); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = default(ResolveType); + return false; + } + } + + /// + /// Attemps to resolve a type using the default options and given name + /// + /// Type to resolve + /// Name of registration + /// Resolved type or default if resolve fails + /// True if resolved sucessfully, false otherwise + public bool TryResolve(string name, out ResolveType resolvedType) + where ResolveType : class + { + try + { + resolvedType = Resolve(name); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = default(ResolveType); + return false; + } + } + + /// + /// Attemps to resolve a type using the given options and name + /// + /// Type to resolve + /// Name of registration + /// Resolution options + /// Resolved type or default if resolve fails + /// True if resolved sucessfully, false otherwise + public bool TryResolve(string name, ResolveOptions options, out ResolveType resolvedType) + where ResolveType : class + { + try + { + resolvedType = Resolve(name, options); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = default(ResolveType); + return false; + } + } + + /// + /// Attemps to resolve a type using the default options and supplied constructor parameters + /// + /// Type to resolve + /// User specified constructor parameters + /// Resolved type or default if resolve fails + /// True if resolved sucessfully, false otherwise + public bool TryResolve(NamedParameterOverloads parameters, out ResolveType resolvedType) + where ResolveType : class + { + try + { + resolvedType = Resolve(parameters); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = default(ResolveType); + return false; + } + } + + /// + /// Attemps to resolve a type using the default options and supplied name and constructor parameters + /// + /// Type to resolve + /// Name of registration + /// User specified constructor parameters + /// Resolved type or default if resolve fails + /// True if resolved sucessfully, false otherwise + public bool TryResolve(string name, NamedParameterOverloads parameters, out ResolveType resolvedType) + where ResolveType : class + { + try + { + resolvedType = Resolve(name, parameters); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = default(ResolveType); + return false; + } + } + + /// + /// Attemps to resolve a type using the supplied options and constructor parameters + /// + /// Type to resolve + /// User specified constructor parameters + /// Resolution options + /// Resolved type or default if resolve fails + /// True if resolved sucessfully, false otherwise + public bool TryResolve(NamedParameterOverloads parameters, ResolveOptions options, out ResolveType resolvedType) + where ResolveType : class + { + try + { + resolvedType = Resolve(parameters, options); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = default(ResolveType); + return false; + } + } + + /// + /// Attemps to resolve a type using the supplied name, options and constructor parameters + /// + /// Type to resolve + /// Name of registration + /// User specified constructor parameters + /// Resolution options + /// Resolved type or default if resolve fails + /// True if resolved sucessfully, false otherwise + public bool TryResolve(string name, NamedParameterOverloads parameters, ResolveOptions options, out ResolveType resolvedType) + where ResolveType : class + { + try + { + resolvedType = Resolve(name, parameters, options); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = default(ResolveType); + return false; + } + } + + /// + /// Returns all registrations of a type + /// + /// Type to resolveAll + /// Whether to include un-named (default) registrations + /// IEnumerable + public IEnumerable ResolveAll(Type resolveType, bool includeUnnamed) + { + return ResolveAllInternal(resolveType, includeUnnamed); + } + + /// + /// Returns all registrations of a type, both named and unnamed + /// + /// Type to resolveAll + /// IEnumerable + public IEnumerable ResolveAll(Type resolveType) + { + return ResolveAll(resolveType, false); + } + + /// + /// Returns all registrations of a type + /// + /// Type to resolveAll + /// Whether to include un-named (default) registrations + /// IEnumerable + public IEnumerable ResolveAll(bool includeUnnamed) + where ResolveType : class + { + return this.ResolveAll(typeof(ResolveType), includeUnnamed).Cast(); + } + + /// + /// Returns all registrations of a type, both named and unnamed + /// + /// Type to resolveAll + /// IEnumerable + public IEnumerable ResolveAll() + where ResolveType : class + { + return ResolveAll(true); + } + + /// + /// Attempts to resolve all public property dependencies on the given object. + /// + /// Object to "build up" + public void BuildUp(object input) + { + BuildUpInternal(input, ResolveOptions.Default); + } + + /// + /// Attempts to resolve all public property dependencies on the given object using the given resolve options. + /// + /// Object to "build up" + /// Resolve options to use + public void BuildUp(object input, ResolveOptions resolveOptions) + { + BuildUpInternal(input, resolveOptions); + } +#endregion +#endregion + +#region Object Factories + /// + /// Provides custom lifetime management for ASP.Net per-request lifetimes etc. + /// + public interface ITinyIoCObjectLifetimeProvider + { + /// + /// Gets the stored object if it exists, or null if not + /// + /// Object instance or null + object GetObject(); + + /// + /// Store the object + /// + /// Object to store + void SetObject(object value); + + /// + /// Release the object + /// + void ReleaseObject(); + } + + private abstract class ObjectFactoryBase + { + /// + /// Whether to assume this factory sucessfully constructs its objects + /// + /// Generally set to true for delegate style factories as CanResolve cannot delve + /// into the delegates they contain. + /// + public virtual bool AssumeConstruction { get { return false; } } + + /// + /// The type the factory instantiates + /// + public abstract Type CreatesType { get; } + + /// + /// Constructor to use, if specified + /// + public ConstructorInfo Constructor { get; protected set; } + + /// + /// Create the type + /// + /// Type user requested to be resolved + /// Container that requested the creation + /// Any user parameters passed + /// + /// + public abstract object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options); + + public virtual ObjectFactoryBase SingletonVariant + { + get + { + throw new TinyIoCRegistrationException(this.GetType(), "singleton"); + } + } + + public virtual ObjectFactoryBase MultiInstanceVariant + { + get + { + throw new TinyIoCRegistrationException(this.GetType(), "multi-instance"); + } + } + + public virtual ObjectFactoryBase StrongReferenceVariant + { + get + { + throw new TinyIoCRegistrationException(this.GetType(), "strong reference"); + } + } + + public virtual ObjectFactoryBase WeakReferenceVariant + { + get + { + throw new TinyIoCRegistrationException(this.GetType(), "weak reference"); + } + } + + public virtual ObjectFactoryBase GetCustomObjectLifetimeVariant(ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorString) + { + throw new TinyIoCRegistrationException(this.GetType(), errorString); + } + + public virtual void SetConstructor(ConstructorInfo constructor) + { + Constructor = constructor; + } + + public virtual ObjectFactoryBase GetFactoryForChildContainer(Type type, TinyIoCContainer parent, TinyIoCContainer child) + { + return this; + } + } + + /// + /// IObjectFactory that creates new instances of types for each resolution + /// + private class MultiInstanceFactory : ObjectFactoryBase + { + private readonly Type registerType; + private readonly Type registerImplementation; + public override Type CreatesType { get { return this.registerImplementation; } } + + public MultiInstanceFactory(Type registerType, Type registerImplementation) + { + //#if NETFX_CORE + // if (registerImplementation.GetTypeInfo().IsAbstract() || registerImplementation.GetTypeInfo().IsInterface()) + // throw new TinyIoCRegistrationTypeException(registerImplementation, "MultiInstanceFactory"); + //#else + if (registerImplementation.IsAbstract() || registerImplementation.IsInterface()) + throw new TinyIoCRegistrationTypeException(registerImplementation, "MultiInstanceFactory"); + //#endif + if (!IsValidAssignment(registerType, registerImplementation)) + throw new TinyIoCRegistrationTypeException(registerImplementation, "MultiInstanceFactory"); + + this.registerType = registerType; + this.registerImplementation = registerImplementation; + } + + public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + try + { + return container.ConstructType(requestedType, this.registerImplementation, Constructor, parameters, options); + } + catch (TinyIoCResolutionException ex) + { + throw new TinyIoCResolutionException(this.registerType, ex); + } + } + + public override ObjectFactoryBase SingletonVariant + { + get + { + return new SingletonFactory(this.registerType, this.registerImplementation); + } + } + + public override ObjectFactoryBase GetCustomObjectLifetimeVariant(ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorString) + { + return new CustomObjectLifetimeFactory(this.registerType, this.registerImplementation, lifetimeProvider, errorString); + } + + public override ObjectFactoryBase MultiInstanceVariant + { + get + { + return this; + } + } + } + + /// + /// IObjectFactory that invokes a specified delegate to construct the object + /// + private class DelegateFactory : ObjectFactoryBase + { + private readonly Type registerType; + + private Func _factory; + + public override bool AssumeConstruction { get { return true; } } + + public override Type CreatesType { get { return this.registerType; } } + + public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + try + { + return _factory.Invoke(container, parameters); + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(this.registerType, ex); + } + } + + public DelegateFactory(Type registerType, Func factory) + { + if (factory == null) + throw new ArgumentNullException("factory"); + + _factory = factory; + + this.registerType = registerType; + } + + public override ObjectFactoryBase WeakReferenceVariant + { + get + { + return new WeakDelegateFactory(this.registerType, _factory); + } + } + + public override ObjectFactoryBase StrongReferenceVariant + { + get + { + return this; + } + } + + public override void SetConstructor(ConstructorInfo constructor) + { + throw new TinyIoCConstructorResolutionException("Constructor selection is not possible for delegate factory registrations"); + } + } + + /// + /// IObjectFactory that invokes a specified delegate to construct the object + /// Holds the delegate using a weak reference + /// + private class WeakDelegateFactory : ObjectFactoryBase + { + private readonly Type registerType; + + private WeakReference _factory; + + public override bool AssumeConstruction { get { return true; } } + + public override Type CreatesType { get { return this.registerType; } } + + public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + var factory = _factory.Target as Func; + + if (factory == null) + throw new TinyIoCWeakReferenceException(this.registerType); + + try + { + return factory.Invoke(container, parameters); + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(this.registerType, ex); + } + } + + public WeakDelegateFactory(Type registerType, Func factory) + { + if (factory == null) + throw new ArgumentNullException("factory"); + + _factory = new WeakReference(factory); + + this.registerType = registerType; + } + + public override ObjectFactoryBase StrongReferenceVariant + { + get + { + var factory = _factory.Target as Func; + + if (factory == null) + throw new TinyIoCWeakReferenceException(this.registerType); + + return new DelegateFactory(this.registerType, factory); + } + } + + public override ObjectFactoryBase WeakReferenceVariant + { + get + { + return this; + } + } + + public override void SetConstructor(ConstructorInfo constructor) + { + throw new TinyIoCConstructorResolutionException("Constructor selection is not possible for delegate factory registrations"); + } + } + + /// + /// Stores an particular instance to return for a type + /// + private class InstanceFactory : ObjectFactoryBase, IDisposable + { + private readonly Type registerType; + private readonly Type registerImplementation; + private object _instance; + + public override bool AssumeConstruction { get { return true; } } + + public InstanceFactory(Type registerType, Type registerImplementation, object instance) + { + if (!IsValidAssignment(registerType, registerImplementation)) + throw new TinyIoCRegistrationTypeException(registerImplementation, "InstanceFactory"); + + this.registerType = registerType; + this.registerImplementation = registerImplementation; + _instance = instance; + } + + public override Type CreatesType + { + get { return this.registerImplementation; } + } + + public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + return _instance; + } + + public override ObjectFactoryBase MultiInstanceVariant + { + get { return new MultiInstanceFactory(this.registerType, this.registerImplementation); } + } + + public override ObjectFactoryBase WeakReferenceVariant + { + get + { + return new WeakInstanceFactory(this.registerType, this.registerImplementation, this._instance); + } + } + + public override ObjectFactoryBase StrongReferenceVariant + { + get + { + return this; + } + } + + public override void SetConstructor(ConstructorInfo constructor) + { + throw new TinyIoCConstructorResolutionException("Constructor selection is not possible for instance factory registrations"); + } + + public void Dispose() + { + var disposable = _instance as IDisposable; + + if (disposable != null) + disposable.Dispose(); + } + } + + /// + /// Stores an particular instance to return for a type + /// + /// Stores the instance with a weak reference + /// + private class WeakInstanceFactory : ObjectFactoryBase, IDisposable + { + private readonly Type registerType; + private readonly Type registerImplementation; + private readonly WeakReference _instance; + + public WeakInstanceFactory(Type registerType, Type registerImplementation, object instance) + { + if (!IsValidAssignment(registerType, registerImplementation)) + throw new TinyIoCRegistrationTypeException(registerImplementation, "WeakInstanceFactory"); + + this.registerType = registerType; + this.registerImplementation = registerImplementation; + _instance = new WeakReference(instance); + } + + public override Type CreatesType + { + get { return this.registerImplementation; } + } + + public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + var instance = _instance.Target; + + if (instance == null) + throw new TinyIoCWeakReferenceException(this.registerType); + + return instance; + } + + public override ObjectFactoryBase MultiInstanceVariant + { + get + { + return new MultiInstanceFactory(this.registerType, this.registerImplementation); + } + } + + public override ObjectFactoryBase WeakReferenceVariant + { + get + { + return this; + } + } + + public override ObjectFactoryBase StrongReferenceVariant + { + get + { + var instance = _instance.Target; + + if (instance == null) + throw new TinyIoCWeakReferenceException(this.registerType); + + return new InstanceFactory(this.registerType, this.registerImplementation, instance); + } + } + + public override void SetConstructor(ConstructorInfo constructor) + { + throw new TinyIoCConstructorResolutionException("Constructor selection is not possible for instance factory registrations"); + } + + public void Dispose() + { + var disposable = _instance.Target as IDisposable; + + if (disposable != null) + disposable.Dispose(); + } + } + + /// + /// A factory that lazy instantiates a type and always returns the same instance + /// + private class SingletonFactory : ObjectFactoryBase, IDisposable + { + private readonly Type registerType; + private readonly Type registerImplementation; + private readonly object SingletonLock = new object(); + private object _Current; + + public SingletonFactory(Type registerType, Type registerImplementation) + { + //#if NETFX_CORE + // if (registerImplementation.GetTypeInfo().IsAbstract() || registerImplementation.GetTypeInfo().IsInterface()) + //#else + if (registerImplementation.IsAbstract() || registerImplementation.IsInterface()) + //#endif + throw new TinyIoCRegistrationTypeException(registerImplementation, "SingletonFactory"); + + if (!IsValidAssignment(registerType, registerImplementation)) + throw new TinyIoCRegistrationTypeException(registerImplementation, "SingletonFactory"); + + this.registerType = registerType; + this.registerImplementation = registerImplementation; + } + + public override Type CreatesType + { + get { return this.registerImplementation; } + } + + public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + if (parameters.Count != 0) + throw new ArgumentException("Cannot specify parameters for singleton types"); + + lock (SingletonLock) + if (_Current == null) + _Current = container.ConstructType(requestedType, this.registerImplementation, Constructor, options); + + return _Current; + } + + public override ObjectFactoryBase SingletonVariant + { + get + { + return this; + } + } + + public override ObjectFactoryBase GetCustomObjectLifetimeVariant(ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorString) + { + return new CustomObjectLifetimeFactory(this.registerType, this.registerImplementation, lifetimeProvider, errorString); + } + + public override ObjectFactoryBase MultiInstanceVariant + { + get + { + return new MultiInstanceFactory(this.registerType, this.registerImplementation); + } + } + + public override ObjectFactoryBase GetFactoryForChildContainer(Type type, TinyIoCContainer parent, TinyIoCContainer child) + { + // We make sure that the singleton is constructed before the child container takes the factory. + // Otherwise the results would vary depending on whether or not the parent container had resolved + // the type before the child container does. + GetObject(type, parent, NamedParameterOverloads.Default, ResolveOptions.Default); + return this; + } + + public void Dispose() + { + if (this._Current == null) + return; + + var disposable = this._Current as IDisposable; + + if (disposable != null) + disposable.Dispose(); + } + } + + /// + /// A factory that offloads lifetime to an external lifetime provider + /// + private class CustomObjectLifetimeFactory : ObjectFactoryBase, IDisposable + { + private readonly object SingletonLock = new object(); + private readonly Type registerType; + private readonly Type registerImplementation; + private readonly ITinyIoCObjectLifetimeProvider _LifetimeProvider; + + public CustomObjectLifetimeFactory(Type registerType, Type registerImplementation, ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorMessage) + { + if (lifetimeProvider == null) + throw new ArgumentNullException("lifetimeProvider", "lifetimeProvider is null."); + + if (!IsValidAssignment(registerType, registerImplementation)) + throw new TinyIoCRegistrationTypeException(registerImplementation, "SingletonFactory"); + + //#if NETFX_CORE + // if (registerImplementation.GetTypeInfo().IsAbstract() || registerImplementation.GetTypeInfo().IsInterface()) + //#else + if (registerImplementation.IsAbstract() || registerImplementation.IsInterface()) + //#endif + throw new TinyIoCRegistrationTypeException(registerImplementation, errorMessage); + + this.registerType = registerType; + this.registerImplementation = registerImplementation; + _LifetimeProvider = lifetimeProvider; + } + + public override Type CreatesType + { + get { return this.registerImplementation; } + } + + public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + object current; + + lock (SingletonLock) + { + current = _LifetimeProvider.GetObject(); + if (current == null) + { + current = container.ConstructType(requestedType, this.registerImplementation, Constructor, options); + _LifetimeProvider.SetObject(current); + } + } + + return current; + } + + public override ObjectFactoryBase SingletonVariant + { + get + { + _LifetimeProvider.ReleaseObject(); + return new SingletonFactory(this.registerType, this.registerImplementation); + } + } + + public override ObjectFactoryBase MultiInstanceVariant + { + get + { + _LifetimeProvider.ReleaseObject(); + return new MultiInstanceFactory(this.registerType, this.registerImplementation); + } + } + + public override ObjectFactoryBase GetCustomObjectLifetimeVariant(ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorString) + { + _LifetimeProvider.ReleaseObject(); + return new CustomObjectLifetimeFactory(this.registerType, this.registerImplementation, lifetimeProvider, errorString); + } + + public override ObjectFactoryBase GetFactoryForChildContainer(Type type, TinyIoCContainer parent, TinyIoCContainer child) + { + // We make sure that the singleton is constructed before the child container takes the factory. + // Otherwise the results would vary depending on whether or not the parent container had resolved + // the type before the child container does. + GetObject(type, parent, NamedParameterOverloads.Default, ResolveOptions.Default); + return this; + } + + public void Dispose() + { + _LifetimeProvider.ReleaseObject(); + } + } +#endregion + +#region Singleton Container + private static readonly TinyIoCContainer _Current = new TinyIoCContainer(); + + static TinyIoCContainer() + { + } + + /// + /// Lazy created Singleton instance of the container for simple scenarios + /// + public static TinyIoCContainer Current + { + get + { + return _Current; + } + } +#endregion + +#region Type Registrations + public sealed class TypeRegistration + { + private int _hashCode; + + public Type Type { get; private set; } + public string Name { get; private set; } + + public TypeRegistration(Type type) + : this(type, string.Empty) + { + } + + public TypeRegistration(Type type, string name) + { + Type = type; + Name = name; + + _hashCode = String.Concat(Type.FullName, "|", Name).GetHashCode(); + } + + public override bool Equals(object obj) + { + var typeRegistration = obj as TypeRegistration; + + if (typeRegistration == null) + return false; + + if (Type != typeRegistration.Type) + return false; + + if (String.Compare(Name, typeRegistration.Name, StringComparison.Ordinal) != 0) + return false; + + return true; + } + + public override int GetHashCode() + { + return _hashCode; + } + } + private readonly SafeDictionary _RegisteredTypes; +#if USE_OBJECT_CONSTRUCTOR + private delegate object ObjectConstructor(params object[] parameters); + private static readonly SafeDictionary _ObjectConstructorCache = new SafeDictionary(); +#endif +#endregion + +#region Constructors + public TinyIoCContainer() + { + _RegisteredTypes = new SafeDictionary(); + + RegisterDefaultTypes(); + } + + TinyIoCContainer _Parent; + private TinyIoCContainer(TinyIoCContainer parent) + : this() + { + _Parent = parent; + } +#endregion + +#region Internal Methods + private readonly object _AutoRegisterLock = new object(); + private void AutoRegisterInternal(IEnumerable assemblies, DuplicateImplementationActions duplicateAction, Func registrationPredicate) + { + lock (_AutoRegisterLock) + { + var types = assemblies.SelectMany(a => a.SafeGetTypes()).Where(t => !IsIgnoredType(t, registrationPredicate)).ToList(); + + var concreteTypes = types + .Where(type => type.IsClass() && (type.IsAbstract() == false) && (type != this.GetType() && (type.DeclaringType != this.GetType()) && (!type.IsGenericTypeDefinition()))) + .ToList(); + + foreach (var type in concreteTypes) + { + try + { + RegisterInternal(type, string.Empty, GetDefaultObjectFactory(type, type)); + } +#if PORTABLE || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 || NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6 + catch (MemberAccessException) +#else + catch (MethodAccessException) +#endif + { + // Ignore methods we can't access - added for Silverlight + } + } + + var abstractInterfaceTypes = from type in types + where ((type.IsInterface() || type.IsAbstract()) && (type.DeclaringType != this.GetType()) && (!type.IsGenericTypeDefinition())) + select type; + + foreach (var type in abstractInterfaceTypes) + { + var localType = type; + var implementations = from implementationType in concreteTypes + where localType.IsAssignableFrom(implementationType) + select implementationType; + + if (implementations.Skip(1).Any()) + { + if (duplicateAction == DuplicateImplementationActions.Fail) + throw new TinyIoCAutoRegistrationException(type, implementations); + + if (duplicateAction == DuplicateImplementationActions.RegisterMultiple) + { + RegisterMultiple(type, implementations); + } + } + + var firstImplementation = implementations.FirstOrDefault(); + if (firstImplementation != null) + { + try + { + RegisterInternal(type, string.Empty, GetDefaultObjectFactory(type, firstImplementation)); + } +#if PORTABLE || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 || NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6 + catch (MemberAccessException) +#else + catch (MethodAccessException) +#endif + { + // Ignore methods we can't access - added for Silverlight + } + } + } + } + } + + private bool IsIgnoredAssembly(Assembly assembly) + { + // TODO - find a better way to remove "system" assemblies from the auto registration + var ignoreChecks = new List>() + { + asm => asm.FullName.StartsWith("Microsoft.", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("System.", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("System,", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("CR_ExtUnitTest", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("mscorlib,", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("CR_VSTest", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("DevExpress.CodeRush", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("xunit.", StringComparison.Ordinal), + }; + + foreach (var check in ignoreChecks) + { + if (check(assembly)) + return true; + } + + return false; + } + + private bool IsIgnoredType(Type type, Func registrationPredicate) + { + // TODO - find a better way to remove "system" types from the auto registration + var ignoreChecks = new List>() + { + t => t.FullName.StartsWith("System.", StringComparison.Ordinal), + t => t.FullName.StartsWith("Microsoft.", StringComparison.Ordinal), + t => t.IsPrimitive(), +#if !UNBOUND_GENERICS_GETCONSTRUCTORS + t => t.IsGenericTypeDefinition(), +#endif + t => (t.GetConstructors(BindingFlags.Instance | BindingFlags.Public).Length == 0) && !(t.IsInterface() || t.IsAbstract()), + }; + + if (registrationPredicate != null) + { + ignoreChecks.Add(t => !registrationPredicate(t)); + } + + foreach (var check in ignoreChecks) + { + if (check(type)) + return true; + } + + return false; + } + + private void RegisterDefaultTypes() + { + Register(this); + +#if TINYMESSENGER + // Only register the TinyMessenger singleton if we are the root container + if (_Parent == null) + Register(); +#endif + } + + private ObjectFactoryBase GetCurrentFactory(TypeRegistration registration) + { + ObjectFactoryBase current = null; + + _RegisteredTypes.TryGetValue(registration, out current); + + return current; + } + + private RegisterOptions RegisterInternal(Type registerType, string name, ObjectFactoryBase factory) + { + var typeRegistration = new TypeRegistration(registerType, name); + + return AddUpdateRegistration(typeRegistration, factory); + } + + private RegisterOptions AddUpdateRegistration(TypeRegistration typeRegistration, ObjectFactoryBase factory) + { + _RegisteredTypes[typeRegistration] = factory; + + return new RegisterOptions(this, typeRegistration); + } + + private bool RemoveRegistration(TypeRegistration typeRegistration) + { + return _RegisteredTypes.Remove(typeRegistration); + } + + private ObjectFactoryBase GetDefaultObjectFactory(Type registerType, Type registerImplementation) + { + //#if NETFX_CORE + // if (registerType.GetTypeInfo().IsInterface() || registerType.GetTypeInfo().IsAbstract()) + //#else + if (registerType.IsInterface() || registerType.IsAbstract()) + //#endif + return new SingletonFactory(registerType, registerImplementation); + + return new MultiInstanceFactory(registerType, registerImplementation); + } + + private bool CanResolveInternal(TypeRegistration registration, NamedParameterOverloads parameters, ResolveOptions options) + { + if (parameters == null) + throw new ArgumentNullException("parameters"); + + Type checkType = registration.Type; + string name = registration.Name; + + ObjectFactoryBase factory; + if (_RegisteredTypes.TryGetValue(new TypeRegistration(checkType, name), out factory)) + { + if (factory.AssumeConstruction) + return true; + + if (factory.Constructor == null) + return (GetBestConstructor(factory.CreatesType, parameters, options) != null) ? true : false; + else + return CanConstruct(factory.Constructor, parameters, options); + } + +#if RESOLVE_OPEN_GENERICS + if (checkType.IsInterface() && checkType.IsGenericType()) + { + // if the type is registered as an open generic, then see if the open generic is registered + if (_RegisteredTypes.TryGetValue(new TypeRegistration(checkType.GetGenericTypeDefinition(), name), out factory)) + { + if (factory.AssumeConstruction) + return true; + + if (factory.Constructor == null) + return (GetBestConstructor(factory.CreatesType, parameters, options) != null) ? true : false; + else + return CanConstruct(factory.Constructor, parameters, options); + } + } +#endif + + // Fail if requesting named resolution and settings set to fail if unresolved + // Or bubble up if we have a parent + if (!string.IsNullOrEmpty(name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.Fail) + return (_Parent != null) ? _Parent.CanResolveInternal(registration, parameters, options) : false; + + // Attemped unnamed fallback container resolution if relevant and requested + if (!string.IsNullOrEmpty(name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.AttemptUnnamedResolution) + { + if (_RegisteredTypes.TryGetValue(new TypeRegistration(checkType), out factory)) + { + if (factory.AssumeConstruction) + return true; + + return (GetBestConstructor(factory.CreatesType, parameters, options) != null) ? true : false; + } + } + + // Check if type is an automatic lazy factory request + if (IsAutomaticLazyFactoryRequest(checkType)) + return true; + + // Check if type is an IEnumerable + if (IsIEnumerableRequest(registration.Type)) + return true; + + // Attempt unregistered construction if possible and requested + // If we cant', bubble if we have a parent + if ((options.UnregisteredResolutionAction == UnregisteredResolutionActions.AttemptResolve) || (checkType.IsGenericType() && options.UnregisteredResolutionAction == UnregisteredResolutionActions.GenericsOnly)) + return (GetBestConstructor(checkType, parameters, options) != null) ? true : (_Parent != null) ? _Parent.CanResolveInternal(registration, parameters, options) : false; + + // Bubble resolution up the container tree if we have a parent + if (_Parent != null) + return _Parent.CanResolveInternal(registration, parameters, options); + + return false; + } + + private bool IsIEnumerableRequest(Type type) + { + if (!type.IsGenericType()) + return false; + + Type genericType = type.GetGenericTypeDefinition(); + + if (genericType == typeof(IEnumerable<>)) + return true; + + return false; + } + + private bool IsAutomaticLazyFactoryRequest(Type type) + { + if (!type.IsGenericType()) + return false; + + Type genericType = type.GetGenericTypeDefinition(); + + // Just a func + if (genericType == typeof(Func<>)) + return true; + + // 2 parameter func with string as first parameter (name) + //#if NETFX_CORE + // if ((genericType == typeof(Func<,>) && type.GetTypeInfo().GenericTypeArguments[0] == typeof(string))) + //#else + if ((genericType == typeof(Func<,>) && type.GetGenericArguments()[0] == typeof(string))) + //#endif + return true; + + // 3 parameter func with string as first parameter (name) and IDictionary as second (parameters) + //#if NETFX_CORE + // if ((genericType == typeof(Func<,,>) && type.GetTypeInfo().GenericTypeArguments[0] == typeof(string) && type.GetTypeInfo().GenericTypeArguments[1] == typeof(IDictionary))) + //#else + if ((genericType == typeof(Func<,,>) && type.GetGenericArguments()[0] == typeof(string) && type.GetGenericArguments()[1] == typeof(IDictionary))) + //#endif + return true; + + return false; + } + + private ObjectFactoryBase GetParentObjectFactory(TypeRegistration registration) + { + if (_Parent == null) + return null; + + ObjectFactoryBase factory; + if (_Parent._RegisteredTypes.TryGetValue(registration, out factory)) + { + return factory.GetFactoryForChildContainer(registration.Type, _Parent, this); + } + + return _Parent.GetParentObjectFactory(registration); + } + + private object ResolveInternal(TypeRegistration registration, NamedParameterOverloads parameters, ResolveOptions options) + { + ObjectFactoryBase factory; + + // Attempt container resolution + if (_RegisteredTypes.TryGetValue(registration, out factory)) + { + try + { + return factory.GetObject(registration.Type, this, parameters, options); + } + catch (TinyIoCResolutionException) + { + throw; + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(registration.Type, ex); + } + } + +#if RESOLVE_OPEN_GENERICS + // Attempt container resolution of open generic + if (registration.Type.IsGenericType()) + { + var openTypeRegistration = new TypeRegistration(registration.Type.GetGenericTypeDefinition(), + registration.Name); + + if (_RegisteredTypes.TryGetValue(openTypeRegistration, out factory)) + { + try + { + return factory.GetObject(registration.Type, this, parameters, options); + } + catch (TinyIoCResolutionException) + { + throw; + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(registration.Type, ex); + } + } + } +#endif + + // Attempt to get a factory from parent if we can + var bubbledObjectFactory = GetParentObjectFactory(registration); + if (bubbledObjectFactory != null) + { + try + { + return bubbledObjectFactory.GetObject(registration.Type, this, parameters, options); + } + catch (TinyIoCResolutionException) + { + throw; + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(registration.Type, ex); + } + } + + // Fail if requesting named resolution and settings set to fail if unresolved + if (!string.IsNullOrEmpty(registration.Name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.Fail) + throw new TinyIoCResolutionException(registration.Type); + + // Attemped unnamed fallback container resolution if relevant and requested + if (!string.IsNullOrEmpty(registration.Name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.AttemptUnnamedResolution) + { + if (_RegisteredTypes.TryGetValue(new TypeRegistration(registration.Type, string.Empty), out factory)) + { + try + { + return factory.GetObject(registration.Type, this, parameters, options); + } + catch (TinyIoCResolutionException) + { + throw; + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(registration.Type, ex); + } + } + } + +#if EXPRESSIONS + // Attempt to construct an automatic lazy factory if possible + if (IsAutomaticLazyFactoryRequest(registration.Type)) + return GetLazyAutomaticFactoryRequest(registration.Type); +#endif + if (IsIEnumerableRequest(registration.Type)) + return GetIEnumerableRequest(registration.Type); + + // Attempt unregistered construction if possible and requested + if ((options.UnregisteredResolutionAction == UnregisteredResolutionActions.AttemptResolve) || (registration.Type.IsGenericType() && options.UnregisteredResolutionAction == UnregisteredResolutionActions.GenericsOnly)) + { + if (!registration.Type.IsAbstract() && !registration.Type.IsInterface()) + return ConstructType(null, registration.Type, parameters, options); + } + + // Unable to resolve - throw + throw new TinyIoCResolutionException(registration.Type); + } + +#if EXPRESSIONS + private object GetLazyAutomaticFactoryRequest(Type type) + { + if (!type.IsGenericType()) + return null; + + Type genericType = type.GetGenericTypeDefinition(); + //#if NETFX_CORE + // Type[] genericArguments = type.GetTypeInfo().GenericTypeArguments.ToArray(); + //#else + Type[] genericArguments = type.GetGenericArguments(); + //#endif + + // Just a func + if (genericType == typeof(Func<>)) + { + Type returnType = genericArguments[0]; + + //#if NETFX_CORE + // MethodInfo resolveMethod = typeof(TinyIoCContainer).GetTypeInfo().GetDeclaredMethods("Resolve").First(mi => !mi.GetParameters().Any()); + //#else + MethodInfo resolveMethod = typeof(TinyIoCContainer).GetMethod("Resolve", new Type[] { }); + //#endif + resolveMethod = resolveMethod.MakeGenericMethod(returnType); + + var resolveCall = Expression.Call(Expression.Constant(this), resolveMethod); + + var resolveLambda = Expression.Lambda(resolveCall).Compile(); + + return resolveLambda; + } + + // 2 parameter func with string as first parameter (name) + if ((genericType == typeof(Func<,>)) && (genericArguments[0] == typeof(string))) + { + Type returnType = genericArguments[1]; + + //#if NETFX_CORE + // MethodInfo resolveMethod = typeof(TinyIoCContainer).GetTypeInfo().GetDeclaredMethods("Resolve").First(mi => mi.GetParameters().Length == 1 && mi.GetParameters()[0].GetType() == typeof(String)); + //#else + MethodInfo resolveMethod = typeof(TinyIoCContainer).GetMethod("Resolve", new Type[] { typeof(String) }); + //#endif + resolveMethod = resolveMethod.MakeGenericMethod(returnType); + + ParameterExpression[] resolveParameters = new ParameterExpression[] { Expression.Parameter(typeof(String), "name") }; + var resolveCall = Expression.Call(Expression.Constant(this), resolveMethod, resolveParameters); + + var resolveLambda = Expression.Lambda(resolveCall, resolveParameters).Compile(); + + return resolveLambda; + } + + // 3 parameter func with string as first parameter (name) and IDictionary as second (parameters) + //#if NETFX_CORE + // if ((genericType == typeof(Func<,,>) && type.GenericTypeArguments[0] == typeof(string) && type.GenericTypeArguments[1] == typeof(IDictionary))) + //#else + if ((genericType == typeof(Func<,,>) && type.GetGenericArguments()[0] == typeof(string) && type.GetGenericArguments()[1] == typeof(IDictionary))) + //#endif + { + Type returnType = genericArguments[2]; + + var name = Expression.Parameter(typeof(string), "name"); + var parameters = Expression.Parameter(typeof(IDictionary), "parameters"); + + //#if NETFX_CORE + // MethodInfo resolveMethod = typeof(TinyIoCContainer).GetTypeInfo().GetDeclaredMethods("Resolve").First(mi => mi.GetParameters().Length == 2 && mi.GetParameters()[0].GetType() == typeof(String) && mi.GetParameters()[1].GetType() == typeof(NamedParameterOverloads)); + //#else + MethodInfo resolveMethod = typeof(TinyIoCContainer).GetMethod("Resolve", new Type[] { typeof(String), typeof(NamedParameterOverloads) }); + //#endif + resolveMethod = resolveMethod.MakeGenericMethod(returnType); + + var resolveCall = Expression.Call(Expression.Constant(this), resolveMethod, name, Expression.Call(typeof(NamedParameterOverloads), "FromIDictionary", null, parameters)); + + var resolveLambda = Expression.Lambda(resolveCall, name, parameters).Compile(); + + return resolveLambda; + } + + throw new TinyIoCResolutionException(type); + } +#endif + private object GetIEnumerableRequest(Type type) + { + //#if NETFX_CORE + // var genericResolveAllMethod = this.GetType().GetGenericMethod("ResolveAll", type.GenericTypeArguments, new[] { typeof(bool) }); + //#else + var genericResolveAllMethod = this.GetType().GetGenericMethod(BindingFlags.Public | BindingFlags.Instance, "ResolveAll", type.GetGenericArguments(), new[] { typeof(bool) }); + //#endif + + return genericResolveAllMethod.Invoke(this, new object[] { false }); + } + + private bool CanConstruct(ConstructorInfo ctor, NamedParameterOverloads parameters, ResolveOptions options) + { + if (parameters == null) + throw new ArgumentNullException("parameters"); + + foreach (var parameter in ctor.GetParameters()) + { + if (string.IsNullOrEmpty(parameter.Name)) + return false; + + var isParameterOverload = parameters.ContainsKey(parameter.Name); + + //#if NETFX_CORE + // if (parameter.ParameterType.GetTypeInfo().IsPrimitive && !isParameterOverload) + //#else + if (parameter.ParameterType.IsPrimitive() && !isParameterOverload) + //#endif + return false; + + if (!isParameterOverload && !CanResolveInternal(new TypeRegistration(parameter.ParameterType), NamedParameterOverloads.Default, options)) + return false; + } + + return true; + } + + private ConstructorInfo GetBestConstructor(Type type, NamedParameterOverloads parameters, ResolveOptions options) + { + if (parameters == null) + throw new ArgumentNullException("parameters"); + + //#if NETFX_CORE + // if (type.GetTypeInfo().IsValueType) + //#else + if (type.IsValueType()) + //#endif + return null; + + // Get constructors in reverse order based on the number of parameters + // i.e. be as "greedy" as possible so we satify the most amount of dependencies possible + var ctors = this.GetTypeConstructors(type); + + foreach (var ctor in ctors) + { + if (this.CanConstruct(ctor, parameters, options)) + return ctor; + } + + return null; + } + + private IEnumerable GetTypeConstructors(Type type) + { + //#if NETFX_CORE + // return type.GetTypeInfo().DeclaredConstructors.OrderByDescending(ctor => ctor.GetParameters().Count()); + //#else + return type.GetConstructors().OrderByDescending(ctor => ctor.GetParameters().Count()); + //#endif + } + + private object ConstructType(Type requestedType, Type implementationType, ResolveOptions options) + { + return ConstructType(requestedType, implementationType, null, NamedParameterOverloads.Default, options); + } + + private object ConstructType(Type requestedType, Type implementationType, ConstructorInfo constructor, ResolveOptions options) + { + return ConstructType(requestedType, implementationType, constructor, NamedParameterOverloads.Default, options); + } + + private object ConstructType(Type requestedType, Type implementationType, NamedParameterOverloads parameters, ResolveOptions options) + { + return ConstructType(requestedType, implementationType, null, parameters, options); + } + + private object ConstructType(Type requestedType, Type implementationType, ConstructorInfo constructor, NamedParameterOverloads parameters, ResolveOptions options) + { + var typeToConstruct = implementationType; + +#if RESOLVE_OPEN_GENERICS + if (implementationType.IsGenericTypeDefinition()) + { + if (requestedType == null || !requestedType.IsGenericType() || !requestedType.GetGenericArguments().Any()) + throw new TinyIoCResolutionException(typeToConstruct); + + typeToConstruct = typeToConstruct.MakeGenericType(requestedType.GetGenericArguments()); + } +#endif + if (constructor == null) + { + // Try and get the best constructor that we can construct + // if we can't construct any then get the constructor + // with the least number of parameters so we can throw a meaningful + // resolve exception + constructor = GetBestConstructor(typeToConstruct, parameters, options) ?? GetTypeConstructors(typeToConstruct).LastOrDefault(); + } + + if (constructor == null) + throw new TinyIoCResolutionException(typeToConstruct); + + var ctorParams = constructor.GetParameters(); + object[] args = new object[ctorParams.Count()]; + + for (int parameterIndex = 0; parameterIndex < ctorParams.Count(); parameterIndex++) + { + var currentParam = ctorParams[parameterIndex]; + + try + { + args[parameterIndex] = parameters.ContainsKey(currentParam.Name) ? + parameters[currentParam.Name] : + ResolveInternal( + new TypeRegistration(currentParam.ParameterType), + NamedParameterOverloads.Default, + options); + } + catch (TinyIoCResolutionException ex) + { + // If a constructor parameter can't be resolved + // it will throw, so wrap it and throw that this can't + // be resolved. + throw new TinyIoCResolutionException(typeToConstruct, ex); + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(typeToConstruct, ex); + } + } + + try + { +#if USE_OBJECT_CONSTRUCTOR + var constructionDelegate = CreateObjectConstructionDelegateWithCache(constructor); + return constructionDelegate.Invoke(args); +#else + return constructor.Invoke(args); +#endif + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(typeToConstruct, ex); + } + } + +#if USE_OBJECT_CONSTRUCTOR + private static ObjectConstructor CreateObjectConstructionDelegateWithCache(ConstructorInfo constructor) + { + ObjectConstructor objectConstructor; + if (_ObjectConstructorCache.TryGetValue(constructor, out objectConstructor)) + return objectConstructor; + + // We could lock the cache here, but there's no real side + // effect to two threads creating the same ObjectConstructor + // at the same time, compared to the cost of a lock for + // every creation. + var constructorParams = constructor.GetParameters(); + var lambdaParams = Expression.Parameter(typeof(object[]), "parameters"); + var newParams = new Expression[constructorParams.Length]; + + for (int i = 0; i < constructorParams.Length; i++) + { + var paramsParameter = Expression.ArrayIndex(lambdaParams, Expression.Constant(i)); + + newParams[i] = Expression.Convert(paramsParameter, constructorParams[i].ParameterType); + } + + var newExpression = Expression.New(constructor, newParams); + + var constructionLambda = Expression.Lambda(typeof(ObjectConstructor), newExpression, lambdaParams); + + objectConstructor = (ObjectConstructor)constructionLambda.Compile(); + + _ObjectConstructorCache[constructor] = objectConstructor; + return objectConstructor; + } +#endif + + private void BuildUpInternal(object input, ResolveOptions resolveOptions) + { + //#if NETFX_CORE + // var properties = from property in input.GetType().GetTypeInfo().DeclaredProperties + // where (property.GetMethod != null) && (property.SetMethod != null) && !property.PropertyType.GetTypeInfo().IsValueType + // select property; + //#else + var properties = from property in input.GetType().GetProperties() + where (property.GetGetMethod() != null) && (property.GetSetMethod() != null) && !property.PropertyType.IsValueType() + select property; + //#endif + + foreach (var property in properties) + { + if (property.GetValue(input, null) == null) + { + try + { + property.SetValue(input, ResolveInternal(new TypeRegistration(property.PropertyType), NamedParameterOverloads.Default, resolveOptions), null); + } + catch (TinyIoCResolutionException) + { + // Catch any resolution errors and ignore them + } + } + } + } + + private IEnumerable GetParentRegistrationsForType(Type resolveType) + { + if (_Parent == null) + return new TypeRegistration[] { }; + + var registrations = _Parent._RegisteredTypes.Keys.Where(tr => tr.Type == resolveType); + + return registrations.Concat(_Parent.GetParentRegistrationsForType(resolveType)); + } + + private IEnumerable ResolveAllInternal(Type resolveType, bool includeUnnamed) + { + var registrations = _RegisteredTypes.Keys.Where(tr => tr.Type == resolveType).Concat(GetParentRegistrationsForType(resolveType)).Distinct(); + + if (!includeUnnamed) + registrations = registrations.Where(tr => tr.Name != string.Empty); + + return registrations.Select(registration => this.ResolveInternal(registration, NamedParameterOverloads.Default, ResolveOptions.Default)); + } + + private static bool IsValidAssignment(Type registerType, Type registerImplementation) + { + if (!registerType.IsGenericTypeDefinition()) + { + if (!registerType.IsAssignableFrom(registerImplementation)) + return false; + } + else + { + if (registerType.IsInterface()) + { +#if (PORTABLE || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 || NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6) + if (!registerImplementation.GetInterfaces().Any(t => t.Name == registerType.Name)) + return false; +#else + if (!registerImplementation.FindInterfaces((t, o) => t.Name == registerType.Name, null).Any()) + return false; +#endif + } + else if (registerType.IsAbstract() && registerImplementation.BaseType() != registerType) + { + return false; + } + } + //#endif + return true; + } + +#endregion + +#region IDisposable Members + bool disposed = false; + public void Dispose() + { + if (!disposed) + { + disposed = true; + + _RegisteredTypes.Dispose(); + + GC.SuppressFinalize(this); + } + } + +#endregion + } + +#if PORTABLE || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 || NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6 + static class ReverseTypeExtender + { + public static bool IsClass(this Type type) + { + return type.GetTypeInfo().IsClass; + } + + public static bool IsAbstract(this Type type) + { + return type.GetTypeInfo().IsAbstract; + } + + public static bool IsInterface(this Type type) + { + return type.GetTypeInfo().IsInterface; + } + + public static bool IsPrimitive(this Type type) + { + return type.GetTypeInfo().IsPrimitive; + } + + public static bool IsValueType(this Type type) + { + return type.GetTypeInfo().IsValueType; + } + + public static bool IsGenericType(this Type type) + { + return type.GetTypeInfo().IsGenericType; + } + + public static bool IsGenericParameter(this Type type) + { + return type.IsGenericParameter; + } + + public static bool IsGenericTypeDefinition(this Type type) + { + return type.GetTypeInfo().IsGenericTypeDefinition; + } + + public static Type BaseType(this Type type) + { + return type.GetTypeInfo().BaseType; + } + + public static Assembly Assembly(this Type type) + { + return type.GetTypeInfo().Assembly; + } + } +#endif + // reverse shim for WinRT SR changes... +#if (!NETFX_CORE && !PORTABLE && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_4 && !NETSTANDARD1_5 || !NETSTANDARD1_6) + static class ReverseTypeExtender + { + public static bool IsClass(this Type type) + { + return type.IsClass; + } + + public static bool IsAbstract(this Type type) + { + return type.IsAbstract; + } + + public static bool IsInterface(this Type type) + { + return type.IsInterface; + } + + public static bool IsPrimitive(this Type type) + { + return type.IsPrimitive; + } + + public static bool IsValueType(this Type type) + { + return type.IsValueType; + } + + public static bool IsGenericType(this Type type) + { + return type.IsGenericType; + } + + public static bool IsGenericParameter(this Type type) + { + return type.IsGenericParameter; + } + + public static bool IsGenericTypeDefinition(this Type type) + { + return type.IsGenericTypeDefinition; + } + + public static Type BaseType(this Type type) + { + return type.BaseType; + } + + public static Assembly Assembly(this Type type) + { + return type.Assembly; + } + } +#endif +} diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ViewModelLocator.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ViewModelLocator.cs index f30f2b18e..08c2bd108 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ViewModelLocator.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ViewModelLocator.cs @@ -1,5 +1,4 @@ -using Autofac; -using eShopOnContainers.Services; +using eShopOnContainers.Services; using System; using System.Globalization; using System.Reflection; @@ -13,105 +12,109 @@ using eShopOnContainers.Core.Services.User; using Xamarin.Forms; using eShopOnContainers.Core.Services.Location; using eShopOnContainers.Core.Services.Marketing; +using TinyIoC; namespace eShopOnContainers.Core.ViewModels.Base { public static class ViewModelLocator { - private static IContainer _container; - - public static readonly BindableProperty AutoWireViewModelProperty = - BindableProperty.CreateAttached("AutoWireViewModel", typeof(bool), typeof(ViewModelLocator), default(bool), propertyChanged: OnAutoWireViewModelChanged); - - public static bool GetAutoWireViewModel(BindableObject bindable) - { - return (bool)bindable.GetValue(ViewModelLocator.AutoWireViewModelProperty); - } - - public static void SetAutoWireViewModel(BindableObject bindable, bool value) - { - bindable.SetValue(ViewModelLocator.AutoWireViewModelProperty, value); - } - - public static bool UseMockService { get; set; } - - public static void RegisterDependencies(bool useMockServices) - { - var builder = new ContainerBuilder(); - - // View models - builder.RegisterType(); - builder.RegisterType(); - builder.RegisterType(); - builder.RegisterType(); - builder.RegisterType(); - builder.RegisterType(); - builder.RegisterType(); - builder.RegisterType(); - builder.RegisterType(); - builder.RegisterType(); + private static TinyIoCContainer _container; + + public static readonly BindableProperty AutoWireViewModelProperty = + BindableProperty.CreateAttached("AutoWireViewModel", typeof(bool), typeof(ViewModelLocator), default(bool), propertyChanged: OnAutoWireViewModelChanged); + + public static bool GetAutoWireViewModel(BindableObject bindable) + { + return (bool)bindable.GetValue(ViewModelLocator.AutoWireViewModelProperty); + } + + public static void SetAutoWireViewModel(BindableObject bindable, bool value) + { + bindable.SetValue(ViewModelLocator.AutoWireViewModelProperty, value); + } + + public static bool UseMockService { get; set; } + + static ViewModelLocator() + { + _container = new TinyIoCContainer(); + + // View models + _container.Register(); + _container.Register(); + _container.Register(); + _container.Register(); + _container.Register(); + _container.Register(); + _container.Register(); + _container.Register(); + _container.Register(); + _container.Register(); // Services - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As().SingleInstance(); - + _container.Register().AsSingleton(); + _container.Register(); + _container.Register(); + _container.Register(); + _container.Register(); + _container.Register().AsSingleton(); + _container.Register().AsSingleton(); + _container.Register().AsSingleton(); + _container.Register().AsSingleton(); + _container.Register().AsSingleton(); + _container.Register().AsSingleton(); + } + + public static void UpdateDependencies(bool useMockServices) + { + // Change injected dependencies if (useMockServices) - { - builder.RegisterInstance(new CatalogMockService()).As(); - builder.RegisterInstance(new BasketMockService()).As(); - builder.RegisterInstance(new OrderMockService()).As(); - builder.RegisterInstance(new UserMockService()).As(); - builder.RegisterInstance(new CampaignMockService()).As(); + { + _container.Register().AsSingleton(); + _container.Register().AsSingleton(); + _container.Register().AsSingleton(); + _container.Register().AsSingleton(); + _container.Register().AsSingleton(); UseMockService = true; - } - else - { - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); + } + else + { + _container.Register().AsSingleton(); + _container.Register().AsSingleton(); + _container.Register().AsSingleton(); + _container.Register().AsSingleton(); + _container.Register().AsSingleton(); UseMockService = false; - } - - if (_container != null) - { - _container.Dispose(); - } - _container = builder.Build(); - } - - public static T Resolve() - { - return _container.Resolve(); - } - - private static void OnAutoWireViewModelChanged(BindableObject bindable, object oldValue, object newValue) - { - var view = bindable as Element; - if (view == null) - { - return; - } - - var viewType = view.GetType(); - var viewName = viewType.FullName.Replace(".Views.", ".ViewModels."); - var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName; - var viewModelName = string.Format(CultureInfo.InvariantCulture, "{0}Model, {1}", viewName, viewAssemblyName); - - var viewModelType = Type.GetType(viewModelName); - if (viewModelType == null) - { - return; - } - var viewModel = _container.Resolve(viewModelType); - view.BindingContext = viewModel; - } - } + } + } + + public static T Resolve() where T : class + { + return _container.Resolve(); + } + + private static void OnAutoWireViewModelChanged(BindableObject bindable, object oldValue, object newValue) + { + var view = bindable as Element; + if (view == null) + { + return; + } + + var viewType = view.GetType(); + var viewName = viewType.FullName.Replace(".Views.", ".ViewModels."); + var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName; + var viewModelName = string.Format(CultureInfo.InvariantCulture, "{0}Model, {1}", viewName, viewAssemblyName); + + var viewModelType = Type.GetType(viewModelName); + if (viewModelType == null) + { + return; + } + var viewModel = _container.Resolve(viewModelType); + view.BindingContext = viewModel; + } + } } \ No newline at end of file diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/SettingsViewModel.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/SettingsViewModel.cs index e6e32393f..6e3af4864 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/SettingsViewModel.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/SettingsViewModel.cs @@ -71,7 +71,7 @@ namespace eShopOnContainers.Core.ViewModels _useAzureServices = value; UpdateUseAzureServices(); - + RaisePropertyChanged(() => UseAzureServices); } } @@ -146,7 +146,7 @@ namespace eShopOnContainers.Core.ViewModels { _endpoint = value; - if(!string.IsNullOrEmpty(_endpoint)) + if (!string.IsNullOrEmpty(_endpoint)) { UpdateEndpoint(); } @@ -213,32 +213,32 @@ namespace eShopOnContainers.Core.ViewModels return base.InitializeAsync(navigationData); } - private async Task ToggleMockServicesAsync() - { - ViewModelLocator.RegisterDependencies(!UseAzureServices); - UpdateInfoUseAzureServices(); - - var previousPageViewModel = NavigationService.PreviousPageViewModel; - if (previousPageViewModel != null) - { - if (previousPageViewModel is MainViewModel) - { - // Slight delay so that page navigation isn't instantaneous - await Task.Delay(1000); - if (UseAzureServices) - { - Settings.AuthAccessToken = string.Empty; - Settings.AuthIdToken = string.Empty; - await NavigationService.NavigateToAsync(new LogoutParameter { Logout = true }); - await NavigationService.RemoveBackStackAsync(); - } - } - } - } + private async Task ToggleMockServicesAsync() + { + ViewModelLocator.UpdateDependencies(!UseAzureServices); + UpdateInfoUseAzureServices(); + + var previousPageViewModel = NavigationService.PreviousPageViewModel; + if (previousPageViewModel != null) + { + if (previousPageViewModel is MainViewModel) + { + // Slight delay so that page navigation isn't instantaneous + await Task.Delay(1000); + if (UseAzureServices) + { + Settings.AuthAccessToken = string.Empty; + Settings.AuthIdToken = string.Empty; + await NavigationService.NavigateToAsync(new LogoutParameter { Logout = true }); + await NavigationService.RemoveBackStackAsync(); + } + } + } + } private void ToggleFakeLocationAsync() { - ViewModelLocator.RegisterDependencies(!UseAzureServices); + ViewModelLocator.UpdateDependencies(!UseAzureServices); UpdateInfoFakeLocation(); } @@ -254,7 +254,7 @@ namespace eShopOnContainers.Core.ViewModels var authToken = Settings.AuthAccessToken; await _locationService.UpdateUserLocation(locationRequest, authToken); - } + } } private void ToggleAllowGpsLocation() diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj index bc631506d..877fdcedf 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj @@ -14,7 +14,6 @@ - diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/eShopOnContainers.Droid.csproj b/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/eShopOnContainers.Droid.csproj index a8c8db3b7..9f0677863 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/eShopOnContainers.Droid.csproj +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/eShopOnContainers.Droid.csproj @@ -184,9 +184,6 @@ ..\..\..\..\packages\IdentityModel.3.0.0\lib\netstandard2.0\IdentityModel.dll - - ..\..\..\..\packages\Autofac.4.6.2\lib\netstandard1.1\Autofac.dll - ..\..\..\..\packages\PInvoke.Windows.Core.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Windows.Core.dll diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/packages.config b/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/packages.config index 8de7bc189..8ad5de972 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/packages.config +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/packages.config @@ -3,7 +3,6 @@ - diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.Droid/eShopOnContainers.TestRunner.Droid.csproj b/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.Droid/eShopOnContainers.TestRunner.Droid.csproj index b3a87f24e..a98319629 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.Droid/eShopOnContainers.TestRunner.Droid.csproj +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.Droid/eShopOnContainers.TestRunner.Droid.csproj @@ -151,9 +151,6 @@ ..\..\..\..\packages\IdentityModel.3.0.0\lib\netstandard2.0\IdentityModel.dll - - ..\..\..\..\packages\Autofac.4.6.2\lib\netstandard1.1\Autofac.dll - ..\..\..\..\packages\Xam.Plugins.Settings.3.1.1\lib\MonoAndroid10\Plugin.Settings.Abstractions.dll diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.Droid/packages.config b/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.Droid/packages.config index 250b32aa3..38ffb765b 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.Droid/packages.config +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.Droid/packages.config @@ -3,7 +3,6 @@ - diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj b/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj index 9db0b441c..36da36c74 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj @@ -144,9 +144,6 @@ ..\..\..\..\packages\xunit.extensibility.execution.2.3.1\lib\netstandard1.1\xunit.execution.dotnet.dll - - ..\..\..\..\packages\Autofac.4.6.2\lib\netstandard1.1\Autofac.dll - ..\..\..\..\packages\Newtonsoft.Json.10.0.3\lib\netstandard1.3\Newtonsoft.Json.dll diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/packages.config b/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/packages.config index 2fb682a30..0c8e037b4 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/packages.config +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/packages.config @@ -1,6 +1,5 @@  - diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/eShopOnContainers.iOS.csproj b/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/eShopOnContainers.iOS.csproj index ebc355be7..b2a962284 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/eShopOnContainers.iOS.csproj +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/eShopOnContainers.iOS.csproj @@ -173,9 +173,6 @@ ..\..\..\..\packages\SlideOverKit.2.1.5\lib\Xamarin.iOS10\SlideOverKit.iOS.dll - - ..\..\..\..\packages\Autofac.4.6.2\lib\netstandard1.1\Autofac.dll - ..\..\..\..\packages\Xam.Plugins.Settings.3.1.1\lib\Xamarin.iOS10\Plugin.Settings.Abstractions.dll @@ -207,6 +204,27 @@ ..\..\..\..\packages\Xam.Plugin.Geolocator.3.0.4\lib\Xamarin.iOS10\Plugin.Geolocator.dll + + ..\..\..\..\packages\PInvoke.Windows.Core.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Windows.Core.dll + + + ..\..\..\..\packages\PInvoke.Kernel32.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Kernel32.dll + + + ..\..\..\..\packages\PInvoke.BCrypt.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.BCrypt.dll + + + ..\..\..\..\packages\PInvoke.NCrypt.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.NCrypt.dll + + + ..\..\..\..\packages\Validation.2.2.8\lib\dotnet\Validation.dll + + + ..\..\..\..\packages\PCLCrypto.2.0.147\lib\xamarinios10\PCLCrypto.dll + + + ..\..\..\..\packages\IdentityModel.3.0.0\lib\netstandard2.0\IdentityModel.dll + diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/packages.config b/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/packages.config index 03a3b7bea..ea0d4ffcb 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/packages.config +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/packages.config @@ -2,13 +2,18 @@ - + + + + + + @@ -58,6 +63,7 @@ + From 2ded6b8cfa1edd6edb4a58ed374feefb3da4c7a1 Mon Sep 17 00:00:00 2001 From: David Britch Date: Tue, 16 Jan 2018 14:21:34 +0000 Subject: [PATCH 2/3] Unit tests all passing. --- .../ViewModels/CatalogViewModelTests.cs | 2 +- .../ViewModels/MainViewModelTests.cs | 2 +- .../ViewModels/MarketingViewModelTests.cs | 2 +- .../ViewModels/MockViewModelTests.cs | 2 +- .../ViewModels/OrderViewModelTests.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/CatalogViewModelTests.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/CatalogViewModelTests.cs index d50f8f646..60a2ded03 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/CatalogViewModelTests.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/CatalogViewModelTests.cs @@ -12,7 +12,7 @@ namespace eShopOnContainers.UnitTests { public CatalogViewModelTests() { - ViewModelLocator.RegisterDependencies(true); + ViewModelLocator.UpdateDependencies(true); } [Fact] diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/MainViewModelTests.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/MainViewModelTests.cs index 1ccffcd34..ca768fc6d 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/MainViewModelTests.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/MainViewModelTests.cs @@ -10,7 +10,7 @@ namespace eShopOnContainers.UnitTests { public MainViewModelTests() { - ViewModelLocator.RegisterDependencies(true); + ViewModelLocator.UpdateDependencies(true); } [Fact] diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/MarketingViewModelTests.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/MarketingViewModelTests.cs index 248e39857..8c58e67eb 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/MarketingViewModelTests.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/MarketingViewModelTests.cs @@ -10,7 +10,7 @@ { public MarketingViewModelTests() { - ViewModelLocator.RegisterDependencies(true); + ViewModelLocator.UpdateDependencies(true); } [Fact] diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/MockViewModelTests.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/MockViewModelTests.cs index b6d6e4cc1..0821f4d98 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/MockViewModelTests.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/MockViewModelTests.cs @@ -9,7 +9,7 @@ namespace eShopOnContainers.UnitTests { public MockViewModelTests() { - ViewModelLocator.RegisterDependencies(true); + ViewModelLocator.UpdateDependencies(true); } [Fact] diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/OrderViewModelTests.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/OrderViewModelTests.cs index fbfc6a951..41e05ebc0 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/OrderViewModelTests.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/ViewModels/OrderViewModelTests.cs @@ -11,7 +11,7 @@ namespace eShopOnContainers.UnitTests { public OrderViewModelTests() { - ViewModelLocator.RegisterDependencies(true); + ViewModelLocator.UpdateDependencies(true); } [Fact] From e98651b2567b4cb6178001e93b5ae21eac4ce2d6 Mon Sep 17 00:00:00 2001 From: David Britch Date: Tue, 16 Jan 2018 14:54:25 +0000 Subject: [PATCH 3/3] Moved TinyIoC location. --- .../eShopOnContainers.Core/{Helpers => TinyIoC}/TinyIoC.cs | 0 .../eShopOnContainers.Core/eShopOnContainers.Core.csproj | 3 +++ 2 files changed, 3 insertions(+) rename src/Mobile/eShopOnContainers/eShopOnContainers.Core/{Helpers => TinyIoC}/TinyIoC.cs (100%) diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/TinyIoC.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/TinyIoC/TinyIoC.cs similarity index 100% rename from src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/TinyIoC.cs rename to src/Mobile/eShopOnContainers/eShopOnContainers.Core/TinyIoC/TinyIoC.cs diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj index 877fdcedf..900c16ada 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj @@ -16,4 +16,7 @@ + + +