c# - 使用 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>();
}
}
解决方案
您可以执行以下操作。有点晚了,所以我的解决方案不是那么复杂......但它有效;)
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))
};
}
}
}
推荐阅读
- python - 如何在累积的熊猫系列中找到一个值?
- discord.js - Discord.js 备份 json 文件
- android - Environment.getExternalStorageDirectory() 已弃用。那么如何在 android API 29 中访问内部存储中的自定义文件夹呢?
- javascript - Google App Script looping through array of values
- git - Master 分支有关于合并分支的额外信息
- android - 奇怪的行为,尝试删除项目时数组为空
- c++ - 为什么重载的取消引用运算符与箭头运算符的工作方式不同?
- node.js - nodejs 应用程序在本地运行良好会在 glitch.com 中出现错误
- android - 从上次打开的活动中启动另一个应用程序意图(恢复)
- reactjs - 无法使用 navigation.navigate 导航到另一个屏幕