如何指定 automapper 异常导致的数据?

Posted

技术标签:

【中文标题】如何指定 automapper 异常导致的数据?【英文标题】:How to specify automapper exception caused data? 【发布时间】:2021-08-13 12:17:06 【问题描述】:

我正在使用第三个工具从文件中读取坐标值,并且工具将数据转换为 Point 类集合。

public class Point

    public string Lon get;set;
    public string Lat get;set;
    public string Elevation get;set;

我想使用自动映射器将Point 类映射到PointEntity

public class PointEntity

    public float Lon get;set;
    public float Lat get;set;
    public float Elevation get;set;

我使用 Automapper 创建了一个地图。

public class LocationProfile : Profile

    public LocationProfile()
    
        CreateMap<Point, PointEntity>();
    

我已经创建了一个通用的映射器扩展来映射列表。

public static class MapperHelper

    public static TDest MapTo<TDest>(this object src)
    
        return (TDest)AutoMapper.Mapper.Map(src, src.GetType(), typeof(TDest));
    


IList<Point> data = readData<Point>("file");
var mapped = data.MapTo<PointEntity>();

但有时用户可能会在文件中输入错误的类型条目,如下所示。

Lon    Lat    Elevation
---    ---    ---------
11.5   25.6   80.56
12ab   89.87  14.83
1.7    x.8    9.3

在这种情况下,代码会抛出异常。

那么我怎样才能找到哪一行和哪一个值是错误的类型呢? (比如row1和Lon值不对)

【问题讨论】:

【参考方案1】:

改变你的设计思维。映射只是将一个数据传输到另一个数据。而 automapper 非常擅长这项任务。把事情简单化。所以,尽量保持简单。我读到您想在映射期间执行验证并提供有关它的信息。这使得映射更加复杂。在我的设计中,我不会让这种情况在映射过程中发生。在映射之前验证此信息。使用验证规则类在映射之前对其进行迭代。或者在读取时验证数据,以便您可以立即告知行和列位置。

示例

使用 ConvertUsing(但如果之前验证则不需要)步骤来实施

public class LocationProfile : Profile

    public LocationProfile()
    
        CreateMap<Point, PointEntity>().ConvertUsing<PointEntityConverter>();
    


public class PointEntityConverter : ITypeConverter<Point, PointEntity>

    public PointEntity Convert(Point source, PointEntity destination, ResolutionContext context)
    
        if (destination == null)
            destination = new PointEntity();

        destination.Lon = Parse(source.Lon, nameof(source.Lon));
        destination.Lat = Parse(source.Lat, nameof(source.Lat));
        destination.Elevation = Parse(source.Elevation, nameof(source.Elevation));

        return destination;
    

    private float Parse(string s, string paramName)
    
        if (float.TryParse(s, out float result))
            return result;

        throw new ArgumentException($"Invalide value (s) for parameter paramName", paramName);
    

实施

private readonly IMapper mapper;
private readonly IValidator<Point> validator;

public HowToSpecifyAutomapperExceptionCausedData(IMapper mapper)

    this.mapper = mapper;
    this.validator = new PointValidator();


public void Example()

    var data = readData<Point>("file");

    for (int i = 0; i < data.Count; i++)
        validator.AssertValide(data[i], (m, p) => OnInvalidArgument(m, p, i));

    var mapped = mapper.Map<IList<PointEntity>>(data);


private void OnInvalidArgument(string message, string paramName, int index)

    throw new ArgumentException($"message on index index", paramName);

验证器

internal interface IValidator<T>

    void AssertValide(T value, InvalideArgumentHandler handler);


internal delegate void InvalideArgumentHandler(string message, string paramName);

internal class PointValidator : IValidator<Point>

    public void AssertValide(Point value, InvalideArgumentHandler handler)
    
        AssertValideFloat(value.Lon, nameof(value.Lon), handler);
        AssertValideFloat(value.Lat, nameof(value.Lat), handler);
        AssertValideFloat(value.Elevation, nameof(value.Elevation), handler);
    

    private void AssertValideFloat(string s, string paramName, InvalideArgumentHandler handler)
    
        if (!float.TryParse(s, out float result))
            handler($"Invalide value (s) for parameter paramName", paramName);
    

为什么要使用委托? 其他示例通过收集所有(也为每个参数 [lon, lat, ...])无效参数。 现在看看使用处理程序的能力。

public void Example()

    var data = readData<Point>("file");

    var errors = new List<string>();

    for (int i = 0; i < data.Count; i++)
        validator.AssertValide(data[i], (m, p) => errors.Add($"m on index i"));

    if (errors.Any()) //using System.Linq;
        throw new ApplicationException(string.Join(Environment.NewLine, errors));

    var mapped = mapper.Map<IList<PointEntity>>(data);

【讨论】:

你的意思是在Point类中创建一个名为Validate的方法。并尝试解析属性。这是一种手动方式。 实现部分是单独的类吗? 你问,“实现部分是一个单独的类吗?”我的第一反应是盲目的yes。因为拥有尽可能小的构建块并在它们所属的地方做事总是更好。但在那之后我会说这取决于情况。在这种情况下你会做什么。我根据您的起始问题给出了一个实现。 (我确实在一个类中编写了示例并为这个答案剪掉了它。请参阅构造函数部分:public HowToSpecifyAutomapperExceptionCausedData(IMapper mapper))

以上是关于如何指定 automapper 异常导致的数据?的主要内容,如果未能解决你的问题,请参考以下文章

Automapper 链/嵌套对象映射抛出异常

实体框架与集合对象,使用 Automapper,更新时异常

AutoMapper指定列名进行映射

Automapper:忽略目标属性的验证异常

将 AutoMapper 与实体框架一起使用时出现异常

如何在 .NetCore 中使用 AutoMapper 高级功能