AutoMapper需要映射成员和构造函数

时间:2019-02-13 01:34:52

标签: c# automapper

我是AutoMapper的新手,我只是想尽一切办法去做最好的事情。

我很快遇到了两个简单但现实的对象模型之间的映射问题。首先是用于服务层:

public sealed class GeoLocation
{
    public GeoLocation(
        double latitude,
        double longitude)
    {
        this.Latitude = latitude;
        this.Longitude = longitude;
    }

    public double Latitude { get; private set; }

    public double Longitude { get; private set; }
}

public sealed class Location
{
    public Location(
        string name,
        GeoLocation geoLocation)
    {
        this.Name = name;
        this.GeoLocation = geoLocation;
    }

    public string Name { get; private set; }

    public GeoLocation GeoLocation { get; private set; }
}

第二个是上面对数据库层的简化表示:

public sealed class LocationEntity
{
    public LocationEntity(
        string name,
        double latitude,
        double longitude)
    {
        this.Name = name;
        this.Latitude = latitude;
        this.Longitude = longitude;
    }

    public string Name { get; }

    public double Latitude { get; }

    public double Longitude { get; }
}

如果我尝试通过简单的CreateMap<Location, LocationEntity>().ReverseMap()调用来映射类型,那么在验证映射时,我可以预料地会遇到问题:

AutoMapperConfigurationException: 
Unmapped members were found. Review the types and members below.
Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type
For no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters
===============================================
Location -> LocationEntity (Destination member list)
UserQuery+Location -> UserQuery+LocationEntity (Destination member list)

No available constructor.
===============================================
LocationEntity -> Location (Destination member list)
UserQuery+LocationEntity -> UserQuery+Location (Destination member list)

Unmapped properties:
GeoLocation
No available constructor.

足够公平。我不想映射每个构造函数参数,所以我尝试调用ConstructUsing

Mapper.Initialize(
    config =>
    {
        config
            .CreateMap<Location, LocationEntity>()
            .ConstructUsing((source, _) => new LocationEntity(source.Name, source.GeoLocation?.Latitude ?? 0.0, source.GeoLocation?.Longitude ?? 0));
        config
            .CreateMap<LocationEntity, Location>()
            .ConstructUsing((source, _) => new Location(source.Name, new GeoLocation(source.Latitude, source.Longitude)));
    });

但是,这仍然抱怨LocationEntity-> Location映射:

AutoMapperConfigurationException: 
Unmapped members were found. Review the types and members below.
Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type
For no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters
===============================================
LocationEntity -> Location (Destination member list)
UserQuery+LocationEntity -> UserQuery+Location (Destination member list)

Unmapped properties:
GeoLocation

不确定要做什么,我向ForMember-> LocationEntity映射添加了一个Location调用:

config
    .CreateMap<LocationEntity, Location>()
    .ConstructUsing((source, _) => new Location(source.Name, new GeoLocation(source.Latitude, source.Longitude)))
    .ForMember(
        x => x.GeoLocation,
        options => options.MapFrom((source, target, _, context) => new GeoLocation(source.Latitude, source.Longitude)));

虽然这解决了问题,但在我看来,我的映射已经变得有些复杂了。我想知道:是否有更好的方法可以做到这一点而不牺牲对象模型的设计?

2 个答案:

答案 0 :(得分:3)

您的对象模型设计基本上只允许通过构造进行映射(转换),因此不能使大多数AutoMapper自动和显式映射功能受益。

ConstructUsing用于选择用于创建目标实例的非默认构造函数,但仍需要成员映射。

您需要的是ConvertUsing方法:

  

跳过成员映射并使用自定义表达式转换为目标类型

Mapper.Initialize(config =>
{
    config.CreateMap<Location, LocationEntity>()
        .ConvertUsing(source => new LocationEntity(source.Name, source.GeoLocation?.Latitude ?? 0.0, source.GeoLocation?.Longitude ?? 0));
    config.CreateMap<LocationEntity, Location>()
        .ConvertUsing(source => new Location(source.Name, new GeoLocation(source.Latitude, source.Longitude)));
});

答案 1 :(得分:0)

如果您真的要接管映射,则

ConvertUsing会很有帮助。但是在这种情况下,更惯用的是通过constructors进行映射。通过将另一个构造函数添加到Location(如果需要,可以私有),甚至可以删除ForCtorParam

CreateMap<Location, LocationEntity>().ReverseMap().ForCtorParam("geoLocation", o=>o.MapFrom(s=>s));

class LocationEntity
{
public LocationEntity(string name, double geoLocationLatitude, double geoLocationLongitude)
{
    this.Name = name;
    this.Latitude = geoLocationLatitude;
    this.Longitude = geoLocationLongitude;
}
public string Name { get; }
public double Latitude { get; }
public double Longitude { get; }
}
相关问题