Object / Object mapping
Fairly often, we need to do some sort of a transformation between one object to another. It is usually across layers, such as when we want to turn an entity to a DTO (for sending on the wire of for UI purposes).
Here is the overall pattern to solve such a task. I did try to make it into a framework, but even I can't make it more complex than this.
public class OrderToOrderDTO { public delegate void Transformer(Order o, OrderDTO d); List<Transformer> transforms = new List<Transformer>() { (y, z) => z.ID = y.Id, (x,z) => z.CustomerName = x.Name }; public OrderDTO Transform(Order k) { OrderDTO t = new OrderDTO(); transforms.ForEach((item) => item(k, t)); return t; } }
Comments
Hehe believe me, it can get plenty more complicated :-)
NPersist supports O/O Mapping, it's pretty much as complex as O/R Mapping.
/Mats
Mats,
I can guess. But please explain why.
For one-way transfer, what about using anonymous types with Linq?
The whole DTO thing is a real pain in general. Going from the Domain to DTOs and then serializing those to JavaScript DTOs and then trying to bring the data back again turned out to be a hell of a lot more pain than I expected.
I was half expecting you to have some whiz-bang solution to this when I read the title. I suppose you are human after all...
Will, I don't see this as painful.
Anonymous types are only useful within a method, not outside of it. Not really helpful in this regard.
ConvertAll is what you want. There is already an established pattern for this (Map). http://diditwith.net/2007/06/21/AHigherCalling.aspx
OK, I have to ask.
Why do you need this list of delegates? Wouldn't a simple CreateFrom method do the transformation more clearly? Maybe you're trying to achieve something other than what I thought.
Jeff,
I am aware of Map. But I think that this approach looks better.
Sergio,
CreateFrom will work, but I may want to do things like:
OrderToOrderDTO, OrderListToOrderDTO, etc.
It is the continuation of a thread in the alt.net mailing list.
You can use anonymous types outside a method.
http://blogs.msdn.com/alexj/archive/2007/11/22/t-castbyexample-t-object-o-t-example.aspx
@Ayende,
Well, you may still want things like lazy loading, inverse management, dirty tracking, original value tracking for optimistic concurrency, etc etc....basically all the stuff I refer to as Domain Model Management (DMM) - perhaps not in a straight DTO case, but in many other O/O scenarios (such as mapping a presentation model to your domain model).
The actual transformation part, however, is obviously straightforward :-) But then I often argue that the Object/Relational transformation - mapping classes and properties to tables and columns - is also fairly straightforward and is often in fact a rather small part of what an O/R Mapper does (at least in cases where the mapper will also do a lot of DMM stuff such as lazy loading etc).
So you do have a point - a pure O/O Mapper would be (almost) that simple...but then, so would (almost) a pure O/R Mapper...well a little more complicated perhaps, but not really that much. However as soon as you start adding DMM to the mix the complexity blows up. And there is just as much reason to support DMM features such as lazy loading and dirty tracking in an O/O Mapper as in an O/R Mapper. Both will, assumably, be dealing with at least one domain model.
/Mats
Ok then, I'm stumped. Why on earth would I do this rather than:
public class OrderToDtoConverter{
public OrderDto Convert(Order o){
}
}
Jeff,
I never said it was a good thing. Just that it is something that works.
You can make the arguments that now you can add stuff to the conversion dynamically, but that isn't the reason, I just wanted to see it done.
I do not get why this is better then object initializers.
public OrderDTO Transform(Order k)
{
}
is way easier in my opinion.
Andrey,
Like I said, this approach lets you add stuff at runtime.
However, the truth is that I never considered the initializer.
I usually need O/O mapping on the client side, e.g. in the presentation model. However, besides simple 1:1 mapping, i often need to flatten the objects, do some calculations, convert IList/ICollection to array and vice versa, and so on.
I solved this by creating a small mapper library (http://otis-lib.googlecode.com) which supports mapping via xml mapping definitions or attributes on type members.
I am still not convinced that it was worth the effort for a relatively simple problem, but thruth be told, it was fun and a nice chance for me to start playing with CodeDOM and some other stuff.
It's a side of LINQ that only few people talk about.
I talked about this on my blog some weeks ago.
Here is the link: http://blogema.wordpress.com/2007/11/13/dto-and-linq/
bye
At least, I would generalize it a bit:
public delegate void Transformer<TFrom, TTo>(TFrom from, TTo to);
List<Transformer<Order, OrderDTO>> transforms = new List<Transformer<Order, OrderDTO>>()
{
(from, to) => to.ID = from.Id,
(from, to) => to.CustomerName = from.Name
};
public TTo Transform<TFrom, TTo>(TFrom from)
where TTo : new()
{
TTo to = new TTo();
transforms.ForEach((item) => item(from, to));
return to;
}
And i would say this is one of those demo cases for extension methods:
so you can say:
Order order = new Order() { Id = 5, Name = "John St. Clair" };
OrderDTO dto = order.ToDTO();
That is why I use Dictionaries as data transfer objects. You don't have to change anything when properties change. Furthermore, I transfer object for all domain objects.
public class TransferObject
{
}
Them they can be easily transformed
public T Transform<T>(TransferObject from) where T : new()
{
}
I think you can combine the approaches -- I will not try to build it, but basically it would be something like:
List<Transformer> transforms = SplitTransforms(order => new OrderDTO {
ID = order.Id,
CustomerName = order.Name
});
and
List<Transformer> SplitTransforms(Expresssion<Func<Order, OrderDto>> transformExpression) { ... }
Ayende,
You appear to be duplicating the work of MulticastDelegate. I'd just create a single delegate containing multiple actions, and then call that once. It will run through all the actions appropriately.
The one downside is that to initialize the delegate instance as an expression, you need to cast the first one to Transformer before combining it with the second:
Transformer t = (Transformer) ((y, z) => z.ID = y.Id) +
Jon
I'm currently using metadata attributes on the DTO classes to allow a generic engine to map/transform (from entities and to entities). Is this considered bad practice?
[]'s
If you only have 'stupid' DTOs without much behavior, I would say that it's OK. However, if you are doing mapping to a type which implements some non-trivial behavior, I would try to avoid combining the transformation logic into the type itself, to keep the responsibilities separated.
Additional downside is that mappings defined by attributes are probably not type safe (i don't know how it is implemented in your case, but i presume that string expressions are used to define mapping). The better solution would be to use delegates but this can look quite ugly when used as attribute parameter, especially in more complicated mappings, which defeats the purpose of such mapping, in a way.
I'm seeing more value specifically in message/relational mapping than a more generic object/object mapping. A message being an implementation of the DTO pattern.
I posted something on this topic a while ago [http://udidahan.weblogs.us/2007/04/15/lazy-loading-and-how-messaging-fixes-everything-again/]
The main idea is to decrease memory utilization (consider what happens when a customer has a million orders) or any of those "enterprise" issues. The last thing you want is to first load a bunch of domain objects in to memory, and then duplicate that same amount of memory in a new set of structures.
I've found this to be particularly useful for GetXXX methods on the service layer - where the Domain Model provides little value anyway.
The best part is that this is already supported by all the O/RM tools.
Comment preview