我知道AutoMapper只是映射两个对象之间的属性。
Automapper不是用于重塑数据,我认为这必须编程。
我有一个PeriodDTO.IEnumerable<Period>
需要重新整理为PeriodListViewModel.List<CellViewModel>
。
每个CellViewModel
都有一个List<RowViewModel>
这不是1比1的映射,我想也许这不应该映射 - 因为它不可能 -
public class PeriodRequest
{
public IEnumerable<Period> Periods { get; set; }
public IEnumerable<DateTime> Weeks { get; set; }
}
public class PeriodListViewModel
{
public PeriodListViewModel(IEnumerable<Period> periods)
{
CellViewModels = new List<CellViewModel>();
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<CellViewModel> CellViewModels { get; set; }
}
public class CellViewModel
{
public CellViewModel()
{
Rows = new List<RowViewModel>();
}
public List<RowViewModel> 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可以轻松进行单元测试。我现在问自己,AutoMapper如何才能让情况变得更好?
答案 0 :(得分:1)
虽然您没有从IEnumerable<Period>
到List<CellViewModel>
的简单1:1映射,但您仍然可以从Automapper中获取一些值,方法是仅在您获得class-to-的地方使用它班级联系人,在您的示例中介于Period
和RowViewModel
之间。
考虑到这一点,您还可以更好地使用LINQ,并且只需设置需要从Period
到RowViewModel
进行重新整形的投影,即可完成所有操作。 / p>
这是一个控制台代码示例,可以准确地说明这一点。请注意,我们通过ValueResolve<TSource,TDestination>
使用Automapper的解析程序类概念进行重新整形,并为CellViewModel
添加了一个额外的构造函数来接受IEnumerable<RowViewModel>
,这有助于改进我们的声明。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AutoMapper;
namespace GroupingMapping
{
public class PeriodRequest
{
public IEnumerable<Period> Periods { get; set; }
public IEnumerable<DateTime> 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<RowViewModel>();
}
public CellViewModel(IEnumerable<RowViewModel> rowVMSet)
{
Rows = new List<RowViewModel>(rowVMSet);
}
public List<RowViewModel> Rows { get; set; }
}
public class PeriodListViewModelEx
{
public PeriodListViewModelEx(IEnumerable<Period> periods)
{
CellViewModels = new List<CellViewModel>(periods
.GroupBy(p => p.LessonNumber)
.OrderBy(grp => grp.Key)
.Select(grp =>
{
return new CellViewModel(
grp.Select(p => { return Mapper.Map<Period, RowViewModel>(p); }));
}));
}
public List<CellViewModel> CellViewModels { get; set; }
}
class DateTimeToDateNameResolver : ValueResolver<DateTime, string>
{
protected override string ResolveCore(DateTime source)
{
return source.ToShortDateString();
}
}
class DateTimeToDayOfWeekResolver : ValueResolver<DateTime, int>
{
protected override int ResolveCore(DateTime source)
{
return (int)source.DayOfWeek;
}
}
class Program
{
static void Main(string[] args)
{
Mapper.CreateMap<Period, RowViewModel>()
.ForMember(dest => dest.DayName, opt => opt.ResolveUsing<DateTimeToDateNameResolver>().FromMember(src => src.LessonDate))
.ForMember(dest => dest.DayIndex, opt => opt.ResolveUsing<DateTimeToDayOfWeekResolver>().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();
}
}
}
答案 1 :(得分:0)
几个月来,我主要使用AutoMapper从我的实体模型中创建我的视图模型,基本上就是你在那里做的。我使用AutoMapper来展平和解决非常复杂的模型。
我个人不会在视图模型的构造函数中这样做。原因是您可能具有PeriodListViewModel的不同来源。我们的想法是,一旦您创建/配置了地图制作器,您可以随意执行Mapper.Map<PeriodViewModel(collectionOfCells);
或Mapper.Map<PeriodViewModel(periodEntity);
,具体取决于具体情况。 这样,您的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<CellViewModel>(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<RowViewModel>(period);
cellViewModel.Rows.Add(rowViewModel);
}
}
...
第二个选项可能是更简单的选项,因为映射会更简单。我认为第一个选项可能更难以维护/调试,因为太多的“魔法”将被添加到CreateMap中。
我没有任何旧的自动映射器代码,因此我无法为您提供有关如何配置实际CreateMap部件的准确代码示例,但第二个选项应该相当简单。
此外,由于automapper只是通过告诉它如何映射单个项目来了解如何映射事物列表,因此您可能会放弃这个:
...
foreach (var groupedPeriod in groupedPeriods)
{
var cellViewModel = new CellViewModel();
CellViewModels.Add(cellViewModel);
cellViewModel.Rows = Mapper.Map<List<RowViewModel>>(groupedPeriod);
}
...
具体取决于集合Rows
的类型,如果它不是List<RowViewModel>
,则更改该部分,以便AutoMapper创建正确的集合类型。有关映射列表的更多信息,请参阅:https://github.com/AutoMapper/AutoMapper/wiki/Lists-and-arrays