0

I have an existing .NET Framework 4.8 Web API project that uses AutoMapper version 7.0.1.

I have upgraded the project to .NET 6 and the AutoMapper nuget package to version 10.1.1.

It appears that the existing mappings no longer work as they did in version 7.0.1.

The existing mappings class look like this:

public static class AutoMapperConfig
{
    public static void CreateMaps()
    {
        Mapper.Initialize(cfg =>
        {
            CreateAccountConnectorMaps(cfg);
            CreateConnectorMaps(cfg);
        });
    }

    private static void CreateAccountConnectorMaps(IMapperConfigurationExpression cfg)
    {
        cfg.CreateMap<AccountConnector, AccountConnectorModel>()
            .ReverseMap()
            .ForMember(m => m.CustomFields, o => o.Ignore())
            .ForMember(m => m.Properties, o => o.Ignore())
            .ForAllMembers(IgnoreSourceValuesWithInternalOrPrivateSetters);
    }

    private static void CreateConnectorMaps(IMapperConfigurationExpression cfg)
    {
        cfg.CreateMap<Connector, ConnectorModel>()
            .ForMember(d => d.Parameters, o => o.MapFrom(s => s.Parameters.Where(p =>
                    !p.IsPartnerProperty &&
                    !p.Hide &&
                    p.Values.None(v => ParameterValues.IsPagingParameter(v.Value)))))
            .ForMember(d => d.AuthDescription, o => o.MapFrom(s => s.Authentication.FirstOrDefault(x => x.IsDefault).AuthDescription))
            .ForMember(d => d.AuthType, o => o.MapFrom(s => s.Authentication.FirstOrDefault(x => x.IsDefault).AuthType))
            .ForMember(d => d.OAuth2Type, o => o.MapFrom(s => s.Authentication.FirstOrDefault(x => x.IsDefault).OAuth2Type))
            .ForMember(d => d.AuthScheme, o => o.MapFrom(s => s.Authentication.FirstOrDefault(x => x.IsDefault).AuthScheme))
            .ForMember(d => d.IsSystemConnector, o => o.MapFrom(s => s.ProductAddon != null && s.ProductAddon.IsSystemConnector))
            .ForMember(d => d.Categories, o => o.MapFrom(s => s.ProductAddon == null ? null : s.ProductAddon.Categories.Select(c => c.Category.Name)))
            .ForMember(d => d.PartnerSetupGuideUrl, o => o.MapFrom(s => s.ProductAddon == null ? null : s.ProductAddon.PartnerSetupGuideUrl))
            .ForMember(d => d.CustomizableCategories, o => o.MapFrom(s => s.MethodCategories == null ? null : s.MethodCategories.Where(item => item.CanCopyToAccountConnectorMethodCategory)))
            .ReverseMap()
            .ForAllMembers(IgnoreSourceValuesWithInternalOrPrivateSetters);
    }

    private static void IgnoreSourceValuesWithInternalOrPrivateSetters<TSource, TDestination, TMember>(IMemberConfigurationExpression<TSource, TDestination, TMember> member)
    {
        member.Condition((source, destination, sourceMember, destMember) =>
        {
            if (sourceMember == null)
            {
                return false;
            }

            var sourceProp = source.GetType().GetProperty(member.DestinationMember.Name);
            return sourceProp?.GetSetMethod(false) != null;
        });
    }
}

The new mappings haven't changed except since the version upgrade the AutoMapperConfig class now inherits from AutoMapper.Profile.

The new mappings class looks like this:

public class AutoMapperConfig : Profile
{
    public AutoMapperConfig()
    {
        CreateAccountConnectorMaps();
        CreateConnectorMaps();
    }

    private void CreateAccountConnectorMaps()
    {
        CreateMap<AccountConnector, AccountConnectorModel>()
            .ReverseMap()
            .ForMember(src => src.CustomFields, opt => opt.Ignore())
            .ForMember(src => src.Properties, opt => opt.Ignore())
            .ForAllMembers(IgnoreSourceValuesWithInternalOrPrivateSetters);
    }

