c# - AutoMapper map 长吗?串起来

标签 c# automapper

我有两个类,一个 ViewModel 和一个 Dto,它们基本上是相同的,除了 Dto 有一个字段“readonly long?”电话;'而 ViewModel 有一个属性 'string Phone { get;放; }'。

我发现让 AutoMapper 工作的唯一方法是将 ViewModel 属性更改为支持属性:

    public long? Phone { get; set; }

    public string PhoneNumberString
    {
        get
        {
            var srv = DependencyResolver.Current.GetService<IPhoneNumberService>();
            return srv.GetFormattedPhoneNumber(Phone);
        }
        set
        {
            var srv = DependencyResolver.Current.GetService<IPhoneNumberService>();
            Phone = srv.GetLongPhoneNumber(value);
        }
    }

然后在 AutoMapper 中,有一大行代码来调用构造函数:

Mapper.Initialize(cfg =>
{
    cfg.CreateMap<MyViewModel, MyDto>()
        .ConstructUsing(src => new MyDto(
            src.Phone
    /* ...Some ~30 other parameters here... */))
        .ReverseMap();
});

...必须有更好的方法来做到这一点吗?我已经尝试过这些:

.ForSourceMember(x => x.PhoneNumberString, opt => opt.DoNotValidate())

.ForMember(x => x.PhoneNumberString, opt => opt.Ignore())

.ForMember(viewModel => viewModel.Phone, options => options.MapFrom<PhoneNumberResolver>());//PhoneNumberResolver implements IValueResolver<ProspectMaintenanceViewModel, ProspectMaintenanceDto, long?>

这一切都给出了“Core.DTO.MyDto 需要有一个带有 0 个参数或只有可选参数的构造函数。”当尝试绘制 map 时,并且:

.ForMember(dest => dest.Phone, opt => opt.MapFrom(src => 5))

尝试配置 AutoMapper 时,会出现“System.ArgumentException:‘表达式必须可写’”。

有什么方法可以让 AutoMapper 理解它可以完全忽略 PhoneNumberString (或者更好的是,我可以通过某种方式让它映射很长?到字符串,这样我就不需要支持属性),而不必使用 dto 的构造函数?

Is there any special reason that requires your DTO to not have a default constructor?

我将所有字段设置为只读,以便我可以包含一个修改(例如“Description = description?.Trim();”)和验证(例如“if (Phone.HasValue && Phone.ToString().长度!= 10)抛出...')参数。这样我就可以确保 Dto 作为值对象始终处于有效状态。

最佳答案

1) 映射到只读字段

所以你有一个 Dto 类:

public class Dto
{
    public readonly long? PhoneNumber;
}

然后你试图强制 AutoMapper 执行此操作:

var dto = new Dto();
dto.PhoneNumber = 123; // <== ERROR! A readonly field cannot be assigned to.

AutoMapper 无法写入只读字段或属性。事实上你也不是。使用 protectedprivate setter 将您的字段转换为属性:

public class Dto
{
    public long? PhoneNumber { get; private set; }
}

或者通过删除 readonly 关键字使其成为常规字段:

public class Dto
{
    public long? PhoneNumber;
}

2) 自定义值解析

ASP.NET MVC

使用 ValueResolver :

public class StringPhoneNumberResolver : IValueResolver<Dto, ViewModel, string>
{
    private readonly IPhoneNumberService _phoneNumberService;

    public StringPhoneNumberResolver()
    {
        _phoneNumberService = DependencyResolver.Current.GetService<IPhoneNumberService>();
    }

    public string Resolve(Dto source, ViewModel destination, string destMember, ResolutionContext context)
    {
        return _phoneNumberService.GetFormattedPhoneNumber(source.PhoneNumber);
    }
}

您应该知道,通常在 DTO 或 IValueResolver 中进行服务注入(inject)是一种反模式。 AutoMapper 应该是愚蠢的,所有类型的注入(inject)等都应该在其他地方处理。话虽这么说,这是 AutoMapper 配置:

Mapper.Initialize(cfg =>
{
    cfg.CreateMap<Dto, ViewModel>()
        .ForMember(viewModel => viewModel.PhoneNumber, options =>
            options.MapFrom<StringPhoneNumberResolver>());
});

如果要将long ==> string 的过程反转为string ==> long只需添加另一个值解析器:

public class LongPhoneNumberResolver : IValueResolver<ViewModel, Dto, long?>
{
    private readonly IPhoneNumberService _phoneNumberService;

    public LongPhoneNumberResolver()
    {
        _phoneNumberService = DependencyResolver.Current.GetService<IPhoneNumberService>();
    }

    public long? Resolve(ViewModel source, Dto destination, long? destMember, ResolutionContext context)
    {
        return _phoneNumberService.GetLongPhoneNumber(source.PhoneNumber);
    }
}

.NET Core

如果您要在完全支持 IServiceCollection 集成的 .NET Core 环境中操作,您应该添加以下 AutoMapper 配置:

serviceCollection.AddAutoMapper(config =>
{
    config.CreateMap<Dto, ViewModel>()
        .ForMember(viewModel => viewModel.PhoneNumber, options =>
            options.MapFrom<StringPhoneNumberResolver>());
}, typeof(Startup));

然后将 IPhoneNumberServce 自动注入(inject)到值解析器中:

public StringPhoneNumberResolver(IPhoneNumberService phoneNumberService)
{
    _phoneNumberService = phoneNumberService;
}

对于依赖注入(inject),我使用了 automapper.extensions.microsoft.dependencyinjection包。

关于c# - AutoMapper map 长吗?串起来,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56656007/

相关文章:

c# - FitNesse/FitSharp - 动态读取 ColumnFixture 的列

C# 容器 : Row, 索引

silverlight - 相当于 silverlight 项目中的自动映射器

c# - AutoMapper Map() 返回错误值

c# - 自动映射器。如果源成员为空则映射

c# - 依赖解析器的 Automapper IMapper 问题

c# - 如何让 JAWS 读取一个简单的标签?

c# - WCF netTcpBinding 异步调用

c# - 我如何知道智能卡是否已插入?

c# - 带有 IDataReader 的 AutoMapper 到 List 总是返回 0 计数