/// <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]
                       ));
        }
Пример #2
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);
        }