枚举及其整数值之间的 AutoMapper 映射因 ReverseMap 而失败
AutoMapper mapping between enum and its integer values fails with ReverseMap
该应用程序是使用 DDD 方法构建的,具有一组单独的持久性模型。我调用了数据库对象,或 dbo:
public class ParentDbo
{
public int ParentId { get; set; }
public int TypeId { get; set; }
}
public class ChildDbo
{
public int ChildId { get; set; }
public ParentDbo Parent { get; set; }
public int RetryNumber { get; set; }
}
我们有一个简单的模型可供查看:父子关系。 RetryNumber
表示数据库中的枚举值。
在检索数据时,它首先使用 Dapper 查询数据库,然后使用其 splitOn
功能将数据映射到其中。这部分无关紧要,但为了完整起见,我还是会展示它:
const string sql = "SELECT * FROM XXX ....";
using (var cnt = _dbConnectionFactory.CreateConnection())
{
var childDbos = await cnt.QueryAsync<ChildDbo, ParentDbo, ChildDbo>(
sql: sql,
map: (childDbo, parentDbo) =>
{
childDbo.Parent = parentDbo;
return childDbo;
},
splitOn: "ParentId"
);
}
Dapper 的局限性在于它无法将数据映射到 私有复杂 对象。这就是为什么我必须拥有 2 套模型的主要原因。我想用私有设置器和其他技术将数据和逻辑封装在域模型中。
这是我的领域模型:
public class Parent
{
public int Id { get; private set; }
public int TypeId { get; private set; }
public Parent(int parentId, int typeId)
{
// Validations
this.Id = parentId;
this.TypeId = typeId;
}
}
public class Child
{
public int Id { get; private set; }
public Parent Parent { get; private set; }
public Attempt Attempt { get; private set; }
public Child(int childId, Parent parent, Attempt attempt)
{
// Validations
this.Id = childId;
this.Parent = parent;
this.Attempt = attempt;
}
}
对于域模型,我不想要 public setter 和无参数构造函数。
Attempt
是具有整数支持值的枚举:
public enum Attempt
{
Original = 1,
FirstRetry = 2,
SecondRetry = 3,
LastRetry = 4
}
最后,我想使用AutoMapper 在Dbos 和领域模型之间进行映射。这是映射:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Child, ChildDbo>()
.ForMember(dest => dest.ChildId, opts => opts.MapFrom(src => src.Id))
.ForMember(dest => dest.RetryNumber, opts => opts.MapFrom(src => (int)src.Attempt))
.ReverseMap();
CreateMap<Parent, ParentDbo>()
.ForMember(dest => dest.ParentId, opts => opts.MapFrom(src => src.Id))
.ReverseMap();
}
}
我想要双向映射,所以我使用 ReverseMap()
。
.Net Fiddle 演示: https://dotnetfiddle.net/saEHWd
它毫无问题地将域模型映射到 dbos:
但从 dbos 到域模型的反向映射会抛出异常:
Unhandled exception. System.ArgumentException: Program+Child needs to have a constructor with 0 args or only optional args. (Parameter 'type')
at lambda_method18(Closure , Object , Child , ResolutionContext )
at AutoMapper.Mapper.MapCore[TSource,TDestination](TSource source, TDestination destination, ResolutionContext context, Type sourceType, Type destinationType, IMemberMap memberMap)
at AutoMapper.Mapper.Map[TSource,TDestination](TSource source, TDestination destination)
at AutoMapper.Mapper.Map[TDestination](Object source)
at Program.Main()
我已经尝试删除枚举 属性 并且一切正常所以我很确定是枚举映射有问题。
据我在您的 fiddle 中看到的,您正在尝试从 ChildDbo
映射到 Parent
,但没有针对它的映射设置。将映射代码更改为:
var child2 = mapper.Map<Child>(childDbo);
并且由于第三个 Child
的 ctor 参数和源 属性 名称不匹配,将映射更改为:
CreateMap<Child, ChildDbo>()
.ForMember(dest => dest.ChildId, opts => opts.MapFrom(src => src.Id))
.ForMember(dest => dest.RetryNumber, opts => opts.MapFrom(src => (int)src.Attempt))
.ReverseMap()
.ConstructUsing((dbo, ctx) => new Child(dbo.ChildId, ctx.Mapper.Map<Parent>(dbo.Parent), (Attempt)dbo.RetryNumber));
见here
或者将第三个Child
的ctor参数重命名为retryNumber
:
public Child(int childId, Parent parent, Attempt retryNumber)
参见 here。
或使用ForCtorParam
:
CreateMap<Child, ChildDbo>()
.ForMember(dest => dest.ChildId, opts => opts.MapFrom(src => src.Id))
.ForMember(dest => dest.RetryNumber, opts => opts.MapFrom(src => (int)src.Attempt))
.ReverseMap()
.ForCtorParam("attempt", opt => opt.MapFrom(dbo => dbo.RetryNumber))
Here.
该应用程序是使用 DDD 方法构建的,具有一组单独的持久性模型。我调用了数据库对象,或 dbo:
public class ParentDbo
{
public int ParentId { get; set; }
public int TypeId { get; set; }
}
public class ChildDbo
{
public int ChildId { get; set; }
public ParentDbo Parent { get; set; }
public int RetryNumber { get; set; }
}
我们有一个简单的模型可供查看:父子关系。 RetryNumber
表示数据库中的枚举值。
在检索数据时,它首先使用 Dapper 查询数据库,然后使用其 splitOn
功能将数据映射到其中。这部分无关紧要,但为了完整起见,我还是会展示它:
const string sql = "SELECT * FROM XXX ....";
using (var cnt = _dbConnectionFactory.CreateConnection())
{
var childDbos = await cnt.QueryAsync<ChildDbo, ParentDbo, ChildDbo>(
sql: sql,
map: (childDbo, parentDbo) =>
{
childDbo.Parent = parentDbo;
return childDbo;
},
splitOn: "ParentId"
);
}
Dapper 的局限性在于它无法将数据映射到 私有复杂 对象。这就是为什么我必须拥有 2 套模型的主要原因。我想用私有设置器和其他技术将数据和逻辑封装在域模型中。
这是我的领域模型:
public class Parent
{
public int Id { get; private set; }
public int TypeId { get; private set; }
public Parent(int parentId, int typeId)
{
// Validations
this.Id = parentId;
this.TypeId = typeId;
}
}
public class Child
{
public int Id { get; private set; }
public Parent Parent { get; private set; }
public Attempt Attempt { get; private set; }
public Child(int childId, Parent parent, Attempt attempt)
{
// Validations
this.Id = childId;
this.Parent = parent;
this.Attempt = attempt;
}
}
对于域模型,我不想要 public setter 和无参数构造函数。
Attempt
是具有整数支持值的枚举:
public enum Attempt
{
Original = 1,
FirstRetry = 2,
SecondRetry = 3,
LastRetry = 4
}
最后,我想使用AutoMapper 在Dbos 和领域模型之间进行映射。这是映射:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Child, ChildDbo>()
.ForMember(dest => dest.ChildId, opts => opts.MapFrom(src => src.Id))
.ForMember(dest => dest.RetryNumber, opts => opts.MapFrom(src => (int)src.Attempt))
.ReverseMap();
CreateMap<Parent, ParentDbo>()
.ForMember(dest => dest.ParentId, opts => opts.MapFrom(src => src.Id))
.ReverseMap();
}
}
我想要双向映射,所以我使用 ReverseMap()
。
.Net Fiddle 演示: https://dotnetfiddle.net/saEHWd
它毫无问题地将域模型映射到 dbos:
但从 dbos 到域模型的反向映射会抛出异常:
Unhandled exception. System.ArgumentException: Program+Child needs to have a constructor with 0 args or only optional args. (Parameter 'type') at lambda_method18(Closure , Object , Child , ResolutionContext ) at AutoMapper.Mapper.MapCore[TSource,TDestination](TSource source, TDestination destination, ResolutionContext context, Type sourceType, Type destinationType, IMemberMap memberMap) at AutoMapper.Mapper.Map[TSource,TDestination](TSource source, TDestination destination) at AutoMapper.Mapper.Map[TDestination](Object source) at Program.Main()
我已经尝试删除枚举 属性 并且一切正常所以我很确定是枚举映射有问题。
据我在您的 fiddle 中看到的,您正在尝试从 ChildDbo
映射到 Parent
,但没有针对它的映射设置。将映射代码更改为:
var child2 = mapper.Map<Child>(childDbo);
并且由于第三个 Child
的 ctor 参数和源 属性 名称不匹配,将映射更改为:
CreateMap<Child, ChildDbo>()
.ForMember(dest => dest.ChildId, opts => opts.MapFrom(src => src.Id))
.ForMember(dest => dest.RetryNumber, opts => opts.MapFrom(src => (int)src.Attempt))
.ReverseMap()
.ConstructUsing((dbo, ctx) => new Child(dbo.ChildId, ctx.Mapper.Map<Parent>(dbo.Parent), (Attempt)dbo.RetryNumber));
见here
或者将第三个Child
的ctor参数重命名为retryNumber
:
public Child(int childId, Parent parent, Attempt retryNumber)
参见 here。
或使用ForCtorParam
:
CreateMap<Child, ChildDbo>()
.ForMember(dest => dest.ChildId, opts => opts.MapFrom(src => src.Id))
.ForMember(dest => dest.RetryNumber, opts => opts.MapFrom(src => (int)src.Attempt))
.ReverseMap()
.ForCtorParam("attempt", opt => opt.MapFrom(dbo => dbo.RetryNumber))
Here.