/
MapperConfigurationExpression.cs
192 lines (155 loc) · 8.71 KB
/
MapperConfigurationExpression.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
using AutoMapper.Features;
using AutoMapper.Internal.Mappers;
using AutoMapper.QueryableExtensions.Impl;
namespace AutoMapper;
using Validator = Action<ValidationContext>;
public interface IMapperConfigurationExpression : IProfileExpression
{
/// <summary>
/// Add an existing profile
/// </summary>
/// <param name="profile">Profile to add</param>
void AddProfile(Profile profile);
/// <summary>
/// Add an existing profile type. Profile will be instantiated and added to the configuration.
/// </summary>
/// <typeparam name="TProfile">Profile type</typeparam>
void AddProfile<TProfile>() where TProfile : Profile, new();
/// <summary>
/// Add an existing profile type. Profile will be instantiated and added to the configuration.
/// </summary>
/// <param name="profileType">Profile type</param>
void AddProfile(Type profileType);
/// <summary>
/// Add profiles contained in an IEnumerable
/// </summary>
/// <param name="enumerableOfProfiles">IEnumerable of Profile</param>
void AddProfiles(IEnumerable<Profile> enumerableOfProfiles);
/// <summary>
/// Add mapping definitions contained in assemblies.
/// Looks for <see cref="Profile" /> definitions and classes decorated with <see cref="AutoMapAttribute" />
/// </summary>
/// <param name="assembliesToScan">Assemblies containing mapping definitions</param>
void AddMaps(IEnumerable<Assembly> assembliesToScan);
/// <summary>
/// Add mapping definitions contained in assemblies.
/// Looks for <see cref="Profile" /> definitions and classes decorated with <see cref="AutoMapAttribute" />
/// </summary>
/// <param name="assembliesToScan">Assemblies containing mapping definitions</param>
void AddMaps(params Assembly[] assembliesToScan);
/// <summary>
/// Add mapping definitions contained in assemblies.
/// Looks for <see cref="Profile" /> definitions and classes decorated with <see cref="AutoMapAttribute" />
/// </summary>
/// <param name="assemblyNamesToScan">Assembly names to load and scan containing mapping definitions</param>
void AddMaps(IEnumerable<string> assemblyNamesToScan);
/// <summary>
/// Add mapping definitions contained in assemblies.
/// Looks for <see cref="Profile" /> definitions and classes decorated with <see cref="AutoMapAttribute" />
/// </summary>
/// <param name="assemblyNamesToScan">Assembly names to load and scan containing mapping definitions</param>
void AddMaps(params string[] assemblyNamesToScan);
/// <summary>
/// Add mapping definitions contained in assemblies.
/// Looks for <see cref="Profile" /> definitions and classes decorated with <see cref="AutoMapAttribute" />
/// </summary>
/// <param name="typesFromAssembliesContainingMappingDefinitions">Types from assemblies containing mapping definitions</param>
void AddMaps(IEnumerable<Type> typesFromAssembliesContainingMappingDefinitions);
/// <summary>
/// Add mapping definitions contained in assemblies.
/// Looks for <see cref="Profile" /> definitions and classes decorated with <see cref="AutoMapAttribute" />
/// </summary>
/// <param name="typesFromAssembliesContainingMappingDefinitions">Types from assemblies containing mapping definitions</param>
void AddMaps(params Type[] typesFromAssembliesContainingMappingDefinitions);
/// <summary>
/// Supply a factory method callback for creating resolvers and type converters
/// </summary>
/// <param name="constructor">Factory method</param>
void ConstructServicesUsing(Func<Type, object> constructor);
/// <summary>
/// Create a named profile with the supplied configuration
/// </summary>
/// <param name="profileName">Profile name, must be unique</param>
/// <param name="config">Profile configuration</param>
void CreateProfile(string profileName, Action<IProfileExpression> config);
}
public sealed class MapperConfigurationExpression : Profile, IGlobalConfigurationExpression
{
private readonly List<Profile> _profiles = new();
private readonly List<Validator> _validators = new();
private readonly List<IObjectMapper> _mappers;
private Func<Type, object> _serviceCtor = Activator.CreateInstance;
private List<IProjectionMapper> _projectionMappers;
public MapperConfigurationExpression() : base() => _mappers = MapperRegistry.Mappers();
/// <summary>
/// Add an action to be called when validating the configuration.
/// </summary>
/// <param name="validator">the validation callback</param>
void IGlobalConfigurationExpression.Validator(Validator validator) =>
_validators.Add(validator ?? throw new ArgumentNullException(nameof(validator)));
/// <summary>
/// How many levels deep should AutoMapper try to inline the execution plan for child classes.
/// See <a href="https://automapper.readthedocs.io/en/latest/Understanding-your-mapping.html">the docs</a> for details.
/// </summary>
int IGlobalConfigurationExpression.MaxExecutionPlanDepth { get; set; } = 1;
List<Validator> IGlobalConfigurationExpression.Validators => _validators;
List<IProjectionMapper> IGlobalConfigurationExpression.ProjectionMappers => _projectionMappers ??= ProjectionBuilder.DefaultProjectionMappers();
/// <summary>
/// How many levels deep should recursive queries be expanded.
/// Must be zero for EF6. Can be greater than zero for EF Core.
/// </summary>
int IGlobalConfigurationExpression.RecursiveQueriesMaxDepth { get; set; }
IReadOnlyCollection<IProfileConfiguration> IGlobalConfigurationExpression.Profiles => _profiles;
Func<Type, object> IGlobalConfigurationExpression.ServiceCtor => _serviceCtor;
public void CreateProfile(string profileName, Action<IProfileExpression> config)
=> AddProfile(new Profile(profileName, config));
List<IObjectMapper> IGlobalConfigurationExpression.Mappers => _mappers;
Features<IGlobalFeature> IGlobalConfigurationExpression.Features { get; } = new Features<IGlobalFeature>();
public void AddProfile(Profile profile) => _profiles.Add(profile);
public void AddProfile<TProfile>() where TProfile : Profile, new() => AddProfile(new TProfile());
public void AddProfile(Type profileType) => AddProfile((Profile)Activator.CreateInstance(profileType));
public void AddProfiles(IEnumerable<Profile> enumerableOfProfiles)
{
foreach (var profile in enumerableOfProfiles)
{
AddProfile(profile);
}
}
public void AddMaps(IEnumerable<Assembly> assembliesToScan)
=> AddMapsCore(assembliesToScan);
public void AddMaps(params Assembly[] assembliesToScan)
=> AddMapsCore(assembliesToScan);
public void AddMaps(IEnumerable<string> assemblyNamesToScan)
=> AddMapsCore(assemblyNamesToScan.Select(Assembly.Load));
public void AddMaps(params string[] assemblyNamesToScan)
=> AddMaps((IEnumerable<string>)assemblyNamesToScan);
public void AddMaps(IEnumerable<Type> typesFromAssembliesContainingMappingDefinitions)
=> AddMapsCore(typesFromAssembliesContainingMappingDefinitions.Select(t => t.GetTypeInfo().Assembly));
public void AddMaps(params Type[] typesFromAssembliesContainingMappingDefinitions)
=> AddMaps((IEnumerable<Type>)typesFromAssembliesContainingMappingDefinitions);
private void AddMapsCore(IEnumerable<Assembly> assembliesToScan)
{
var autoMapAttributeProfile = new Profile(nameof(AutoMapAttribute));
foreach (var type in assembliesToScan.Where(a => !a.IsDynamic && a != typeof(Profile).Assembly).SelectMany(a => a.GetTypes()))
{
if (typeof(Profile).IsAssignableFrom(type) && !type.IsAbstract && !type.ContainsGenericParameters)
{
AddProfile(type);
}
foreach (var autoMapAttribute in type.GetCustomAttributes<AutoMapAttribute>())
{
var mappingExpression = (MappingExpression) autoMapAttributeProfile.CreateMap(autoMapAttribute.SourceType, type);
foreach (var memberInfo in type.GetMembers(BindingFlags.Public | BindingFlags.Instance))
{
foreach (var memberConfigurationProvider in memberInfo.GetCustomAttributes().OfType<IMemberConfigurationProvider>())
{
mappingExpression.ForMember(memberInfo, memberConfigurationProvider.ApplyConfiguration);
}
}
autoMapAttribute.ApplyConfiguration(mappingExpression);
}
}
AddProfile(autoMapAttributeProfile);
}
public void ConstructServicesUsing(Func<Type, object> constructor) => _serviceCtor = constructor ?? throw new ArgumentNullException(nameof(constructor));
}