如何指定自动映射器异常导致的数据?

How to specify automapper exception caused data?

我正在使用第三个工具从文件中读取坐标值,该工具将数据投射到 Point class 集合。

public class Point
{
    public string Lon {get;set;}
    public string Lat {get;set;}
    public string Elevation {get;set;}
}

我想使用自动映射器将 Point class 映射到 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值是错误的)

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

例子

使用 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);
}