tools/AutoMapper/ConfigurationValidator.cs (102 lines of code) (raw):
using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper.Configuration;
namespace AutoMapper
{
public class ConfigurationValidator
{
private readonly IConfigurationProvider _config;
public ConfigurationValidator(IConfigurationProvider config) => _config = config;
public void AssertConfigurationIsValid(IEnumerable<TypeMap> typeMaps)
{
var maps = typeMaps as TypeMap[] ?? typeMaps.ToArray();
var badTypeMaps =
(from typeMap in maps
where typeMap.ShouldCheckForValid()
let unmappedPropertyNames = typeMap.GetUnmappedPropertyNames()
let canConstruct = typeMap.PassesCtorValidation()
where unmappedPropertyNames.Length > 0 || !canConstruct
select new AutoMapperConfigurationException.TypeMapConfigErrors(typeMap, unmappedPropertyNames, canConstruct)
).ToArray();
if (badTypeMaps.Any())
{
throw new AutoMapperConfigurationException(badTypeMaps);
}
var typeMapsChecked = new List<TypeMap>();
var configExceptions = new List<Exception>();
foreach (var typeMap in maps)
{
try
{
DryRunTypeMap(typeMapsChecked, typeMap.Types, typeMap, null);
}
catch (Exception e)
{
configExceptions.Add(e);
}
}
if (configExceptions.Count > 1)
{
throw new AggregateException(configExceptions);
}
if (configExceptions.Count > 0)
{
throw configExceptions[0];
}
}
private void DryRunTypeMap(ICollection<TypeMap> typeMapsChecked, TypePair types, TypeMap typeMap, PropertyMap propertyMap)
{
if(typeMap == null)
{
typeMap = _config.ResolveTypeMap(types.SourceType, types.DestinationType);
}
if (typeMap != null)
{
if(typeMapsChecked.Contains(typeMap))
{
return;
}
typeMapsChecked.Add(typeMap);
if(typeMap.CustomMapper != null || typeMap.TypeConverterType != null)
{
return;
}
var context = new ValidationContext(types, propertyMap, typeMap);
_config.Validate(context);
CheckPropertyMaps(typeMapsChecked, typeMap);
typeMap.IsValid = true;
}
else
{
var mapperToUse = _config.FindMapper(types);
if (mapperToUse == null)
{
// Maps with no match get mapped at runtime yolo
if (propertyMap.TypeMap.Profile.CreateMissingTypeMaps)
return;
throw new AutoMapperConfigurationException(propertyMap.TypeMap.Types) { PropertyMap = propertyMap };
}
var context = new ValidationContext(types, propertyMap, mapperToUse);
_config.Validate(context);
if(mapperToUse is IObjectMapperInfo mapperInfo)
{
var newTypePair = mapperInfo.GetAssociatedTypes(types);
DryRunTypeMap(typeMapsChecked, newTypePair, null, propertyMap);
}
}
}
private void CheckPropertyMaps(ICollection<TypeMap> typeMapsChecked, TypeMap typeMap)
{
foreach (var propertyMap in typeMap.GetPropertyMaps())
{
if (propertyMap.Ignored) continue;
var sourceType = propertyMap.SourceType;
if (sourceType == null) continue;
// when we don't know what the source type is, bail
if (sourceType.IsGenericParameter || sourceType == typeof (object))
return;
var destinationType = propertyMap.DestinationProperty.GetMemberType();
DryRunTypeMap(typeMapsChecked, new TypePair(sourceType, destinationType), null, propertyMap);
}
}
}
}