    private void CreateConnectorMaps()
    {
        CreateMap<Connector, ConnectorModel>()
            .ForMember(dest => dest.Parameters, opt => opt.MapFrom(src => src.Parameters.Where(p =>
                    !p.IsPartnerProperty &&
                    !p.Hide &&
                    p.Values.None(v => ParameterValues.IsPagingParameter(v.Value)))))
            .ForMember(dest => dest.AuthDescription, opt => opt.MapFrom(src => src.Authentication.FirstOrDefault(x => x.IsDefault).AuthDescription))
            .ForMember(dest => dest.AuthType, opt => opt.MapFrom(src => src.Authentication.FirstOrDefault(x => x.IsDefault).AuthType))
            .ForMember(dest => dest.OAuth2Type, opt => opt.MapFrom(src => src.Authentication.FirstOrDefault(x => x.IsDefault).OAuth2Type))
            .ForMember(dest => dest.AuthScheme, opt => opt.MapFrom(src => src.Authentication.FirstOrDefault(x => x.IsDefault).AuthScheme))
            .ForMember(dest => dest.IsSystemConnector, opt => opt.MapFrom(src => src.ProductAddon != null && src.ProductAddon.IsSystemConnector))
            .ForMember(dest => dest.Categories, opt => opt.MapFrom(src => src.ProductAddon == null ? null : src.ProductAddon.Categories.Select(c => c.Category.Name)))
            .ForMember(dest => dest.PartnerSetupGuideUrl, opt => opt.MapFrom(src => src.ProductAddon == null ? null : src.ProductAddon.PartnerSetupGuideUrl))
            .ForMember(dest => dest.CustomizableCategories, opt => opt.MapFrom(src => src.MethodCategories == null ? null : src.MethodCategories.Where(mc => mc.CanCopyToAccountConnectorMethodCategory)))
            .ReverseMap()
            .ForAllMembers(IgnoreSourceValuesWithInternalOrPrivateSetters);
    }

    private void IgnoreSourceValuesWithInternalOrPrivateSetters<TSource, TDestination, TMember>(IMemberConfigurationExpression<TSource, TDestination, TMember> member)
    {
        member.Condition((source, destination, sourceMember, destMember) =>
        {
            if (sourceMember == null)
            {
                return false;
            }

            var sourceProp = source.GetType().GetProperty(member.DestinationMember.Name);
            return sourceProp?.GetSetMethod(false) != null;
        });
    }
}

N.B. The domain entities and the models are the same across both projects.

In the Web API controller action on the existing .NET Framework Web API project when I try to map an instance of an AccountConnector to the AccountConnectorModel type using:

var mappedModel = Mapper.Map<AccountConnectorModel>(accountConnector);

it maps successfully.

However when I do the same on the new .NET 6 Web API project using:

var mappedModel = _mapper.Map<AccountConnectorModel>(accountConnector);

It throws a mapping exception, with 2 inner exceptions.

Outer Exception

Error mapping types.

Mapping types:
AccountConnector -> AccountConnectorModel
Domain.Connectors.AccountConnector -> Models.AccountConnectorModel

Type Map configuration:
AccountConnector -> AccountConnectorModel
Domain.Connectors.AccountConnector -> Models.AccountConnectorModel

Destination Member:
Connector

Inner Exception 1

Error mapping types.

Mapping types:
Connector -> ConnectorModel
Domain.Connectors.Connector -> Models.ConnectorModel

Type Map configuration:
Connector -> ConnectorModel
Domain.Connectors.Connector -> Models.ConnectorModel

Destination Member:
Authentication

Inner Exception 2

Missing type map configuration or unsupported mapping.

Mapping types:
ConnectorAuthentication -> ConnectorAuthenticationModel
Domain.Connectors.ConnectorAuthentication -> Models.ConnectorAuthenticationModel

0 Answers0