Note that there are some explanatory texts on larger screens.

plurals
  1. POMapping between DTO and domain objects, how can I make the process transparent to my repository?
    primarykey
    data
    text
    <p><strong>Background:</strong> </p> <p>I am writing a social network-esque web application using ASP.NET MVC. My project is layed out as follows:</p> <ol> <li><em>Presentation layer</em> - Views and front-end framework. Data is housed in Viewmodels mapped from BOs.</li> <li><em>Business Layer</em> - BO manipulation and aggregation for use on presentation layer as well as hydration of BOs from Data Layer.</li> <li><em>Data Layer</em> - Repositories live here as well as code for retrieving data from db. POCOs are defined here.</li> </ol> <p>Previously the project was using SQL and Dbcontext to hydrate BOs created from POCO classes defined in the Data Layer. However due to the nature of the project(as it has grown) requirements have outgrown the SQL based architecture for storing data. I made the decision to switch to <a href="http://redis.io/" rel="nofollow noreferrer">Redis</a> but am now having difficulty mapping between Redis and my POCO classes.</p> <p><strong>Root of the problem</strong>: </p> <p>I have elected to use <a href="https://github.com/ServiceStack/ServiceStack.Redis" rel="nofollow noreferrer">Service Stack's Redis Client</a> for interacting with the Redis db. The client provides a <strong>Strongly-Typed Client</strong> that allows you to specify the object being store/retrieved from the server and serializes/deserializes it for you(which is wonderful). The problem with this though is that <a href="https://stackoverflow.com/a/8919931/1469797"><strong>any property that has a public getter will be serialized along with the object being stored</strong></a>. </p> <p>For me this defeats the point of using Redis as I do not want child objects to be stored in an aggregate manner -- I want them to be stored relationally so that each object is independent but related to some other object.</p> <p>To do this I created two sets of objects:</p> <p><strong>Domain Objects(BOs)</strong></p> <pre><code>public class Participant : Base { public string ForwardConnections { get; set; } public string ReverseConnections { get; set; } public string Name { get; set; } public string City { get; set; } } </code></pre> <p>and <strong>DTOs</strong></p> <pre><code>public class Participant : Base { public AceOfSets&lt;Connection&gt; ForwardConnections { get; set; } public AceOfSets&lt;Connection&gt; ReverseConnections { get; set; } public string Name { get; set; } public string City { get; set; } } </code></pre> <p><strong>Base</strong></p> <pre><code>public class Base { public long Id { get; set; } public DateTimeOffset CreatedOn { get; set; } public string Key { get; set; } } </code></pre> <p>Using this design I actually store DTOs in the Redis DB. Any property on a Domain Object that is an object is stored on its respective DTO as a string <strong>key</strong>. Every object(both DTO and DO) inherit from a <code>Base</code> class which has a <code>Key</code> property. This way I can store the relationships between object properties without aggregating and duplicating each object.</p> <p><strong>Where I'm running into trouble</strong>: </p> <p>To move between DO and DTO I am using <strong>AutoMapper</strong> and a suite of custom <code>ITypeConverter</code> classes that map between <code>string</code> and whatever class has the same name on the respective DO property(and vice versa, class to string) where <code>string</code> is the key for the object to retrieve in the Redis DB. This should work well however I now have two sets of methods in my Data Layer:</p> <ol> <li>Basic operations for getting <strong>domain objects</strong> which reside in my repository -- these are the methods I want my Business Layer to interact with.</li> <li>Basic operations for getting <strong>DTOs</strong> which reside in a separate class and are only used to access the Redis db through the client.</li> </ol> <p>I want these two sets of operations to be mapped so that operating on a DO from the repository performs the necessary operation on the DTO after transferring data between the two.</p> <p>In essence I want repositories to be almost ignorant of DTOs. Ideally this would be the flow for store/retrieval operations.</p> <p><strong>Retrieval</strong> Get(Member DTO) from Redis -> Map to (Member BO) -> return to repository</p> <p><strong>Store</strong> Store(Member BO) from app -> Map to (Member DTO) -> store to Redis</p> <p><strong>However I have not found a decent way to design this mapping process and am at a loss as what to do now.</strong></p> <p>What I have done in the interim is use reflection with generics in the basic operation methods in the repository to match up the two sets of classes like this.</p> <pre><code>public List&lt;T&gt; GetSetByKey&lt;T&gt;(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&lt;List&lt;T&gt;&gt;(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); } .... } </code></pre> <p><strong>This is a terrible workaround</strong>. I don't like anything about it but I cannot think of another way to do it. What I need is a new design idea for handling the mapping interaction -- or the whole thing in general. Are there any mapping libraries that could be useful for mapping between methods or classes like I'm trying to do? How can I fix this problem?</p> <p><strong>TLDR</strong> How can I map between Domain objects and DTOs in a way that is transparent to my Data Layer?</p> <p><strong>EDIT</strong>: </p> <p>Here is the read operation as it currently stands:</p> <pre><code>//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&lt;T&gt; 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&lt;T&gt;(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&lt;T&gt;(obj); ///Dynamically maps from source(DataModel) to destination type(DomainModel T) } //In RepositoryMapper public virtual T GetById&lt;T&gt;(long id) where T : DataModel.Base { using (var o = RedisClient.As&lt;T&gt;()) { return o.GetById(id); } } //In AutoMapper Configuration protected override void Configure() { //An example of how Automapper deals with conversion from key -&gt; object Mapper.CreateMap&lt;string, Participant&gt;().ConvertUsing&lt;KeyToBaseConverter&lt;Participant, DataModel.Participant&gt;&gt;(); } //The conversion public class KeyToBaseConverter&lt;T, U&gt; : ITypeConverter&lt;string, T&gt; 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&lt;U&gt;(context.SourceValue.ToString()); return Mapper.DynamicMap&lt;U, T&gt;(datamodel); } } </code></pre> <p><strong>Using Psuedo-code what i'd like to have happen</strong></p> <pre><code>//Read Operation //in domain repository public T GetByKey&lt;T&gt;(string key) where T : Base { U type = DataModelMapper.Map(T); return DataModelRepo.GetByKey&lt;U&gt;(string key); } //in DTO repository(facing Redis) public GetByKey&lt;U&gt;(string key) where U : DataModel.Base { using(var client = RedisClient.As&lt;U&gt;()) { var obj = client.GetByKey(key); T type = DataModelMapper.ReverseMap(U); return Mapper.Map&lt;T&gt;(obj); } } //Write Operation //in domain repository public void Save&lt;T&gt;(T Entity) where T : Base { U type = DataModelMapper.Map(T); DataModelRepo.Save&lt;U&gt;(Entity); } //in DTO repository(facing Redis) public void Save&lt;U&gt;(T Entity) where U : DataModel.Base where T : Base { var obj = Mapper.Map&lt;U&gt;(Entity); using(var client = RedisClient.As&lt;U&gt;()) { client.Store(obj); } } </code></pre> <p>So it's very similar to what I'm already doing, the barrier im running up against is translating between the two types of models and passing the generic type parameter to <code>RepositoryMapper</code> and back again.</p> <p>Since I wrote this question I've thought about investing more into my AutoMapper implementation, I may be able to use it as the sole go-between for the two repositories -- so basically called <code>Map</code> between the two generics and then lets AutoMapper decide how to get and populate the return object based on some more rules I'd put in place in configuration...</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload