首页 > 解决方案 > 使用 Automapper 将扁平化 SQL 响应映射到包含列表列表的对象

问题描述

我在运行时收到的错误消息是:

未映射的成员被发现。查看下面的类型和成员。添加自定义映射表达式、忽略、添加自定义解析器或修改源/目标类型

对于没有匹配的构造函数,添加一个无参数 ctor、添加可选参数或映射所有构造函数参数

List'1 -> MobileRoot(目标成员列表)System.Collections.Generic.List'1[[Strata.CS.Jazz.Biz.Dashboard.MobileInfo, Strata.CS.Jazz.Biz, Version=2019.10.0.0, Culture=中性,PublicKeyToken=null]] -> Strata.Jazz.Web.Controllers.MobileController+MobileRoot(目标成员列表)

未映射的属性:用户


从错误消息中我可以看出,AutoMapper 需要知道如何处理在 MobileRoot 中创建的 ForMember 用户,然后将其传播到链中的每个后续列表。谁能告诉我如何使用 AutoMapper 有效地做到这一点?我知道如何使用 GroupBy 和 Select 对 Linq 执行此操作,因此我认为 AutoMapper 应该可以做到这一点。


我的查询返回这个类:

public class MobileInfo
{
    public string NameFull { get; set; }
    public string EmailAddress { get; set; }
    public string SolutionName { get; set; }
    public int SortOrder { get; set; }
    public string Name { get; set; }
    public bool IsLegacy { get; set; }
    public string Description { get; set; }
    public string WidgetName { get; set; }
    public int Row { get; set; }
    public int Col { get; set; }
    public int Height { get; set; }
    public int Width { get; set; }
    public string WidgetClassName { get; set; }
    public string  Data { get; set; }
}

我想将 Automapper 与配置文件一起使用以使其返回:

    internal class MobileRoot
    {
        public IEnumerable<MobileUser> Users { get; set; }
    }

    internal class MobileUser
    {
        public string NameFull { get; set; }
        public string EmailAddress { get; set; }
        public IEnumerable<MobileSolution> Solutions { get; set; }
    }

    internal class MobileSolution
    {
        public string Solution { get; set; } // MobileInfo.SolutionName
        public int SortOrder { get; set; }
        public IEnumerable<MobileDashboard> Dashboards { get; set; }
    }

    internal class MobileDashboard
    {
        public string Dashboard { get; set; } // MobileInfo.Name
        public bool IsLegacy { get; set; }
        public string Description { get; set; }
        public IEnumerable<MobileWidget> Widgets { get; set; }
    }

    internal class MobileWidget
    {
        public string Widget { get; set; } // MobileInfo.WidgetName
        public int Row { get; set; }
        public int Col { get; set; }
        public int Height { get; set; }
        public int Width { get; set; }
        public string WidgetClassName { get; set; }
        public string Data { get; set; }
    }

到目前为止我定义的配置文件是:

    public class ProfileMobileRoot : Profile
    {
        public ProfileMobileRoot()
        {
            CreateMap<MobileInfo, MobileRoot>();
        }
    }

    public class ProfileMobileUser : Profile
    {
        public ProfileMobileUser()
        {
            CreateMap<MobileInfo, MobileUser>();
        }
    }

    public class ProfileMobileSolution : Profile
    {
        public ProfileMobileSolution()
        {
            CreateMap<MobileInfo, MobileSolution>();
        }
    }

    public class ProfileMobileDashboard : Profile
    {
        public ProfileMobileDashboard()
        {
            CreateMap<MobileInfo, MobileRoot>();
        }
    }

    public class ProfileMobileWidget : Profile
    {
        public ProfileMobileWidget()
        {
            CreateMap<MobileInfo, MobileWidget>();
        }
    }

标签: c#.netautomapper

解决方案


您可以执行以下操作。有点晚了,所以我的解决方案不是那么复杂......但它有效;)

public class ProfileMobileRoot : Profile
{
    public ProfileMobileRoot()
    {
        CreateMap<MobileInfo, MobileWidget>()
            .ForMember(x=>x.Name, opt=>opt.MapFrom(x=>x.WidgetName));
        CreateMap<IEnumerable<MobileInfo>, IEnumerable<MobileDashboard>>()
            .ConvertUsing<DashboardConverter>();

        CreateMap<IEnumerable<MobileInfo>, IEnumerable<MobileSolution>>()
            .ConvertUsing<SolutionConverter>();
        CreateMap<IEnumerable<MobileInfo>, IEnumerable<MobileUser>>()
            .ConvertUsing<UserConverter>();

        CreateMap<IEnumerable<MobileInfo>, MobileRoot>()
            .ForMember(x => x.Users, opt => opt.MapFrom(x => x.ToList()));
    }
}
class UserConverter : ITypeConverter<IEnumerable<MobileInfo>, IEnumerable<MobileUser>>
{
    public IEnumerable<MobileUser> Convert(IEnumerable<MobileInfo> source, IEnumerable<MobileUser> destination, ResolutionContext context)
    {
        var groups = source.GroupBy(x => new { x.NameFull, x.EmailAddress});
        foreach (var v in groups)
        {
            yield return new MobileUser()
            {
                EmailAddress = v.Key.EmailAddress,
                NameFull = v.Key.NameFull,
                Solutions = context.Mapper.Map<IEnumerable<MobileSolution>>(source.Where(x =>
                    v.Key.NameFull == x.NameFull && v.Key.EmailAddress== x.EmailAddress)).ToList()
            };
        }
    }
}
class SolutionConverter : ITypeConverter<IEnumerable<MobileInfo>, IEnumerable<MobileSolution>>
{
    public IEnumerable<MobileSolution> Convert(IEnumerable<MobileInfo> source, IEnumerable<MobileSolution> destination, ResolutionContext context)
    {
        var groups = source.GroupBy(x => new { x.SolutionName, x.SortOrder});
        foreach (var v in groups)
        {
            yield return new MobileSolution()
            {
                Solution = v.Key.SolutionName,
                SortOrder = v.Key.SortOrder,
                Dashboards= context.Mapper.Map<IEnumerable<MobileDashboard>>(source.Where(x =>
                    v.Key.SolutionName== x.SolutionName&& v.Key.SortOrder== x.SortOrder)).ToList()
            };
        }
    }
}

class DashboardConverter : ITypeConverter<IEnumerable<MobileInfo>, IEnumerable<MobileDashboard>>
{
    public IEnumerable<MobileDashboard> Convert(IEnumerable<MobileInfo> source, IEnumerable<MobileDashboard> destination, ResolutionContext context)
    {
        var groups = source.GroupBy(x => new {x.Name, x.IsLegacy, x.Description});
        foreach (var v in groups)
        {
            yield return new MobileDashboard()
            {
                Dashboard = v.Key.Name,
                Description = v.Key.Description,
                IsLegacy = v.Key.IsLegacy,
                Widgets = context.Mapper.Map<IEnumerable<MobileWidget>>(source.Where(x =>
                    v.Key.IsLegacy == x.IsLegacy && v.Key.Name == x.Name && v.Key.Description == x.Description))
            };
        }
    }
}

推荐阅读