internal AttemptTuple <MapperDefinition> TryGetMapper(Type source, Type target, bool createIfNonExistant) { Func <AttemptTuple <MapperDefinition> > findTypeMapper = () => { //if we have a specified TypeMapper for <TSource,Target> MapperDefinition mapper; if (_mappers.TryGetValue(new TypeMapDefinition(source, target), out mapper)) { return(new AttemptTuple <MapperDefinition>(true, mapper)); } //now, check if we have the specified mapper using type inheritance var found = _mappers.Where(x => x.Value.Metadata.PermitTypeInheritance && x.Value.Metadata.SourceType.IsAssignableFrom(source) && x.Value.Metadata.DestinationType.IsAssignableFrom(target)).ToArray(); if (found.Any()) { return(new AttemptTuple <MapperDefinition>(true, found.Single().Value)); } return(AttemptTuple <MapperDefinition> .False); }; var result = findTypeMapper(); if (result.Success) { return(result); } //if we've got this far, then create a new default map and add it to our mappers if (createIfNonExistant) { //need to lock as there could be many threads trying to do the same thing with the same types using (new WriteLockDisposable(_locker)) { //we'll now actually need to re-check if it exists result = findTypeMapper(); if (result.Success) { return(result); } //if both Source and Target types are Enumerables return new EnumerableTypeMapper<TSource,TTarget>() if (source.IsEnumerable() && target.IsEnumerable()) { var mapper = (ITypeMapper) Activator.CreateInstance(typeof(EnumerableTypeMapper <,>).MakeGenericType(source, target) , this); //pass this factory into the constructor //create the map and store it against our list var meta = new TypeMapperMetadata(source, target, false); var mapperDef = new MapperDefinition(mapper, meta); return(new AttemptTuple <MapperDefinition>(true, _mappers.GetOrAdd(new TypeMapDefinition(source, target), mapperDef))); } else { //if not enumerable, just create a normal map var concreteTypeMapper = typeof(TypeMapper <,>).MakeGenericType(new[] { source, target }); var mapper = (ITypeMapper)Creator.Create(concreteTypeMapper, this); var defaultMeta = new TypeMapperMetadata(source, target, false); var defaultMapperDef = new MapperDefinition(mapper, defaultMeta); return(new AttemptTuple <MapperDefinition>(true, _mappers.GetOrAdd(new TypeMapDefinition(source, target), defaultMapperDef))); } } } return(AttemptTuple <MapperDefinition> .False); }
/// <summary> /// Maps the source object and source object type to the destinationSubclassType based on a mapper that supports inheritance /// with a destination type specified as the destinationBaseclassType. /// </summary> /// <remarks> /// An example of this is if a map was defined to go from an Examine SearchResult to a TypedEntity with the allow inheritance flag /// set to true and then a mapping operation occured from a SearchResult to a User (a subclass of TypedEntity). /// /// This method will first check to see if there is a mapping operation declared to go between the baseclass to the subclass, if so /// it will perform the normal mapping from the source object to the destinationBaseclassType, and then map from the base class type /// to the sub class type. /// /// If this method doesn't find a map to go from the base class type to the sub class type, it will attempt to instantiate a new /// instance of the subclass and then do a map to the new existing object using the mapper already defined. /// </remarks> /// <param name="source"></param> /// <param name="sourceType"></param> /// <param name="destinationBaseclassType"></param> /// <param name="destinationSubclassType"></param> /// <param name="mapperDefinition"></param> /// <param name="scope"></param> /// <returns></returns> protected virtual object MapToSubclass(object source, Type sourceType, Type destinationBaseclassType, Type destinationSubclassType, MapperDefinition mapperDefinition, MappingExecutionScope scope) { if (TypeFinder.IsTypeAssignableFrom(destinationSubclassType, destinationBaseclassType)) { //if its assignable, just map it return(mapperDefinition.Mapper.Map(source, scope)); } if (destinationSubclassType.IsSubclassOf(destinationBaseclassType)) { //we need to see if there's a mapping registered to go from the base class to the sub class, //if so, we'll do the normal mapping to the base class, and then map again to the sub class. var subclassTypeMapDef = TryGetMapper(destinationBaseclassType, destinationSubclassType, false); if (subclassTypeMapDef.Success) { //there's actually a map declared to go from the base class to subclass type, so well first //map to the base class type from the source, then from the base class type to the sub class type. var baseTypeMapped = mapperDefinition.Mapper.Map(source); return(subclassTypeMapDef.Result.Mapper.Map(baseTypeMapped, scope)); } //there's no map declared so we need to now try to create the 'subclass' type and then map to that existing object try { var mapTo = Activator.CreateInstance(destinationSubclassType); Map(source, mapTo, sourceType, destinationSubclassType); return(mapTo); } catch (MissingMethodException ex) { throw new InvalidOperationException( string.Format("Could not map from {0} to {1} because {1} does not have a parameterless constructor", sourceType.Name, destinationSubclassType.Name), ex); } } throw new InvalidCastException("Cannot map from type " + sourceType + " to subclass type " + destinationSubclassType + " from the map specified for the baseclass type " + destinationBaseclassType); }