如何处理同一C#类的编码和解码版本

场景

我正在更新我的.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的更改。这创建了很多重复的映射代码,我仍然在两个类中都停留在重复的属性上,而没有明确指出要解码/编码的字段,这一切都比需要的麻烦得多。

谢谢您的建议!

xzz00 回答:如何处理同一C#类的编码和解码版本

这是不能真正直接回答您问题的答案之一,但是是解决当前问题的另一种方法。根据我上面的评论。

我不会尝试进行“硬编码”转换,也不会使别名成为对象生命周期的某些内在部分。这里的想法是标识符的转换应该是显而易见的,显式的和可插入的。

让我们从一个界面开始:

public interface IObscuredIDProvider
{
    public string GetObscuredID(int id);
    public void SetObscuredID(int id,string obscuredID);
}

然后,对于我们的测试,一个非常简单的映射器,仅将int作为字符串返回。您的生产版本可以由hashids.org项目或任何您喜欢的项目支持:

public class NonObscuredIDProvider : IObscuredIDProvider
{
    public string GetObscuredID(int id)
    {
        return id.ToString();
    }

    public void SetObscuredID(int id,string obscuredID)
    {
        // noop
    }
}

您需要将IObscuredIDProvider实例注入到将“外部/不受信任”数据转换为“受信任/域”数据的任何层中。在这里,您可以将实体ID从模糊的版本分配给内部版本,反之亦然。

这有意义吗?希望这比在复杂的嵌套转换中烘焙更容易理解和实现解决方案。...

本文链接:https://www.f2er.com/2836212.html

大家都在问