在域模型和视图模型之间进行深度映射
我需要在平面ViewModel和深层结构化的Domain模型之间映射两种方法。 这将是我们解决方案中的常见情况。
我的模特是:
public class Client
{
...
public NotificationSettings NotificationSettings { get; set; }
public ContactDetails ContactDetails { get; set; }
...
}
public class NotificationSettings
{
...
public bool ReceiveActivityEmails { get; set; }
public bool ReceiveActivitySms { get; set; }
...
}
public class ContactDetails
{
...
public string Email { get; set }
public string MobileNumber { get; set; }
...
}
public class ClientNotificationOptionsViewModel
{
public string Email { get; set }
public string MobileNumber { get; set; }
public bool ReceiveActivityEmails { get; set; }
public bool ReceiveActivitySms { get; set; }
}
映射代码:
Mapper.CreateMap<Client, ClientNotificationOptionsViewModel>()
.ForMember(x => x.ReceiveActivityEmails, opt => opt.MapFrom(x => x.NotificationSettings.ReceiveActivityEmails))
.ForMember(x => x.ReceiveActivitySms, opt => opt.MapFrom(x => x.NotificationSettings.ReceiveActivitySms))
.ForMember(x => x.Email, opt => opt.MapFrom(x => x.ContactDetails.Email))
.ForMember(x => x.MobileNumber, opt => opt.MapFrom(x => x.ContactDetails.MobileNumber));
// Have to use AfterMap because ForMember(x => x.NotificationSettings.ReceiveActivityEmail) generates "expression must resolve to top-level member" error
Mapper.CreateMap<ClientNotificationOptionsViewModel, Client>()
.IgnoreUnmapped()
.AfterMap((from, to) =>
{
to.NotificationSettings.ReceiveActivityEmail = from.ReceiveActivityEmail;
to.NotificationSettings.ReceiveActivitySms = from.ReceiveActivitySms;
to.ContactDetails.Email = from.Email;
to.ContactDetails.MobileNumber = from.MobileNumber;
});
...
// Hack as ForAllMembers() returns void instead of fluent API syntax
public static IMappingExpression<TSource, TDest> IgnoreUnmapped<TSource, TDest>(this IMappingExpression<TSource, TDest> expression)
{
expression.ForAllMembers(opt => opt.Ignore());
return expression;
}
我不喜欢它,因为:
1)很麻烦
2)第二个映射几乎摧毁了Automapper的功能并手动实现了工作 - 唯一的优点是在代码中引用Automapper的一致性
有谁可以建议:
a)使用Automapper获取深层属性的更好方法?
b)更好的方式来执行这样的双向映射?
c)在这种情况下是否应该打扰使用Automapper的建议? 是否有一个令人信服的理由不会恢复到手动编码的简单方法? 例如。:
void MapManually(Client client, ClientNotificationOptionsViewModel viewModel)
{
viewModel.Email = client.ContactDetails.Email;
// etc
}
void MapManually(ClientNotificationOptionsViewModel viewModel, Client client)
{
client.ContactDetails.Email = viewModel.Email;
// etc
}
-Brendan
PS重构领域模型不是解决方案。
PPS可以通过扩展方法和一些时髦的反射来清理上面的代码来设置深层属性......但是如果可能的话,我宁愿使用automapper功能。
这也可以通过这种方式完成:
Mapper.CreateMap<ClientNotificationOptionsViewModel, Client>()
.ForMember(x => x.NotificationSettings, opt => opt.MapFrom(x => new NotificationSettings() { ReceiveActivityEmails = x.ReceiveActivityEmails, ReceiveActivitySms = x.ReceiveActivitySms}))
.ForMember(x => x.ContactDetails, opt => opt.MapFrom(x => new ContactDetails() { Email = x.Email, MobileNumber = x.MobileNumber }));
但与您的解决方案没有多大区别。
另外,您可以通过从模型创建一个映射到内部类来完成它:
Mapper.CreateMap<ClientNotificationOptionsViewModel, ContactDetails>();
Mapper.CreateMap<ClientNotificationOptionsViewModel, NotificationSettings>();
Mapper.CreateMap<ClientNotificationOptionsViewModel, Client>()
.ForMember(x => x.NotificationSettings, opt => opt.MapFrom(x => x))
.ForMember(x => x.ContactDetails, opt => opt.MapFrom(x => x));
您不需要在新映射中指定ForMember,因为这两个类中的属性具有相同的名称。
最后我发现AutoMapper不适合我的场景。
相反,我构建了一个自定义实用程序以提供双向映射和深度属性映射,从而允许如下配置。 鉴于我们的项目范围,我相信这是有道理的。
BiMapper.CreateProfile<Client, ClientNotificationsViewModel>()
.Map(x => x.NotificationSettings.ReceiveActivityEmail, x => x.ReceiveActivityEmail)
.Map(x => x.NotificationSettings.ReceiveActivitySms, x => x.ReceiveActivitySms)
.Map(x => x.ContactDetails.Email, x => x.Email)
.Map(x => x.ContactDetails.MobileNumber, x => x.MobileNumber);
BiMapper.PerformMap(client, viewModel);
BiMapper.PerformMap(viewModel, client);
道歉我无法分享实施,因为它是商业工作。 不过,我希望它可以帮助其他人知道建立自己的文件并不是不可能的,并且可以提供优于AutoMapper的优点或手动执行。
链接地址: http://www.djcxy.com/p/37351.html上一篇: way, deep mapping, between domain models and viewmodels