AutoMapper是否也可用作重新形状映射工具

我知道AutoMapper只是映射两个对象之间的属性。

Automapper不是为了重塑数据,我认为这必须编程。

我有一个PeriodDTO.IEnumerable ,需要重新整形为PeriodListViewModel.List

每个CellViewModel包含一个List

这不是1比1的映射,我想也许这不应该被映射 – 因为它不可能 –

 public class PeriodRequest { public IEnumerable Periods { get; set; } public IEnumerable Weeks { get; set; } } public class PeriodListViewModel { public PeriodListViewModel(IEnumerable periods) { CellViewModels = new List(); var groupedPeriods = periods.GroupBy(p => p.LessonNumber).OrderBy(p => p.Key).ToList(); foreach (var groupedPeriod in groupedPeriods) { var cellViewModel = new CellViewModel(); CellViewModels.Add(cellViewModel); foreach (var period in groupedPeriod) { var rowViewModel = new RowViewModel(); rowViewModel.Content = period.Content; rowViewModel.SchoolclassCode = period.SchoolclassCode; rowViewModel.DayName = period.LessonDate.ToShortDateString(); rowViewModel.DayIndex = (int)period.LessonDate.DayOfWeek; rowViewModel.LessonNumber = period.LessonNumber; cellViewModel.Rows.Add(rowViewModel); } } } public List CellViewModels { get; set; } } public class CellViewModel { public CellViewModel() { Rows = new List(); } public List Rows { get; set; } } public class RowViewModel { public string DayName { get; set; } public string SchoolclassCode { get; set; } public string Content { get; set; } public int DayIndex { get; set; } public int LessonNumber { get; set; } } public class Period { public int PeriodId { get; set; } public DateTime LessonDate { get; set; } public int LessonNumber { get; set; } public string SchoolclassCode { get; set; } public string Content { get; set; } public int SchoolyearId { get; set; } public Schoolyear Schoolyear { get; set; } ... } 

目前,PeriodListViewModel可以轻松进行unit testing。 我现在问自己,AutoMapper如何才能让情况变得更好?

虽然您没有从IEnumerableList的简单1:1映射,但您仍然可以从List获取某些值,只能在您获得类到级联系的位置使用它,您的示例位于PeriodRowViewModel之间。

考虑到这一点,您还可以更好地使用LINQ并在一次扫描动作中完成所有操作,前提是您设置了需要从PeriodRowViewModel重新整形的投影。

