AutoMapper, требующий отображения членов и конструктора - PullRequest
0 голосов
/ 13 февраля 2019

Я новичок в 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 голосов
/ 14 февраля 2019

ConvertUsing полезно, если вы действительно хотите взять на себя отображение.Но более идиоматичным в этом случае будет отображение через конструкторов .Добавив еще один конструктор в 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; }
}
0 голосов
/ 13 февраля 2019

Конструкция вашей объектной модели в основном позволяет отображать (преобразовывать) только через конструкцию, поэтому не может использовать большинство возможностей автоматического и явного сопоставления 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)));
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...