背景:
我正在使用ASP.NET MVC编写一个社交网络风格的web应用程序。我的项目如下:
在此之前,该项目使用SQL和Dbcontext来补充数据层中定义的POCO类创建的BOs。然而,由于项目的性质(随着项目的增长),需求已经超过了用于存储数据的基于SQL的体系结构。我决定改用Redis,但现在很难在Redis和POCO类之间进行映射。
问题的根:
我选择使用Service Stack的Redis客户端与Redis进行交互。客户端提供了一个强类型客户端,允许您指定从服务器存储/检索的对象,并为您序列化/反序列化它(这很棒)。不过,这方面的问题是具有公共getter的任何属性都将与存储的对象一起序列化。。
对我来说,这违背了使用Redis的意义,因为我不希望以聚合的方式存储子对象--我希望以关系的方式存储它们,这样每个对象都是独立的,但与其他一些对象相关。
为此,我创建了两组对象:
域对象(BOs)
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
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; }
}碱基
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中检索的对象的键。不过,我现在的数据层中有两组方法:
我希望映射这两组操作,以便在存储库中的DO操作在DTO上执行必要的操作,然后在两者之间传输数据。
本质上,我希望存储库对DTO几乎一无所知。理想情况下,这将是存储/检索操作的流程。
检索从Redis ->映射到(成员BO) ->返回存储库
存储存储(成员BO)从app ->映射到(成员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);
}
....
},这是一个糟糕的解决办法,。我不喜欢这件事,但我想不出别的办法。我需要的是一个新的设计思想来处理映射交互--或者整个事情。是否有任何映射库可以用于方法或类之间的映射,就像我试图做的那样?我怎样才能解决这个问题?
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);
}
}使用Psuedo代码,我想要发生的事情,
//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根据我在配置中设置的更多规则来决定如何获取和填充返回对象.
发布于 2013-11-05 14:00:30
在我看来,代码太复杂了。如果您是第一次使用Redis构建模型层,您将如何构造模型层?
我会放弃DTO和AutoMapper (您将其用作Redis的"ORM“),并将我的类建模为Redis数据结构。
例如,我的参与者应该是这样的:
public class Participant : Base
{
public string Name { get; set; }
public string City { get; set; }
}在这里,我在Redis中使用的参与者的密钥应该是“urn:参与者:1”。在我的回购中我会:
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,从而实现简单性,并且仍然可以获得正确的域。
希望我能帮上忙
https://stackoverflow.com/questions/19660640
复制相似问题