这是一个控制台代码示例,它正好展示了这一点。 请注意,我们通过ValueResolve使用ValueResolve的解析器类概念进行重新整形,并且我为CellViewModel添加了一个额外的构造CellViewModel来接受IEnumerable ,这有助于改进我们的声明。

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using AutoMapper; namespace GroupingMapping { public class PeriodRequest { public IEnumerable Periods { get; set; } public IEnumerable Weeks { get; set; } } public class RowViewModel { public string DayName { get; set; } public string SchoolclassCode { get; set; } public string Content { get; set; } public int DayIndex { get; set; } public int LessonNumber { get; set; } } public class Period { public int PeriodId { get; set; } public DateTime LessonDate { get; set; } public int LessonNumber { get; set; } public string SchoolclassCode { get; set; } public string Content { get; set; } public int SchoolyearId { get; set; } // No definition provided for Schoolyear //public Schoolyear Schoolyear { get; set; } } public class CellViewModel { public CellViewModel() { Rows = new List(); } public CellViewModel(IEnumerable rowVMSet) { Rows = new List(rowVMSet); } public List Rows { get; set; } } public class PeriodListViewModelEx { public PeriodListViewModelEx(IEnumerable periods) { CellViewModels = new List(periods .GroupBy(p => p.LessonNumber) .OrderBy(grp => grp.Key) .Select(grp => { return new CellViewModel( grp.Select(p => { return Mapper.Map(p); })); })); } public List CellViewModels { get; set; } } class DateTimeToDateNameResolver : ValueResolver { protected override string ResolveCore(DateTime source) { return source.ToShortDateString(); } } class DateTimeToDayOfWeekResolver : ValueResolver { protected override int ResolveCore(DateTime source) { return (int)source.DayOfWeek; } } class Program { static void Main(string[] args) { Mapper.CreateMap() .ForMember(dest => dest.DayName, opt => opt.ResolveUsing().FromMember(src => src.LessonDate)) .ForMember(dest => dest.DayIndex, opt => opt.ResolveUsing().FromMember(src => src.LessonDate)); Period[] periods = new Period[3]; periods[0] = new Period { PeriodId = 1, LessonDate = DateTime.Today.Add(new TimeSpan(1, 0, 0, 0)), LessonNumber = 101, SchoolclassCode = "CS101", Content = "Intro to CS", SchoolyearId = 2013 }; periods[1] = new Period { PeriodId = 2, LessonDate = DateTime.Today.Add(new TimeSpan(2, 0, 0, 0)), LessonNumber = 101, SchoolclassCode = "CS101", Content = "Intro to CS", SchoolyearId = 2013 }; periods[2] = new Period { PeriodId = 3, LessonDate = DateTime.Today.Add(new TimeSpan(1, 0, 0, 0)), LessonNumber = 102, SchoolclassCode = "EN101", Content = "English (I)", SchoolyearId = 2013 }; PeriodListViewModelEx pvModel = new PeriodListViewModelEx(periods); Console.WriteLine("CellViews: {0}", pvModel.CellViewModels.Count); foreach (CellViewModel cvm in pvModel.CellViewModels) { Console.WriteLine("{0} items in CellViewModel Group", cvm.Rows.Count); } Console.WriteLine("Inspecting CellViewModel Rows"); foreach (CellViewModel cvm in pvModel.CellViewModels) { foreach (RowViewModel rvm in cvm.Rows) { Console.WriteLine(" DayName: {0}", rvm.DayName); Console.WriteLine(" SchoolclassCode: {0}", rvm.SchoolclassCode); Console.WriteLine(" Content: {0}", rvm.Content); Console.WriteLine(" DayIndex: {0}", rvm.DayIndex); Console.WriteLine(" LessonNumber: {0}", rvm.LessonNumber); Console.WriteLine(" -"); } Console.WriteLine("--"); } Console.ReadKey(); } } } 

几个月来,我主要使用AutoMapper从我的实体模型中创建我的视图模型,基本上就是你在那里做的。 我使用AutoMapper来展平和解决非常复杂的模型。

我个人不会在视图模型的构造函数中这样做。 原因是您可能有不同的PeriodListViewModel源。 这个想法是,一旦你创建/配置了映射器,你就可以随意做Mapper.MapMapper.Map 取决于具体情况。 这样,您的PeriodViewModels和Period类都不会彼此有任何知识或关系。 如果Period类响应数据库更改而更改,那么您只需调整您的automapper映射以适应更改,使PeriodViewModel类保持不变,因此不必触及任何使用它的视图。 反之亦然。

即使我没有使用automapper,我仍然会使用静态辅助方法复制实体模型来查看模型。 像View Model这样简单的东西应该有一个简单的默认构造函数,以便于使用。

但是,我会说在AutoMapper中进行复杂的映射非常具有挑战性,调试有时令人生畏。 很多时候,我发现自己在另一个开发人员的办公室帮助他们了解正在发生的映射链。 正如它的名字所暗示的那样,当它深入研究子对象/集合/导航属性时,它会自动映射它们,因此很多事情都是隐含的 ,这很难调试。 您基本上必须对这些对象图进行很多心理可视化,然后搜索您认为正在使用的映射。 某些映射选项不允许使用多行匿名方法,因此您无法放置断点或额外的日志记录代码。

作为一个经常将数据从数据库推送到前端的开发人员,AutoMapper几乎就是我一直梦寐以求的,但在使用它几个月之后,我可能会避免使用它来翻译超过2级深度的对象图。 对于你在你的例子中所做的事情,我认为AutoMapper会很好。

更新:

我会继续查询和映射分离。 第一个查询以获取我的实体,因为我可能需要将参数传递到where条件。 然后将ToList中生成的List传递给automapper.map,将Period实体转换为PeriodViewModel。 在您的情况下,由于group by,您有一个嵌套集合。 您可以创建从Periods(groupedPeriod)集合到单个CellViewModel的映射

 foreach (var groupOfPeriods in groupedPeriods) { Mapper.Map(groupOfPeriods ); CellViewModels.Add(cellViewModel); } 

我相信这会起作用(假设你为它创建一个映射,因为IGrouping实现IEnumerable,在这种情况下,TElement是一个Period。

另一个选项是,而不是创建从IEnumerable到CellViewModel的映射,而不是创建从Period到RowViewModel的映射,并执行以下操作:

 ... foreach (var groupedPeriod in groupedPeriods) { var cellViewModel = new CellViewModel(); CellViewModels.Add(cellViewModel); foreach (var period in groupedPeriod) { var rowViewModel = Mapper.Map(period); cellViewModel.Rows.Add(rowViewModel); } } ... 

第二个选项可能是更简单的选项,因为映射会更简单。 我认为第一个选项可能更难维护/调试,因为过多的“魔法”将被添加到CreateMap中。

我没有任何旧的自动映射器代码,所以我不能给你准确的代码示例如何配置实际的CreateMap部分,但第二个选项应该相当简单。

此外,由于automapper只是通过告诉它如何映射单个项目来了解如何映射事物列表,因此您可能会放弃:

 ... foreach (var groupedPeriod in groupedPeriods) { var cellViewModel = new CellViewModel(); CellViewModels.Add(cellViewModel); cellViewModel.Rows = Mapper.Map>(groupedPeriod); } ... 

它取决于Rows集合的类型,如果它不是List则更改该部分,以便AutoMapper创建正确的集合类型。 有关映射列表的更多信息,请参阅: https : //github.com/AutoMapper/AutoMapper/wiki/Lists-and-arrays