/// <summary> /// This will throw an exception if no suitable constructors were retrieved, it will never return null. Cases where a converter may not be generated include /// that where no public parameterless constructor exists on the destination type. It also includes the case where the MatchAll propertySettingType was /// specified at instantiation and there is at least one publicly-settable, non-indexed property that could not be dealt with. /// </summary> public ICompilableTypeConverter <TSource, TDest> Get <TSource, TDest>() { var constructor = typeof(TDest).GetConstructor(new Type[0]); if (constructor == null) { throw new ByPropertyMappingFailureException(typeof(TSource), typeof(TDest), ByPropertyMappingFailureException.FailureReasonOptions.NoParameterLessConstructor, null); } var propertyGetters = new List <ICompilablePropertyGetter>(); var propertiesToSet = new List <PropertyInfo>(); foreach (var property in typeof(TDest).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)) { // If there isn't a public, non-indexed setter then move on if ((property.GetSetMethod() == null) || (property.GetIndexParameters().Length > 0)) { continue; } // If this is a property to ignore then do just that if (_propertiesToIgnore.Any(p => p.MatchesProperty(property))) { continue; } // If we can't retrieve a property getter for the property then either give up (if MatchAll) or push on (if MatchAsManyAsPossible) var propertyGetter = _propertyGetterFactory.TryToGet(typeof(TSource), property.Name, property.PropertyType); if (propertyGetter == null) { if (_propertySettingType == PropertySettingTypeOptions.MatchAll) { throw new ByPropertyMappingFailureException(typeof(TSource), typeof(TDest), ByPropertyMappingFailureException.FailureReasonOptions.UnableToMapProperty, property); } else { continue; } } // Otherwise, add this property to the list! propertyGetters.Add(propertyGetter); propertiesToSet.Add(property); } // Have to use Activator.CreateInstance as CompilableTypeConverterByPropertySetting requires that TDest implement "new()" which we check at run // time above but can't know at compile time return((ICompilableTypeConverter <TSource, TDest>)Activator.CreateInstance( typeof(CompilableTypeConverterByPropertySetting <,>).MakeGenericType( typeof(TSource), typeof(TDest) ), propertyGetters, propertiesToSet, _nullSourceBehaviour, (_nullSourceBehaviour == ByPropertySettingNullSourceBehaviourOptions.CreateEmptyInstanceWithDefaultPropertyValues) ? _initialisedFlagsIfTranslatingNullsToEmptyInstances.Where(p => typeof(TDest).HasProperty(p)) : new PropertyInfo[0] )); }
/// <summary> /// This will throw an exception if no suitable constructors were retrieved, it will never return null /// </summary> public ICompilableTypeConverter <TSource, TDest> Get <TSource, TDest>() { var failedCandidates = new List <ByConstructorMappingFailureException.ConstructorOptionFailureDetails>(); var constructorCandidates = new List <ICompilableTypeConverterByConstructor <TSource, TDest> >(); var constructors = typeof(TDest).GetConstructors(); foreach (var constructor in constructors) { var args = constructor.GetParameters(); if ((args.Length == 0) && (_parameterLessConstructorBehaviour == ParameterLessConstructorBehaviourOptions.Ignore)) { failedCandidates.Add(new ByConstructorMappingFailureException.ConstructorOptionFailureDetails( constructor, ByConstructorMappingFailureException.ConstructorOptionFailureDetails.FailureReasonOptions.ParameterLessConstructorNotAllowed, null )); continue; } var defaultValuePropertyGetters = new List <ICompilableConstructorDefaultValuePropertyGetter>(); var otherPropertyGetters = new List <ICompilablePropertyGetter>(); var candidate = true; foreach (var arg in args) { var propertyGetter = _propertyGetterFactory.TryToGet(typeof(TSource), arg.Name, arg.ParameterType); if (propertyGetter != null) { otherPropertyGetters.Add(propertyGetter); continue; } if (arg.IsOptional) { defaultValuePropertyGetters.Add( (ICompilableConstructorDefaultValuePropertyGetter)Activator.CreateInstance( typeof(CompilableConstructorDefaultValuePropertyGetter <,>).MakeGenericType( typeof(TSource), arg.ParameterType ), constructor, arg.Name ) ); continue; } failedCandidates.Add(new ByConstructorMappingFailureException.ConstructorOptionFailureDetails( constructor, ByConstructorMappingFailureException.ConstructorOptionFailureDetails.FailureReasonOptions.UnableToMapConstructorArgument, arg )); candidate = false; break; } if (candidate) { constructorCandidates.Add( new CompilableTypeConverterByConstructor <TSource, TDest>( otherPropertyGetters, defaultValuePropertyGetters, constructor ) ); } } if (constructorCandidates.Count == 0) { throw new ByConstructorMappingFailureException( typeof(TSource), typeof(TDest), failedCandidates ); } // The ITypeConverterPrioritiser interface may be used to prioritise and filter. If there are multiple candidates then it may only // prioritise the results (depending upon implementation), but it may also filter some out (again, depending upon implementation). // This means that the prioritiser must be consulted even if there is only a single candidate (there is no prioritisation required // in that case since there is only one option, but the possibility that this candidate will not be allowed through the filter // means that the prioritiser must still be called upon). var constructorPrioritiser = _constructorPrioritiserFactory.Get <TSource, TDest>(); var selectedCandidate = constructorPrioritiser.Get(constructorCandidates); var selectedCandidateCompiled = constructorCandidates.FirstOrDefault(c => c == selectedCandidate); if (selectedCandidateCompiled == null) { throw new ByConstructorMappingFailureException( typeof(TSource), typeof(TDest), constructorCandidates.Select(c => new ByConstructorMappingFailureException.ConstructorOptionFailureDetails( c.Constructor, ByConstructorMappingFailureException.ConstructorOptionFailureDetails.FailureReasonOptions.FilteredOutByPrioritiser, null )) ); } return(selectedCandidateCompiled); }