// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection; using System.Web.Mvc.Properties; namespace System.Web.Mvc { public class DependencyResolver { private static DependencyResolver _instance = new DependencyResolver(); private IDependencyResolver _current; /// /// Cache should always be a new CacheDependencyResolver(_current). /// private CacheDependencyResolver _currentCache; public DependencyResolver() { InnerSetResolver(new DefaultDependencyResolver()); } public static IDependencyResolver Current { get { return _instance.InnerCurrent; } } internal static IDependencyResolver CurrentCache { get { return _instance.InnerCurrentCache; } } public IDependencyResolver InnerCurrent { get { return _current; } } /// /// Provides caching over results returned by Current. /// internal IDependencyResolver InnerCurrentCache { get { return _currentCache; } } public static void SetResolver(IDependencyResolver resolver) { _instance.InnerSetResolver(resolver); } public static void SetResolver(object commonServiceLocator) { _instance.InnerSetResolver(commonServiceLocator); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types.")] public static void SetResolver(Func getService, Func> getServices) { _instance.InnerSetResolver(getService, getServices); } public void InnerSetResolver(IDependencyResolver resolver) { if (resolver == null) { throw new ArgumentNullException("resolver"); } _current = resolver; _currentCache = new CacheDependencyResolver(_current); } public void InnerSetResolver(object commonServiceLocator) { if (commonServiceLocator == null) { throw new ArgumentNullException("commonServiceLocator"); } Type locatorType = commonServiceLocator.GetType(); MethodInfo getInstance = locatorType.GetMethod("GetInstance", new[] { typeof(Type) }); MethodInfo getInstances = locatorType.GetMethod("GetAllInstances", new[] { typeof(Type) }); if (getInstance == null || getInstance.ReturnType != typeof(object) || getInstances == null || getInstances.ReturnType != typeof(IEnumerable)) { throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, MvcResources.DependencyResolver_DoesNotImplementICommonServiceLocator, locatorType.FullName), "commonServiceLocator"); } var getService = (Func)Delegate.CreateDelegate(typeof(Func), commonServiceLocator, getInstance); var getServices = (Func>)Delegate.CreateDelegate(typeof(Func>), commonServiceLocator, getInstances); InnerSetResolver(new DelegateBasedDependencyResolver(getService, getServices)); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types.")] public void InnerSetResolver(Func getService, Func> getServices) { if (getService == null) { throw new ArgumentNullException("getService"); } if (getServices == null) { throw new ArgumentNullException("getServices"); } InnerSetResolver(new DelegateBasedDependencyResolver(getService, getServices)); } /// /// Wraps an IDependencyResolver and ensures single instance per-type. /// /// /// Note it's possible for multiple threads to race and call the _resolver service multiple times. /// We'll pick one winner and ignore the others and still guarantee a unique instance. /// private sealed class CacheDependencyResolver : IDependencyResolver { private readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); private readonly ConcurrentDictionary> _cacheMultiple = new ConcurrentDictionary>(); private readonly IDependencyResolver _resolver; public CacheDependencyResolver(IDependencyResolver resolver) { _resolver = resolver; } public object GetService(Type serviceType) { return _cache.GetOrAdd(serviceType, _resolver.GetService); } public IEnumerable GetServices(Type serviceType) { return _cacheMultiple.GetOrAdd(serviceType, _resolver.GetServices); } } private class DefaultDependencyResolver : IDependencyResolver { [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This method might throw exceptions whose type we cannot strongly link against; namely, ActivationException from common service locator")] public object GetService(Type serviceType) { // Since attempting to create an instance of an interface or an abstract type results in an exception, immediately return null // to improve performance and the debugging experience with first-chance exceptions enabled. if (serviceType.IsInterface || serviceType.IsAbstract) { return null; } try { return Activator.CreateInstance(serviceType); } catch { return null; } } public IEnumerable GetServices(Type serviceType) { return Enumerable.Empty(); } } private class DelegateBasedDependencyResolver : IDependencyResolver { private Func _getService; private Func> _getServices; public DelegateBasedDependencyResolver(Func getService, Func> getServices) { _getService = getService; _getServices = getServices; } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This method might throw exceptions whose type we cannot strongly link against; namely, ActivationException from common service locator")] public object GetService(Type type) { try { return _getService.Invoke(type); } catch { return null; } } public IEnumerable GetServices(Type type) { return _getServices(type); } } } }