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