首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >DTO和域对象之间的映射,如何使流程对我的存储库透明?

DTO和域对象之间的映射,如何使流程对我的存储库透明?
EN

Stack Overflow用户
提问于 2013-10-29 14:11:32
回答 1查看 3.9K关注 0票数 4

背景:

我正在使用ASP.NET MVC编写一个社交网络风格的web应用程序。我的项目如下:

  1. 表示层视图和前端框架.数据保存在从BOs映射的视图模型中。
  2. 业务层-用于表示层的BO操作和聚合,以及来自数据层的BOs的水合。
  3. 数据层-存储库以及从db检索数据的代码。在这里定义了POCOs。

在此之前,该项目使用SQL和Dbcontext来补充数据层中定义的POCO类创建的BOs。然而,由于项目的性质(随着项目的增长),需求已经超过了用于存储数据的基于SQL的体系结构。我决定改用Redis,但现在很难在Redis和POCO类之间进行映射。

问题的

我选择使用Service Stack的Redis客户端与Redis进行交互。客户端提供了一个强类型客户端,允许您指定从服务器存储/检索的对象,并为您序列化/反序列化它(这很棒)。不过,这方面的问题是具有公共getter的任何属性都将与存储的对象一起序列化。

对我来说,这违背了使用Redis的意义,因为我不希望以聚合的方式存储子对象--我希望以关系的方式存储它们,这样每个对象都是独立的,但与其他一些对象相关。

为此,我创建了两组对象:

域对象(BOs)

代码语言:javascript
复制
public class Participant : Base
{
    public string ForwardConnections { get; set; }
    public string ReverseConnections { get; set; }
    public string Name { get; set; }
    public string City { get; set; }
}

DTO

代码语言:javascript
复制
public class Participant : Base
{
    public AceOfSets<Connection> ForwardConnections { get; set; }
    public AceOfSets<Connection> ReverseConnections { get; set; }
    public string Name { get; set; }
    public string City { get; set; }
 }

碱基

代码语言:javascript
复制
public class Base
{
    public long Id { get; set; }
    public DateTimeOffset CreatedOn { get; set; }
    public string Key { get; set; }
}

使用此设计,我实际上将DTO存储在Redis DB中。作为对象的域对象上的任何属性都作为字符串存储在其各自的DTO上。每个对象( DTO和DO)都继承具有Base属性的Key类。通过这种方式,我可以存储对象属性之间的关系,而无需聚合和复制每个对象。

,我遇到麻烦了,

为了在DO和DTO之间移动,我使用AutoMapper和一组自定义ITypeConverter类,这些类在string和任何类之间映射,它们在各自的DO属性上具有相同的名称(反之亦然,从类到字符串),其中string是要在Redis DB中检索的对象的键。不过,我现在的数据层中有两组方法:

  1. 获取驻留在我的存储库中的域对象的基本操作--这些是我希望业务层与之交互的方法。
  2. 获取驻留在单独类中的DTO的基本操作,这些操作仅用于通过客户端访问Redis。

我希望映射这两组操作,以便在存储库中的DO操作在DTO上执行必要的操作,然后在两者之间传输数据。

本质上,我希望存储库对DTO几乎一无所知。理想情况下,这将是存储/检索操作的流程。

检索从Redis ->映射到(成员BO) ->返回存储库

存储存储(成员BO)从app ->映射到(成员DTO) ->存储到Redis

然而,并没有找到一种很好的方法来设计这个映射过程,我对现在该做什么感到困惑。

在过渡期间,我所做的是在存储库中的基本操作方法中使用反射和泛型来匹配这两组类。

