C# DTO和域对象之间的映射,如何使流程对存储库透明?
背景: 我正在使用ASP.NETMVC编写一个社交网络风格的web应用程序。我的项目布局如下: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的体系结构。我决定切换到,但现在很难在
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中检索的键。这应该可以很好地工作,但是我现在在数据层中有两组方法:
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
}