Skip to content

Commit

Permalink
C# 12
Browse files Browse the repository at this point in the history
  • Loading branch information
lbargaoanu committed May 22, 2024
1 parent 6bb05d5 commit 343a455
Show file tree
Hide file tree
Showing 55 changed files with 361 additions and 592 deletions.
6 changes: 5 additions & 1 deletion src/AutoMapper/ApiCompatBaseline.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
Compat issues with assembly AutoMapper:
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.AutoMapAttribute' changed from '[AttributeUsageAttribute(1036, AllowMultiple=true)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, AllowMultiple=true)]' in the implementation.
CannotSealType : Type 'AutoMapper.AutoMapperConfigurationException.TypeMapConfigErrors' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
TypeCannotChangeClassification : Type 'AutoMapper.AutoMapperConfigurationException.TypeMapConfigErrors' is a 'struct' in the implementation but is a 'class' in the contract.
CannotSealType : Type 'AutoMapper.DuplicateTypeMapConfigurationException.TypeMapConfigErrors' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
TypeCannotChangeClassification : Type 'AutoMapper.DuplicateTypeMapConfigurationException.TypeMapConfigErrors' is a 'struct' in the implementation but is a 'class' in the contract.
CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.NullableContextAttribute' exists on 'AutoMapper.IMapper' in the contract but not the implementation.
CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.NullableContextAttribute' exists on 'AutoMapper.IMapper.Map(System.Object, System.Object, System.Type, System.Type, System.Action<AutoMapper.IMappingOperationOptions<System.Object, System.Object>>)' in the contract but not the implementation.
CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.NullableAttribute' exists on parameter 'opts' on member 'AutoMapper.IMapper.Map(System.Object, System.Object, System.Type, System.Type, System.Action<AutoMapper.IMappingOperationOptions<System.Object, System.Object>>)' in the contract but not the implementation.
Expand Down Expand Up @@ -43,4 +47,4 @@ CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.NullableAttri
CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.NullableContextAttribute' exists on 'AutoMapper.QueryableExtensions.Extensions.ProjectTo<TDestination>(System.Linq.IQueryable, AutoMapper.IConfigurationProvider, System.Object, System.Linq.Expressions.Expression<System.Func<TDestination, System.Object>>[])' in the contract but not the implementation.
CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.NullableAttribute' exists on parameter 'parameters' on member 'AutoMapper.QueryableExtensions.Extensions.ProjectTo<TDestination>(System.Linq.IQueryable, AutoMapper.IConfigurationProvider, System.Object, System.Linq.Expressions.Expression<System.Func<TDestination, System.Object>>[])' in the contract but not the implementation.
CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.NullableAttribute' exists on generic param 'TDestination' on member 'AutoMapper.QueryableExtensions.Extensions.ProjectTo<TDestination>(System.Linq.IQueryable, AutoMapper.IConfigurationProvider, System.Object, System.Linq.Expressions.Expression<System.Func<TDestination, System.Object>>[])' in the contract but not the implementation.
Total Issues: 44
Total Issues: 48
53 changes: 13 additions & 40 deletions src/AutoMapper/AutoMapperMappingException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,63 +67,37 @@ public override string StackTrace
{
return string.Join(Environment.NewLine,
base.StackTrace
.Split(new[] {Environment.NewLine}, StringSplitOptions.None)
.Split([Environment.NewLine], StringSplitOptions.None)
.Where(str => !str.TrimStart().StartsWith("at AutoMapper.")));
}
}
#endif
}
public class DuplicateTypeMapConfigurationException : Exception
public class DuplicateTypeMapConfigurationException(DuplicateTypeMapConfigurationException.TypeMapConfigErrors[] errors) : Exception
{
public TypeMapConfigErrors[] Errors { get; }

public DuplicateTypeMapConfigurationException(TypeMapConfigErrors[] errors)
public TypeMapConfigErrors[] Errors { get; } = errors;
public override string Message { get; } = GetErrors(errors);
static string GetErrors(TypeMapConfigErrors[] errors)
{
Errors = errors;
var builder = new StringBuilder();
StringBuilder builder = new();
builder.AppendLine("Duplicate CreateMap calls:");
foreach (var error in Errors)
foreach(var error in errors)
{
builder.AppendLine($"{error.Types.SourceType.FullName} to {error.Types.DestinationType.FullName} defined in profiles:");
builder.AppendLine(string.Join(Environment.NewLine, error.ProfileNames));
}
builder.AppendLine("This can cause configuration collisions and inconsistent mappings. Use a single CreateMap call per type pair.");
Message = builder.ToString();
}

public class TypeMapConfigErrors
{
public string[] ProfileNames { get; }
public TypePair Types { get; }

public TypeMapConfigErrors(TypePair types, string[] profileNames)
{
Types = types;
ProfileNames = profileNames;
}
return builder.ToString();
}

public override string Message { get; }
public readonly record struct TypeMapConfigErrors(TypePair Types, string[] ProfileNames);
}
public class AutoMapperConfigurationException : Exception
{
public TypeMapConfigErrors[] Errors { get; }
public TypePair? Types { get; }
public MemberMap MemberMap { get; set; }

public class TypeMapConfigErrors
{
public TypeMap TypeMap { get; }
public string[] UnmappedPropertyNames { get; }
public bool CanConstruct { get; }

public TypeMapConfigErrors(TypeMap typeMap, string[] unmappedPropertyNames, bool canConstruct)
{
TypeMap = typeMap;
UnmappedPropertyNames = unmappedPropertyNames;
CanConstruct = canConstruct;
}
}
public readonly record struct TypeMapConfigErrors(TypeMap TypeMap, string[] UnmappedPropertyNames, bool CanConstruct);

public AutoMapperConfigurationException(string message)
: base(message)
Expand Down Expand Up @@ -170,16 +144,15 @@ public override string Message
}
if (Errors != null)
{
var message =
new StringBuilder(
StringBuilder message = new(
"\nUnmapped members were found. Review the types and members below.\nAdd a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type\nFor no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters\n");

foreach (var error in Errors)
{
var len = error.TypeMap.SourceType.FullName.Length +
error.TypeMap.DestinationType.FullName.Length + 5;

message.AppendLine(new string('=', len));
message.AppendLine(new('=', len));
message.AppendLine(error.TypeMap.SourceType.Name + " -> " + error.TypeMap.DestinationType.Name +
" (" +
error.TypeMap.ConfiguredMemberList + " member list)");
Expand Down Expand Up @@ -214,7 +187,7 @@ public override string StackTrace
if (Errors != null)
return string.Join(Environment.NewLine,
base.StackTrace
.Split(new[] { Environment.NewLine }, StringSplitOptions.None)
.Split([Environment.NewLine], StringSplitOptions.None)
.Where(str => !str.TrimStart().StartsWith("at AutoMapper."))
.ToArray());

Expand Down
7 changes: 2 additions & 5 deletions src/AutoMapper/Configuration/Annotations/AutoMapAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@
/// Discovered during scanning assembly scanning for configuration when calling <see cref="O:AutoMapper.IMapperConfigurationExpression.AddMaps"/>
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, AllowMultiple = true)]
public sealed class AutoMapAttribute : Attribute
public sealed class AutoMapAttribute(Type sourceType) : Attribute
{
public AutoMapAttribute(Type sourceType)
=> SourceType = sourceType;

public Type SourceType { get; }
public Type SourceType { get; } = sourceType;
public bool ReverseMap { get; set; }

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,9 @@
/// Must be used in combination with <see cref="AutoMapAttribute" />
/// </remarks>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class MappingOrderAttribute : Attribute, IMemberConfigurationProvider
public sealed class MappingOrderAttribute(int value) : Attribute, IMemberConfigurationProvider
{
public int Value { get; }

public MappingOrderAttribute(int value)
{
Value = value;
}
public int Value { get; } = value;

public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,12 @@
/// Must be used in combination with <see cref="AutoMapAttribute" />
/// </remarks>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class NullSubstituteAttribute : Attribute, IMemberConfigurationProvider
public sealed class NullSubstituteAttribute(object value) : Attribute, IMemberConfigurationProvider
{
/// <summary>
/// Value to use if source value is null
/// </summary>
public object Value { get; }

public NullSubstituteAttribute(object value)
{
Value = value;
}
public object Value { get; } = value;

public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@
/// Must be used in combination with <see cref="AutoMapAttribute" />
/// </remarks>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class SourceMemberAttribute : Attribute, IMemberConfigurationProvider
public sealed class SourceMemberAttribute(string name) : Attribute, IMemberConfigurationProvider
{
public string Name { get; }

public SourceMemberAttribute(string name) => Name = name;
public string Name { get; } = name;

public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,12 @@
/// Must be used in combination with <see cref="AutoMapAttribute" />
/// </remarks>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class ValueConverterAttribute : Attribute, IMemberConfigurationProvider
public sealed class ValueConverterAttribute(Type type) : Attribute, IMemberConfigurationProvider
{
/// <summary>
/// <see cref="IValueConverter{TSourceMember,TDestinationMember}" /> type
/// </summary>
public Type Type { get; }

public ValueConverterAttribute(Type type)
{
Type = type;
}
public Type Type { get; } = type;

public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,12 @@
/// Must be used in combination with <see cref="AutoMapAttribute" />
/// </remarks>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class ValueResolverAttribute : Attribute, IMemberConfigurationProvider
public sealed class ValueResolverAttribute(Type type) : Attribute, IMemberConfigurationProvider
{
/// <summary>
/// <see cref="IValueResolver{TSource,TDestination,TDestMember}" /> or <see cref="IMemberValueResolver{TSource,TDestination,TSourceMember,TDestMember}" /> type
/// </summary>
public Type Type { get; }

public ValueResolverAttribute(Type type)
{
Type = type;
}
public Type Type { get; } = type;

public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
{
Expand Down
23 changes: 9 additions & 14 deletions src/AutoMapper/Configuration/ConfigurationValidator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using AutoMapper.Internal.Mappers;
namespace AutoMapper.Configuration;

[EditorBrowsable(EditorBrowsableState.Never)]
public readonly record struct ConfigurationValidator(IGlobalConfigurationExpression Expression)
{
Expand All @@ -11,7 +10,7 @@ private void Validate(ValidationContext context)
validator(context);
}
}
public void AssertConfigurationExpressionIsValid(IGlobalConfiguration config, IEnumerable<TypeMap> typeMaps)
public void AssertConfigurationExpressionIsValid(IGlobalConfiguration config, TypeMap[] typeMaps)
{
var duplicateTypeMapConfigs = Expression.Profiles.Append((Profile)Expression)
.SelectMany(p => p.TypeMapConfigs, (profile, typeMap) => (profile, typeMap))
Expand All @@ -26,25 +25,23 @@ public void AssertConfigurationExpressionIsValid(IGlobalConfiguration config, IE
}
AssertConfigurationIsValid(config, typeMaps);
}
public void AssertConfigurationIsValid(IGlobalConfiguration config, IEnumerable<TypeMap> typeMaps)
public void AssertConfigurationIsValid(IGlobalConfiguration config, TypeMap[] typeMaps)
{
var maps = typeMaps as TypeMap[] ?? typeMaps.ToArray();
var badTypeMaps =
(from typeMap in maps
(from typeMap in typeMaps
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())
if (badTypeMaps.Length > 0)
{
throw new AutoMapperConfigurationException(badTypeMaps);
}
var typeMapsChecked = new HashSet<TypeMap>();
var configExceptions = new List<Exception>();
foreach (var typeMap in maps)
HashSet<TypeMap> typeMapsChecked = [];
List<Exception> configExceptions = [];
foreach (var typeMap in typeMaps)
{
try
{
Expand Down Expand Up @@ -81,8 +78,7 @@ private void DryRunTypeMap(IGlobalConfiguration config, HashSet<TypeMap> typeMap
return;
}
typeMapsChecked.Add(typeMap);
var context = new ValidationContext(types, memberMap, typeMap);
Validate(context);
Validate(new(types, memberMap, typeMap));
if(!typeMap.ShouldCheckForValid)
{
return;
Expand All @@ -96,8 +92,7 @@ private void DryRunTypeMap(IGlobalConfiguration config, HashSet<TypeMap> typeMap
{
throw new AutoMapperConfigurationException(memberMap.TypeMap.Types) { MemberMap = memberMap };
}
var context = new ValidationContext(types, memberMap, ObjectMapper: mapperToUse);
Validate(context);
Validate(new(types, memberMap, ObjectMapper: mapperToUse));
if (mapperToUse.GetAssociatedTypes(types) is TypePair newTypes && newTypes != types)
{
DryRunTypeMap(config, typeMapsChecked, newTypes, null, memberMap);
Expand Down
22 changes: 8 additions & 14 deletions src/AutoMapper/Configuration/Conventions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ public void Merge(MemberConfiguration other)
}
public sealed class PrePostfixName : ISourceToDestinationNameMapper
{
public List<string> DestinationPrefixes { get; } = new();
public List<string> DestinationPostfixes { get; } = new();
public List<string> DestinationPrefixes { get; } = [];
public List<string> DestinationPostfixes { get; } = [];
public MemberInfo GetSourceMember(TypeDetails sourceTypeDetails, Type destType, Type destMemberType, string nameToSearch)
{
MemberInfo member;
Expand All @@ -89,15 +89,15 @@ public void Merge(ISourceToDestinationNameMapper other)
}
public sealed class ReplaceName : ISourceToDestinationNameMapper
{
public List<MemberNameReplacer> MemberNameReplacers { get; } = new();
public List<MemberNameReplacer> MemberNameReplacers { get; } = [];
public MemberInfo GetSourceMember(TypeDetails sourceTypeDetails, Type destType, Type destMemberType, string nameToSearch)
{
var possibleSourceNames = PossibleNames(nameToSearch);
if (possibleSourceNames.Length == 0)
if (possibleSourceNames.Count == 0)
{
return null;
}
var possibleDestNames = sourceTypeDetails.ReadAccessors.Select(mi => (mi, possibles : PossibleNames(mi.Name))).ToArray();
var possibleDestNames = Array.ConvertAll(sourceTypeDetails.ReadAccessors, mi => (mi, possibles : PossibleNames(mi.Name)));
foreach (var sourceName in possibleSourceNames)
{
foreach (var (mi, possibles) in possibleDestNames)
Expand All @@ -110,15 +110,9 @@ public MemberInfo GetSourceMember(TypeDetails sourceTypeDetails, Type destType,
}
return null;
}
public void Merge(ISourceToDestinationNameMapper other)
{
var typedOther = (ReplaceName)other;
MemberNameReplacers.TryAdd(typedOther.MemberNameReplacers);
}
private string[] PossibleNames(string nameToSearch) =>
MemberNameReplacers.Select(r => nameToSearch.Replace(r.OriginalValue, r.NewValue))
.Concat(new[] { MemberNameReplacers.Aggregate(nameToSearch, (s, r) => s.Replace(r.OriginalValue, r.NewValue)), nameToSearch })
.ToArray();
public void Merge(ISourceToDestinationNameMapper other) => MemberNameReplacers.TryAdd(((ReplaceName)other).MemberNameReplacers);
private List<string> PossibleNames(string nameToSearch) => [..MemberNameReplacers.Select(r => nameToSearch.Replace(r.OriginalValue, r.NewValue)),
MemberNameReplacers.Aggregate(nameToSearch, (s, r) => s.Replace(r.OriginalValue, r.NewValue)), nameToSearch];
}
[EditorBrowsable(EditorBrowsableState.Never)]
public readonly record struct MemberNameReplacer(string OriginalValue, string NewValue);
Expand Down
20 changes: 4 additions & 16 deletions src/AutoMapper/Configuration/CtorParamConfigurationExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,36 +34,24 @@ public interface ICtorParameterConfiguration
void Configure(TypeMap typeMap);
}
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class CtorParamConfigurationExpression<TSource, TDestination> : ICtorParamConfigurationExpression<TSource>, ICtorParameterConfiguration
public sealed class CtorParamConfigurationExpression<TSource, TDestination>(string ctorParamName, Type sourceType) : ICtorParamConfigurationExpression<TSource>, ICtorParameterConfiguration
{
public string CtorParamName { get; }
public Type SourceType { get; }

private readonly List<Action<ConstructorParameterMap>> _ctorParamActions = new List<Action<ConstructorParameterMap>>();

public CtorParamConfigurationExpression(string ctorParamName, Type sourceType)
{
CtorParamName = ctorParamName;
SourceType = sourceType;
}

public string CtorParamName { get; } = ctorParamName;
public Type SourceType { get; } = sourceType;
private readonly List<Action<ConstructorParameterMap>> _ctorParamActions = [];
public void MapFrom<TMember>(Expression<Func<TSource, TMember>> sourceMember) =>
_ctorParamActions.Add(cpm => cpm.MapFrom(sourceMember));

public void MapFrom<TMember>(Func<TSource, ResolutionContext, TMember> resolver)
{
Expression<Func<TSource, TDestination, TMember, ResolutionContext, TMember>> resolverExpression = (src, dest, destMember, ctxt) => resolver(src, ctxt);
_ctorParamActions.Add(cpm => cpm.SetResolver(new FuncResolver(resolverExpression)));
}

public void MapFrom(string sourceMembersPath)
{
var sourceMembers = ReflectionHelper.GetMemberPath(SourceType, sourceMembersPath);
_ctorParamActions.Add(cpm => cpm.MapFrom(sourceMembersPath, sourceMembers));
}

public void ExplicitExpansion(bool value) => _ctorParamActions.Add(cpm => cpm.ExplicitExpansion = value);

public void Configure(TypeMap typeMap)
{
var ctorMap = typeMap.ConstructorMap;
Expand Down
Loading

0 comments on commit 343a455

Please sign in to comment.