场景
我正在更新我的.NET API,以对所有数据库密钥字段进行编码,以使顺序密钥不会暴露给最终用户。我为此使用hashids.net并建立了辅助方法来快速解码/编码自动映射器映射中的属性。但是,API有多个版本,只有最新版本的API才能使用此功能进行更新,这意味着我不能简单地覆盖现有的类。我已经实现了一些可行的解决方案,但是它们都带有一种不好的代码味道,我希望清除它们。
解决方案
我目前正在控制器层执行编码。我也可以在数据访问层看到这样做的好处,但是觉得在该层存在更大的泄漏/丢失转换风险,特别是因为API有许多不同的数据源。另外,隐藏密钥是外界的问题,因为控制器是网守,所以在那里感觉很合适。
应用程序当前具有以下模型模式,无法更改:模型(数据库中存在的模型)> ValueObject(服务模型,VO)> DTO(API模型)。
(1)初始尝试
下面是一个需要支持编码和解码状态的类的示例,其中Utils.Encode()
和Utils.Decode()
是辅助方法,将使用Hashids在int和字符串之间转换字段。
//EquipmentDTO.cs
public class EquipmentDTO //encoded class
{
public string Id {get; set;}
public string Name {get; set;}
}
public class EquipmentUnencodedDTO //decoded class
{
public int Id {get; set;}
public string Name {get; set;}
}
//Automapper.cs
CreateMap<EquipmentUnencodedDTO,EquipmentDTO>()
.ForMember(dst => dst.Id,opt => opt.MapFrom(src => Utils.Encode(src.Id)));
CreateMap<EquipmentDTO,EquipmentUnencodedDTO>()
.ForMember(dst => dst.Id,opt => opt.MapFrom(src => Utils.Decode(src.Id)));
CreateMap<EquipmentVO,EquipmentDTO>() //mapping from service model to controller model
.ForMember(dst => dst.Id,opt => opt.MapFrom(src => Utils.Encode(src.Id)));
CreateMap<EquipmentDTO,EquipmentVO>()
.ForMember(dst => dst.Id,opt => opt.MapFrom(src => Utils.Decode(src.Id)));
CreateMap<Equipment,EquipmentVO>() //mapping from DB model to service model
.ForMember(dst => dst.Id,opt => opt.MapFrom(src => src.Id));
- 我选择制作现有的
EquipmentDTO
编码版本 因为我希望这成为新的标准 最终导致弃用和移除EquipmentUnencodedDTO
,因为原来的控制者最终获得 已更新。 - 我选择不为
CreateMap<EquipmentVO,EquipmentDTO>
复制CreateMap<EquipmentVO,EquipmentUnencodedDTO>
(反之亦然),因为 这会导致AutoMapper文件中出现很多重复, 已经很大了(尽管也许这不是一个真正的问题?) - 我不喜欢这种解决方案,因为在我的旧控制器中,映射现在令人困惑。例如,在POST中,未经编码的输入DTO必须通过以下方式转换为服务模型:
Mapper.Map<EquipmentVO>(Mapper.Map<EquipmentDTO>(unencodedEquipmentInput))
。- 话虽这么说,这应该是暂时的问题,所以这是一个真正的问题吗?
- 如果我创建了
CreateMap<EquipmentVO,EquipmentUnencodedDTO>
,此问题将消失
- 我不喜欢这种解决方案,因为我的班级有很多重复的字段,在编码版本和解码版本之间没有变化
(2)第二次尝试
以上两个要点使我重构:
public class EquipmentDTO
{
public string Id {get; set;}
public string Name {get; set;}
public Decoded Decode(){
return Mapper.Map<Decoded>(this);
}
public class Decoded: EquipmentDTO {
public new int Id {get; set;}
public EquipmentDTO Encode(){
return Mapper.Map<EquipmentDTO>(this);
}
}
}
// Automappers are the same,except EquipmentUnencodedDTO is now EquipmentDTO.Decoded
- 我喜欢现在在编码状态和解码状态之间切换非常简单,将上面的双重映射简化为:
Mapper.Map<EquipmentVO>(unencodedEquipmentInput.Encode());
- 我喜欢嵌套类,因为它可以将两个类之间的关系编成代码,并且在识别哪些字段进行编码/解码方面做得更好
- 我认为这闻起来更糟
(3)下次尝试
我的下一个尝试是将已解码类的缺失映射添加到服务模型,并撤消尝试2的更改。这创建了很多重复的映射代码,我仍然在两个类中都停留在重复的属性上,而没有明确指出要解码/编码的字段,这一切都比需要的麻烦得多。
谢谢您的建议!