C#-Automapper项目从子级访问父级

从ProjectTo doesn’t support BeforeMap开始,如何将父对象传递给子对象集合?这是代码:

void Main()
{
    Mapper.Initialize(c =>
    {
        c.AddProfile<Prof>();
    });

    myDbContext.Profiles
        .ProjectTo<ProfileDto>()
        .Dump(); // Linqpad Extension

}

public class Prof : Profile
{
    public Prof()
    {
        CreateMap<ProfileEntity, ProfileDto>();
        CreateMap<UserEntity, UserDto>();
    }
}

public class ProfileDto
{
    public string Name { get; set; }
    public IEnumerable<UserDto> Users { get; set; }
}

public class UserDto
{
    public string Name { get; set; }
    public string ProfileName { get; set; }
}

我只想填写“ ProfileName”属性.

解决方法:

AutoMapper映射配置在单个(源,目标)级别上工作.我看不到将父级传递给子级源配置的任何方法.当然,您可以像在其他答案中一样使用全投影(LINQ Select),但是,如果要映射的对象包含许多属性,那么即使按惯例它们按名称匹配,这也会导致很多冗余代码.

想到的一种方法是使用中间投影将父项与子项配对:

class Prof : Profile
{
    public Prof()
    {
        CreateMap<ProfileEntity, ProfileDto>()
            .ForMember(dst => dst.Users, opt => opt.MapFrom(src =>
                src.Users.Select(u => new { User = u, Profile = src })));
    }
}

现在,AutoMapper Flattening功能将自动将source.Profile.Name映射到target.ProfileName.问题是主要属性(来自用户)不会被映射,因为它们在目标对象中没有前缀用户(例如UserName).我们可以通过定制Naming Convention解决此问题.将以下内容添加到项目中的新代码文件中:

using System;
using System.Collections.Generic;
using System.Reflection;
using AutoMapper.Configuration.Conventions;
using AutoMapper.Internal;

namespace AutoMapper.Configuration.Conventions
{
    public class NameMatchMember : IChildMemberConfiguration
    {
        public bool MapDestinationPropertyToSource(ProfileMap options, TypeDetails sourceType, Type destType, Type destMemberType, string nameToSearch, LinkedList<MemberInfo> resolvers, IMemberConfiguration parent)
        {
            foreach (var memberInfo in sourceType.AllMembers)
            {
                if (!destType.Name.StartsWith(memberInfo.Name)) continue;
                resolvers.AddLast(memberInfo);
                var memberType = options.CreateTypeDetails(ReflectionHelper.GetMemberType(memberInfo));
                if (parent.MapDestinationPropertyToSource(options, memberType, destType, destMemberType, nameToSearch, resolvers))
                    return true;
                resolvers.RemoveLast();
            }
            return false;
        }
    }
}

namespace AutoMapper
{
    public static class Extensions
    {
        public static IMemberConfiguration AddNameMatchMemberConvention(this IProfileExpression target) =>
            target.AddMemberConfiguration().AddNameMatchMemberConvention();

        public static IMemberConfiguration AddNameMatchMemberConvention(this IMemberConfiguration target) =>
            target.AddMember<NameMatchMember>();
    }
}

第一类实现自定义约定.对于未由其他约定映射的每个目标名称,它将尝试将其映射到作为目标类型名称前缀的源属性(例如,用户-> UserDto).

第二种只是将其添加到配置中的便捷方法.它可以在配置或概要文件级别上应用,但是为了对匿名类型产生影响,必须在配置级别上完成.

现在,您需要将其应用于您的配置:

Mapper.Initialize(c =>
{
    c.AddNameMatchMemberConvention();
    c.AddProfile(new Prof());
});

加上以上配置文件配置,就足以执行所需的映射.

上一篇:C#-ProjectTo无法识别无参数构造函数


下一篇:C#-AutoMapper覆盖递归类型