diff --git a/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs b/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs index f2223d338e..6aa3cce51c 100644 --- a/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs +++ b/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs @@ -23,7 +23,7 @@ namespace Umbraco.Core.ObjectResolution where TResolver : ResolverBase { #region Constructors - + /// /// Initializes a new instance of the class with an empty list of objects, /// with creation of objects based on an HttpRequest lifetime scope. @@ -31,9 +31,11 @@ namespace Umbraco.Core.ObjectResolution /// The lifetime scope of instantiated objects, default is per Application. /// If is per HttpRequest then there must be a current HttpContext. /// is per HttpRequest but the current HttpContext is null. - protected LazyManyObjectsResolverBase(ObjectLifetimeScope scope = ObjectLifetimeScope.Application) - : base(scope) - { } + protected LazyManyObjectsResolverBase(ObjectLifetimeScope scope = ObjectLifetimeScope.Application) + : base(scope) + { + Initialize(); + } /// /// Initializes a new instance of the class with an empty list of objects, @@ -41,9 +43,11 @@ namespace Umbraco.Core.ObjectResolution /// /// The HttpContextBase corresponding to the HttpRequest. /// is null. - protected LazyManyObjectsResolverBase(HttpContextBase httpContext) - : base(httpContext) - { } + protected LazyManyObjectsResolverBase(HttpContextBase httpContext) + : base(httpContext) + { + Initialize(); + } /// /// Initializes a new instance of the class with an initial list @@ -101,23 +105,37 @@ namespace Umbraco.Core.ObjectResolution private readonly List> _lazyTypeList = new List>(); private readonly List>> _typeListProducerList = new List>>(); private readonly List _excludedTypesList = new List(); + private Lazy> _resolvedTypes = null; + + private void Initialize() + { + _resolvedTypes = new Lazy>(() => + { + var resolvedTypes = new List(); + + // get the types by evaluating the lazy & producers + var types = new List(); + types.AddRange(_lazyTypeList.Select(x => x.Value)); + types.AddRange(_typeListProducerList.SelectMany(x => x())); + + // we need to validate each resolved type now since we could + // not do it before evaluating the lazy & producers + foreach (var type in types.Where(x => _excludedTypesList.Contains(x) == false)) + { + AddValidAndNoDuplicate(resolvedTypes, type); + } + + return resolvedTypes; + }); + } - private List _resolvedTypes = null; - private readonly ReaderWriterLockSlim _resolvedTypesLock = new ReaderWriterLockSlim(); - /// /// Gets a value indicating whether the resolver has resolved types to create instances from. /// /// To be used in unit tests. public bool HasResolvedTypes { - get - { - using (new ReadLock(_resolvedTypesLock)) - { - return _resolvedTypes != null; - } - } + get { return _resolvedTypes.IsValueCreated; } } /// @@ -126,32 +144,7 @@ namespace Umbraco.Core.ObjectResolution /// When called, will get the types from the lazy list. protected override IEnumerable InstanceTypes { - get - { - using (var lck = new UpgradeableReadLock(_resolvedTypesLock)) - { - if (_resolvedTypes == null) - { - lck.UpgradeToWriteLock(); - - _resolvedTypes = new List(); - - // get the types by evaluating the lazy & producers - var types = new List(); - types.AddRange(_lazyTypeList.Select(x => x.Value)); - types.AddRange(_typeListProducerList.SelectMany(x => x())); - - // we need to validate each resolved type now since we could - // not do it before evaluating the lazy & producers - foreach (var type in types.Where(x => !_excludedTypesList.Contains(x))) - { - AddValidAndNoDuplicate(_resolvedTypes, type); - } - } - - return _resolvedTypes; - } - } + get { return _resolvedTypes.Value; } } /// diff --git a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs index 8bb5995891..95ff923c58 100644 --- a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs +++ b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.ObjectResolution where TResolved : class where TResolver : ResolverBase { - private IEnumerable _applicationInstances = null; + private Lazy> _applicationInstances = null; private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); private readonly string _httpContextKey; private readonly List _instanceTypes = new List(); @@ -47,6 +47,8 @@ namespace Umbraco.Core.ObjectResolution if (scope == ObjectLifetimeScope.HttpRequest) _httpContextKey = this.GetType().FullName; _instanceTypes = new List(); + + InitializeAppInstances(); } /// @@ -64,6 +66,8 @@ namespace Umbraco.Core.ObjectResolution _httpContextKey = this.GetType().FullName; CurrentHttpContext = httpContext; _instanceTypes = new List(); + + InitializeAppInstances(); } /// @@ -94,6 +98,11 @@ namespace Umbraco.Core.ObjectResolution } #endregion + private void InitializeAppInstances() + { + _applicationInstances = new Lazy>(() => CreateInstances().ToArray()); + } + /// /// Gets or sets a value indicating whether the resolver can resolve objects before resolution is frozen. /// @@ -178,30 +187,17 @@ namespace Umbraco.Core.ObjectResolution switch (LifetimeScope) { case ObjectLifetimeScope.HttpRequest: - // create new instances per HttpContext - using (var l = new UpgradeableReadLock(_lock)) - { - // create if not already there - if (CurrentHttpContext.Items[_httpContextKey] == null) - { - l.UpgradeToWriteLock(); - CurrentHttpContext.Items[_httpContextKey] = CreateInstances().ToArray(); - } - return (TResolved[])CurrentHttpContext.Items[_httpContextKey]; - } - case ObjectLifetimeScope.Application: - // create new instances per application - using (var l = new UpgradeableReadLock(_lock)) + // create new instances per HttpContext + if (CurrentHttpContext.Items[_httpContextKey] == null) { - // create if not already there - if (_applicationInstances == null) - { - l.UpgradeToWriteLock(); - _applicationInstances = CreateInstances().ToArray(); - } - return _applicationInstances; + CurrentHttpContext.Items[_httpContextKey] = CreateInstances().ToArray(); } + return (TResolved[])CurrentHttpContext.Items[_httpContextKey]; + + case ObjectLifetimeScope.Application: + + return _applicationInstances.Value; case ObjectLifetimeScope.Transient: default: diff --git a/src/Umbraco.Core/Persistence/Mappers/BaseMapper.cs b/src/Umbraco.Core/Persistence/Mappers/BaseMapper.cs index 006821e2b3..ea7c8ab8f9 100644 --- a/src/Umbraco.Core/Persistence/Mappers/BaseMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/BaseMapper.cs @@ -13,12 +13,21 @@ namespace Umbraco.Core.Persistence.Mappers internal abstract void BuildMap(); - internal string Map(string propertyName) + internal string Map(string propertyName, bool throws = false) { DtoMapModel dtoTypeProperty; - return PropertyInfoCache.TryGetValue(propertyName, out dtoTypeProperty) - ? GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo) - : string.Empty; + if (PropertyInfoCache.TryGetValue(propertyName, out dtoTypeProperty)) + { + return GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo); + } + else + { + if (throws) + { + throw new InvalidOperationException("Could not get the value with the key " + propertyName + " from the property info cache, keys available: " + string.Join(", ", PropertyInfoCache.Keys)); + } + return string.Empty; + } } internal void CacheMap(Expression> sourceMember, Expression> destinationMember) @@ -30,7 +39,12 @@ namespace Umbraco.Core.Persistence.Mappers internal DtoMapModel ResolveMapping(Expression> sourceMember, Expression> destinationMember) { var source = ExpressionHelper.FindProperty(sourceMember); - var destination = ExpressionHelper.FindProperty(destinationMember) as PropertyInfo; + var destination = (PropertyInfo)ExpressionHelper.FindProperty(destinationMember); + + if (destination == null) + { + throw new InvalidOperationException("The 'destination' returned was null, cannot resolve the mapping"); + } return new DtoMapModel(typeof(TDestination), destination, source.Name); } diff --git a/src/Umbraco.Core/Persistence/Mappers/MappingResolver.cs b/src/Umbraco.Core/Persistence/Mappers/MappingResolver.cs index 4b6b513bf2..007ddbd014 100644 --- a/src/Umbraco.Core/Persistence/Mappers/MappingResolver.cs +++ b/src/Umbraco.Core/Persistence/Mappers/MappingResolver.cs @@ -41,11 +41,6 @@ namespace Umbraco.Core.Persistence.Mappers { return byAttribute.Result; } - - //static mapper registration if not using attributes, could be something like this: - //if (type == typeof (UserType)) - // return new UserTypeMapper(); - throw new Exception("Invalid Type: A Mapper could not be resolved based on the passed in Type"); }); } @@ -56,31 +51,18 @@ namespace Umbraco.Core.Persistence.Mappers /// /// private Attempt TryGetMapperByAttribute(Type entityType) - { - //get all BaseMapper types that have a MapperFor attribute: - var assignedMapperTypes = InstanceTypes; - + { //check if any of the mappers are assigned to this type - var mapper = assignedMapperTypes.FirstOrDefault( - x => x.GetCustomAttributes(false) + var mapper = Values.FirstOrDefault( + x => x.GetType().GetCustomAttributes(false) .Any(m => m.EntityType == entityType)); if (mapper == null) { return Attempt.Fail(); } - try - { - var instance = Activator.CreateInstance(mapper) as BaseMapper; - return instance != null - ? Attempt.Succeed(instance) - : Attempt.Fail(); - } - catch (Exception ex) - { - LogHelper.Error(typeof(MappingResolver), "Could not instantiate mapper of type " + mapper, ex); - return Attempt.Fail(ex); - } + + return Attempt.Succeed(mapper); } internal string GetMapping(Type type, string propertyName) diff --git a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs index 1a561b7bf4..a0ccfaa070 100644 --- a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Linq.Expressions; @@ -26,13 +27,17 @@ namespace Umbraco.Core.Persistence.Querying m.Expression.NodeType == ExpressionType.Parameter && m.Expression.Type == typeof(T)) { - var field = _mapper.Map(m.Member.Name); - return field; + var field = _mapper.Map(m.Member.Name, true); + if (field.IsNullOrWhiteSpace()) + throw new InvalidOperationException("The mapper returned an empty field for the member name: " + m.Member.Name); + return field; } if (m.Expression != null && m.Expression.NodeType == ExpressionType.Convert) { - var field = _mapper.Map(m.Member.Name); + var field = _mapper.Map(m.Member.Name, true); + if (field.IsNullOrWhiteSpace()) + throw new InvalidOperationException("The mapper returned an empty field for the member name: " + m.Member.Name); return field; }