代码语言:javascript
复制
public List<T> GetSetByKey<T>(string key) where T : Base
{
   Type a = RepositoryMapperHelper.MapClass(typeof(T)); //Matches up class type to respective DTO class
   var obj =
   typeof(RepositoryMapper).GetMethod("GetSetByKey") //RepositoryMapper class contains operations for retreiving DTOs from Redis DB using RedisClient
   .MakeGenericMethod(a)
   .Invoke(RepoMapper, new object[] { key });

    return Mapper.DynamicMap<List<T>>(obj); //Determines types at run-time and uses custom ITypeConverter (in conjunction with RepositoryMapper) to hydrate DO properties
 }
 public static class RepositoryMapperHelper
 {
    public static Type MapClass(Type t)
    {
        if(t == typeof(Connection))
        {
            return typeof (RedisModel.Connection);
        }
 ....
 }

,这是一个糟糕的解决办法,。我不喜欢这件事,但我想不出别的办法。我需要的是一个新的设计思想来处理映射交互--或者整个事情。是否有任何映射库可以用于方法或类之间的映射,就像我试图做的那样?我怎样才能解决这个问题?

TLDR如何以对数据层透明的方式在域对象和DTO之间映射?

编辑:

以下是当前的读取操作:

代码语言:javascript
复制
//Make a request to a repository for an object
ParticipantRepository repo = new ParticipantRepository();
repo.GetById(theId);

//My BaseRepository has all generic methods.
//There is a BaseRepository<T> class that inherits from BaseRepository and allows me to call methods without needing to specify T because it is specified when  you instantiate the repository.

//In BaseRepository
public virtual T GetById<T>(long id) where T : Base
{
    Type a = RepositoryMapperHelper.MapClass(typeof(T));
    var obj =
    typeof(RepositoryMapper).GetMethod("GetById")
    .MakeGenericMethod(a)
    .Invoke(RepoMapper, new object[] { id }); //Builds the Generic Method using the respective DataModel.Type returned from MapClass

     return Mapper.DynamicMap<T>(obj); ///Dynamically maps from source(DataModel) to destination type(DomainModel T)
}

 //In RepositoryMapper
 public virtual T GetById<T>(long id) where T : DataModel.Base
 {
     using (var o = RedisClient.As<T>())
     {
       return o.GetById(id);
     }
 }

 //In AutoMapper Configuration
protected override void Configure()
   {
   //An example of how Automapper deals with conversion from key -> object
   Mapper.CreateMap<string, Participant>().ConvertUsing<KeyToBaseConverter<Participant, DataModel.Participant>>();
   }

//The conversion
public class KeyToBaseConverter<T, U> : ITypeConverter<string, T>
    where T : Base
    where U : DataModel.Base
{
   public RepositoryMapper Repository = new RepositoryMapper();
   public T Convert(ResolutionContext context)
   {
       //Get the actual DTO using the Key or Id
       var datamodel = Repository.GetByKey<U>(context.SourceValue.ToString()); 
       return Mapper.DynamicMap<U, T>(datamodel);
   }
}

使用Psuedo代码,我想要发生的事情,

代码语言:javascript
复制
//Read Operation
//in domain repository
public T GetByKey<T>(string key) where T : Base
{
   U type = DataModelMapper.Map(T);
   return DataModelRepo.GetByKey<U>(string key);
}

//in DTO repository(facing Redis)
public GetByKey<U>(string key) where U : DataModel.Base
{
  using(var client = RedisClient.As<U>())
   {
      var obj = client.GetByKey(key);
      T type = DataModelMapper.ReverseMap(U);
      return Mapper.Map<T>(obj);
   }
}

//Write Operation
//in domain repository
public void Save<T>(T Entity) where T : Base
{
   U type = DataModelMapper.Map(T);
   DataModelRepo.Save<U>(Entity);
}

//in DTO repository(facing Redis)
public void Save<U>(T Entity) where U : DataModel.Base where T : Base
{
  var obj = Mapper.Map<U>(Entity);
  using(var client = RedisClient.As<U>())
   {
      client.Store(obj);
   }
}

因此,这与我已经做的非常相似,我遇到的障碍是在两种类型的模型之间进行转换,并将泛型类型参数传递给RepositoryMapper,然后再返回。

既然我写了这个问题,我已经考虑过对我的AutoMapper实现进行更多的投资,我也许可以把它作为两个库之间的唯一中间部分--基本上被称为两个泛型之间的Map,然后让AutoMapper根据我在配置中设置的更多规则来决定如何获取和填充返回对象.

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-11-05 14:00:30

在我看来,代码太复杂了。如果您是第一次使用Redis构建模型层,您将如何构造模型层?

我会放弃DTO和AutoMapper (您将其用作Redis的"ORM“),并将我的类建模为Redis数据结构。

例如,我的参与者应该是这样的:

代码语言:javascript
复制
public class Participant : Base
{        
    public string Name { get; set; }
    public string City { get; set; }
}

在这里,我在Redis中使用的参与者的密钥应该是“urn:参与者:1”。在我的回购中我会:

代码语言:javascript
复制
public Participant GetById(string id)
{       
   return this.Redis.GetById<Participant>(id);
}

public List<Connection> GetForwardConnections(string participantId)
{       
   return this.Redis.GetTypedClient<Connection>().Lists["urn:participant:1:forwardconnections"].ToList();
}

public List<Connection> GetReverseConnections(string participantId)
{       
   return this.Redis.GetTypedClient<Connection>().Lists["urn:participant:1:reverseconnections"].ToList(); // you could also use sets 
}

通过删除许多抽象、映射、dto,从而实现简单性,并且仍然可以获得正确的域。

希望我能帮上忙

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19660640

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档