Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# DTO和域对象之间的映射,如何使流程对存储库透明?_C#_Redis_Mapping_<img Src="//i.stack.imgur.com/WM7S8.png" Height="16" Width="18" Alt="" Class="sponsor Tag Img">servicestack_Data Access Layer - Fatal编程技术网 servicestack,data-access-layer,C#,Redis,Mapping,servicestack,Data Access Layer" /> servicestack,data-access-layer,C#,Redis,Mapping,servicestack,Data Access Layer" />

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

C# DTO和域对象之间的映射,如何使流程对存储库透明?,c#,redis,mapping,servicestack,data-access-layer,C#,Redis,Mapping,servicestack,Data Access Layer,背景: 我正在使用ASP.NETMVC编写一个社交网络风格的web应用程序。我的项目布局如下: 表示层-视图和前端框架。数据存储在从BOs映射的Viewmodels中 业务层—用于表示层的BO操作和聚合,以及来自数据层的BO水合作用 数据层-存储库和从数据库检索数据的代码都位于这里。这里定义了POCO 在此之前,该项目使用SQL和Dbcontext从数据层中定义的POCO类创建BOs。然而,由于项目的性质(随着项目的发展),存储数据的需求已经超过了基于SQL的体系结构。我决定切换到,但现在很难在

背景:

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

  • 表示层-视图和前端框架。数据存储在从BOs映射的Viewmodels中
  • 业务层—用于表示层的BO操作和聚合,以及来自数据层的BO水合作用
  • 数据层-存储库和从数据库检索数据的代码都位于这里。这里定义了POCO
  • 在此之前,该项目使用SQL和Dbcontext从数据层中定义的POCO类创建BOs。然而,由于项目的性质(随着项目的发展),存储数据的需求已经超过了基于SQL的体系结构。我决定切换到,但现在很难在Redis和我的POCO类之间进行映射

    问题的根源

    我选择使用它与Redis db进行交互。客户端提供了一个强类型客户端,它允许您指定从服务器存储/检索的对象,并为您序列化/反序列化它(这很好)。但问题是

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

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

    域对象(BO)

    DTO

    public class Participant : Base
    {
        public string ForwardConnections { get; set; }
        public string ReverseConnections { get; set; }
        public string Name { get; set; }
        public string City { get; set; }
    }
    
    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; }
     }
    
    使用这种设计,我实际上将DTO存储在Redis DB中。域对象上作为对象的任何属性都将作为字符串键存储在其各自的DTO上。每个对象(DTO和DO)都从一个
    Base
    类继承,该类具有
    Key
    属性。通过这种方式,我可以存储对象属性之间的关系,而无需聚合和复制每个对象

    我遇到麻烦的地方

    为了在DO和DTO之间移动,我使用了AutoMapper和一套自定义
    ITypeConverter
    类,这些类在
    string
    和在相应DO属性上具有相同名称的任何类之间映射(反之亦然,类到字符串),其中
    string
    是对象在Redis DB中检索的键。这应该可以很好地工作,但是我现在在数据层中有两组方法:

  • 获取驻留在我的存储库中的域对象的基本操作——这些是我希望业务层与之交互的方法
  • 获取驻留在单独类中且仅用于通过客户端访问Redis db的DTO的基本操作
  • 我希望映射这两组操作,以便在存储库中对DO进行操作时,在两者之间传输数据后,对DTO执行必要的操作

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

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

    商店商店(会员BO)从应用程序->映射到(会员DTO)->商店到Redis

    然而,我还没有找到一个合适的方法来设计这个映射过程,我现在不知道该怎么办。

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

    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);
            }
     ....
     }
    
    公共列表GetSetByKey(字符串键),其中T:Base
    {
    Type a=RepositoryMapperHelper.MapClass(typeof(T));//将类类型与相应的DTO类匹配
    var obj=
    typeof(RepositoryMapper).GetMethod(“GetSetByKey”)//RepositoryMapper类包含使用RedisClient从Redis DB检索DTO的操作
    .MakeGenericMethod(a)
    .Invoke(RepoMapper,新对象[]{key});
    return Mapper.DynamicMap(obj);//在运行时确定类型,并使用自定义的ITypeConverter(与RepositoryMapper一起)创建属性
    }
    公共静态类RepositoryMapperHelper
    {
    公共静态类型MapClass(类型t)
    {
    if(t==类型(连接))
    {
    返回类型(RedisModel.Connection);
    }
    ....
    }
    
    这是一个糟糕的解决方案。我不喜欢它的任何方面,但我想不出另一种方法来做。我需要的是一个新的设计思想来处理映射交互——或者说整个事情。是否有任何映射库可以像我尝试的那样用于方法或类之间的映射?如何我能解决这个问题吗

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

    编辑

    以下是当前的读取操作:

    //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);
       }
    }
    
    //向存储库请求对象
    ParticipantRepository回购=新的ParticipantRepository();
    回购GetById(theId);
    //我的BaseRepository拥有所有通用方法。
    //有一个BaseRepository类继承自BaseRepository,它允许我调用方法而不需要指定T,因为它是在实例化存储库时指定的。
    //在BaseRepository中
    公共虚拟T GetById(长id),其中T:Base
    {
    Type a=RepositoryMapperHelper.MapClass(typeof(T));
    var obj=
    typeof(RepositoryMapper).GetMethod(“GetById”)
    .MakeGenericMethod(a)
    .Invoke(RepoMapper,new object[]{id});//使用MapClass返回的相应DataModel.Type构建泛型方法
    返回Mapper.DynamicMap(obj);///从源(数据模型)动态映射到目标类型(DomainModel T)
    }
    //存储映射器
    公共虚拟T GetById(长id),其中T:DataModel.Base
    {
    使用(var)
    
    //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);
       }
    }
    
    //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);
       }
    }
    
    public class Participant : Base
    {        
        public string Name { get; set; }
        public string City { get; set; }
    }
    
    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 
    }