[英]AutoMapper mapping from multiple properties to complex objects fails with ReverseMap and custom value resolver
我在将多个属性反向映射回复杂对象时遇到问题,即使使用自定义值解析器也是如此。
以下是持久性 model:
public class EmailDbo
{
public int EmailId { get; set; }
public DateTime DateCreated { get; set; }
public DateTime? DateSent { get; set; }
public string SendTo { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
public bool DownloadAvailable { get; set; }
public DateTime? AdminDateSent { get; set; }
public string AdminEmail { get; set; }
public string AdminSubject { get; set; }
public string AdminBody { get; set; }
public int StatusId { get; set; }
}
我有来自数据库的 Dapper map 数据并填写此 model。
以下是我想要 map 与持久性 model 来回的域模型:
public class Email
{
public string SendTo { get; private set; }
public string Subject { get; private set; }
public string Body { get; private set; }
public DateTime? DateSent { get; private set; }
public Email(string sendTo, string subject, string body, DateTime? dateSent = null)
{
// Validations
this.SendTo = sendTo;
this.Subject = subject;
this.Body = body;
this.DateSent = dateSent;
}
}
public enum EmailTaskStatus
{
Sent = 1,
Unsent = 2
}
public class EmailTask
{
public int Id { get; private set; }
public DateTime DateCreated { get; private set; }
public Email PayerEmail { get; private set; }
public Email AdminEmail { get; private set; }
public bool DownloadAvailableForAdmin { get; private set; }
public EmailTaskStatus Status { get; private set; }
public EmailTask(int emailTaskId, DateTime dateCreated, Email payerEmail, Email adminEmail,
bool downloadAvailable, EmailTaskStatus status)
{
// Validations
this.Id = emailTaskId;
this.DateCreated = dateCreated;
this.PayerEmail = payerEmail;
this.AdminEmail = adminEmail;
this.DownloadAvailableForAdmin = downloadAvailable;
this.Status = status;
}
}
我想为付款人和管理员 email 使用一个名为Email
的值 object。 您可以看出它们只是扁平地存储在数据库/持久性 model 中。 付款人 email 是必需的,但不是管理员 email。
我的映射配置如下:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<EmailTask, EmailDbo>()
.ForMember(dest => dest.EmailId, opts => opts.MapFrom(src => src.Id))
.ForMember(dest => dest.SendTo, opts => opts.MapFrom(src => src.PayerEmail.SendTo))
.ForMember(dest => dest.Subject, opts => opts.MapFrom(src => src.PayerEmail.Subject))
.ForMember(dest => dest.Body, opts => opts.MapFrom(src => src.PayerEmail.Body))
.ForMember(dest => dest.DateSent, opts => opts.MapFrom(src => src.PayerEmail.DateSent))
.ForMember(dest => dest.DownloadAvailable, opts => opts.MapFrom(src => src.DownloadAvailableForAdmin))
.ForMember(dest => dest.AdminEmail, opts =>
{
opts.PreCondition(src => (src.AdminEmail != null));
opts.MapFrom(src => src.AdminEmail.SendTo);
})
.ForMember(dest => dest.AdminSubject, opts =>
{
opts.PreCondition(src => (src.AdminEmail != null));
opts.MapFrom(src => src.AdminEmail.Subject);
})
.ForMember(dest => dest.AdminBody, opts =>
{
opts.PreCondition(src => (src.AdminEmail != null));
opts.MapFrom(src => src.AdminEmail.Body);
})
.ForMember(dest => dest.AdminDateSent, opts =>
{
opts.PreCondition(src => (src.AdminEmail != null));
opts.MapFrom(src => src.AdminEmail.DateSent);
})
.ForMember(dest => dest.StatusId, opts => opts.MapFrom(src => (int)src.Status))
.ReverseMap()
.ForCtorParam("status", opts => opts.MapFrom(src => src.StatusId))
.ForMember(dest => dest.PayerEmail, opts => opts.MapFrom<PayerEmailValueResolver>())
.ForMember(dest => dest.AdminEmail, opts => opts.MapFrom<AdminEmailValueResolver>());
}
}
在ReverseMap()
之后,我想获取多个属性并构造复杂的 object Email
。 因此,我为此定义了两个自定义值解析器:
public class PayerEmailValueResolver : IValueResolver<EmailDbo, EmailTask, Email>
{
public Email Resolve(EmailDbo emailDbo, EmailTask emailTask, Email email, ResolutionContext context)
{
return new Email(emailDbo.SendTo, emailDbo.Subject, emailDbo.Body, emailDbo.DateSent);
}
}
public class AdminEmailValueResolver : IValueResolver<EmailDbo, EmailTask, Email>
{
public Email Resolve(EmailDbo emailDbo, EmailTask emailTask, Email email, ResolutionContext context)
{
if (String.IsNullOrWhiteSpace(emailDbo.AdminEmail) &&
String.IsNullOrWhiteSpace(emailDbo.AdminSubject) &&
String.IsNullOrWhiteSpace(emailDbo.AdminBody) &&
!emailDbo.AdminDateSent.HasValue)
{
return null;
}
return new Email(emailDbo.SendTo, emailDbo.Subject, emailDbo.Body, emailDbo.DateSent);
}
}
与往常一样,从域 model 到 Dbo 的映射工作正常:
但不是其他方式,从 Dbo 到域 model。 它抛出异常:
未处理的异常。 System.ArgumentException:Program+EmailTask 需要有一个带有 0 个参数或只有可选参数的构造函数。 AutoMapper.Mapper.MapCore[TSource,Tdestination](TSource source, Tdestination destination, ResolutionContext context, Type sourceType, Type destinationType, IMemberMap memberMap) 中的 lambda_method32(Closure, Object, EmailTask, ResolutionContext ) 中的(参数“类型”)。 Mapper.Map[TSource,Tdestination](TSource source, Tdestination destination) at AutoMapper.Mapper.Map[TDestination](Object source)
.Net Fiddle 演示: https://dotnetfiddle.net/DcTsPG
我想知道 AutoMapper 是否混淆了这两个Email
对象:付款人 email 和管理员 email,因为它们都是Email
类型。
相反 map AutoMapper
无法创建EmailTask
的实例。
将无参数构造函数添加到您的EmailTask
class -
public EmailTask()
{
// AutoMapper use only
}
此外,由于您的值解析器正在创建Email
的实例,因此也向您的Email
class 添加一个无参数构造函数 -
public Email()
{
// AutoMapper use only
}
最后,修改EmailTask
class 中的PayerEmail
和AdminEmail
属性,以便可以公开设置它们 -
public Email PayerEmail { get; set; }
public Email AdminEmail { get; set; }
那应该可以解决您的问题。
编辑:
@David Liang,在阅读了您的评论后,我想说,根据 DDD 来适应您的场景,您可能需要修改当前的映射方法。
问题是,当您从EmailTask
映射EmailDbo
时,该过程更容易,因为EmailDbo
是 DTO 类型 class 没有参数化构造函数。 因此,仅属性映射就足以完成这项工作。
但是,当您尝试从EmailDbo
执行EmailTask
EmailTask 时,您正在尝试实例化一个域 model class ,它具有非常严格的定义,无法从外部尝试保护它的复杂类型作为参数的参数化构造函数,并且无法访问。 因此,您当前使用的.ReverseMap()
方法不会很有帮助,因为仅属性映射不足以为您提供实例化 class 所需的所有构造函数参数。 剧中还有 AutoMapper 的命名约定。
以下是来自EmailDbo
的EmailTask
的映射配置,其中分离出反向映射,并将值解析器重构为帮助器 class。 前向映射保持不变。
CreateMap<EmailDbo, EmailTask>()
.ConstructUsing((s, d) =>
new EmailTask(
s.EmailId,
s.DateCreated,
Helper.GetPayerEmail(s),
Helper.GetAdminEmail(s),
s.DownloadAvailable,
(EmailTaskStatus)s.StatusId))
.IgnoreAllPropertiesWithAnInaccessibleSetter();
Helper
class -
public class Helper
{
public static Email GetPayerEmail(EmailDbo emailDbo)
{
return new Email(emailDbo.SendTo, emailDbo.Subject, emailDbo.Body, emailDbo.DateSent);
}
public static Email GetAdminEmail(EmailDbo emailDbo)
{
if (string.IsNullOrWhiteSpace(emailDbo.AdminEmail) && string.IsNullOrWhiteSpace(emailDbo.AdminSubject)
&& string.IsNullOrWhiteSpace(emailDbo.AdminBody) && !emailDbo.AdminDateSent.HasValue)
{
return null;
}
return new Email(emailDbo.SendTo, emailDbo.Subject, emailDbo.Body, emailDbo.DateSent);
}
}
这是完整的小提琴 - https://dotnetfiddle.net/2MxSdt
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.