Again, please forgive any code mistakes. I've written this post up largely without the help of a compiler to help catch the bugs.
The initial solution I came up with was some sort of case statement but after a few hours of research I came to the realization that C# simply doesn't support a case statements on types. A dictionary object was my next port of call. More specifically a Dictionary of Dictionaries. The nice thing about dictionaries in C# is that you can access them like arrays, using a key in place of the index. So a Dictionary of Dictionaries can be used the same way as a 2 dimensional array. In our case we want to use types as our keys so our dictionary syntax becomes:
dict[typeof(Source)][typeof(Dest)];
Linq syntax uses Func<TSource, TDest> for L2SQL and wraps it into a much less restricted Expression<...> for L2OBJs. It is really easy to pull the Func out of the Expression so our actual Projections are going be of type Expression<Func<TSource, TDest>>
Our key will be the destination type. Given that, our inner dictionary will need to be a dictionary of type:
IDictionary<TDest, Expression<Func<TSource, TDest>>>;
Where TDest is our Key and our Expression is our Value. If that looks ugly, it's because it is.
Our outer dictionary will need to hold our inner dictionary and be accessed by our source type, so it's type will be:
IDictionary<TSource, IDictionary<TDest, Expression<Func<TSource, TDest>>>>;
That is *really* ugly. Not to mention that our code isn't generic at all, and our dictionary object is strongly typed so it won't just accept actual types. It needs the more generic Type type. And for convenience sake, MS have included a non generic Expression class (which is as it turns out more generic.)
IDictionary<Type, IDictionary<Type, Expression>>;
That's about as nice as we're going to get in this case I think. There's not a whole lot we can do to clean that up. So what's the next best thing to getting rid of the dust in the house? Sweeping it under the rug! So what we want to do is abstract the nastiness away from the programmer.
Currently we can only set up our projection dictionary by doing something like the following:
IDictionary<Type, IDictionary<Type, Expression>> projections = new Dictionary<Type, Dictionary<Type, Expression>>() {
{ typeof(Source1), new Dictionary<Type, Expression>() {
{ typeof(Dest1), (Expression)(() => {
return new Dest1() {
...
}
}) }
, { typeof(Dest2), (Expression)(() => {
return new Dest2() {
...
}
}) }
} }
, { typeof(Source2), new Dictionary<Type, Expression>() {
{ typeof(Dest3), (Expression)(() => {
return new Dest3() {
...
}
}) }
, { typeof(Dest4), (Expression)(() => {
return new Dest4() {
...
}
}) }
} }
};
Which is pretty brutal, especially since in MVC.NET you're going to be working with probably at least 10+ domain models and twice as many view models. The whole point of this exercise is to reduce the amount of code we have to write while still having flexibility. So, lets get to refactoring.
The obvious starting point is to wrap the previous code into an Add function. To use the Source and Dest types we'll have to make it a generic function, which is fine:
void Add<TSource, TDest>(IDictionary<Type, IDictionary<Type, Expression>> Projections, Expression Expression) {
Type source = typeof(TSource);
Type dest = typeof(TDest);
if(true == Projections.ContainsKey(source)) {
Projections[source].Add(Expression);
} else {
IDictionary<Type, Expression>> d = new Dictionary<Type, Expression>>();
d.Add(dest, Expression);
Projections.Add(source, d);
}
}
IDictionary<Type, IDictionary<Type, Expression>> projections =
new Dictionary<Type, IDictionary<Type, Expression>>
;
Expression e = (Expression)(() => {
return new TDest() {
...
}
});
Add<Source, Dest>(projections, e);
That's not bad, but we can do better. After all, you don't want to have to type all that mess every time. One easy thing we can do to clean the syntax up is move our Expressions to variables rather than methods.
Expression e = (Expression)(o => new TDest() {
...
}
);
But we're not done yet. Since the generic Expression class derives from the Expression class, we can do away with the casting...
Expression e = o => new TDest() {
...
}
;
The Add function could also be refactored more. Instead of having to write Add<Source, Dest>(d, e) for every single projection, wouldn't it be nicer to simply split it out into two? Add<Source>(d) and Add<Dest>(d, e).
Lets do that.
void Add<TSource>(IDictionary<Type, IDictionary<Type, Expression>> Projections, IDictionary<Type, Expression> Dictionary) {
Projections.Add(typeof(TSource), Dictionary);
}
void Add<TDest>(IDictionary<Type, Expression> Dictionary, Expression Expression) {
Dictionary.Add(typeof(TDest), Expression);
}
One of the nice features of C# is the ability to make Extension methods. Make your method static and slap it in a static class, add the 'this' keyword to the first parameter and the type of object that it is will allow you to use that method as if it was a part of the original class while abstracting away the parameter. Lets do that here:
static class Mapper {
static void Add<TSource>(this IDictionary<Type, IDictionary<Type, Expression>> Projections, IDictionary<Type, Expression> Dictionary) {
Projections(typeof(TSource), Dictionary);
}
static void Add<TDest>(this IDictionary<Type, Expression> Dictionary, Expression Expression) {
Dictionary.Add(typeof(TDest), Expression);
}
}
Our Add methods can now be called like so:
d2.Add<Dest>(e);
d1.Add<Source>(d2);
Almost done with the Add functions. Rather than calling d1.Add(); many times, wouldn't it be nicer to simply chain them?
static IDictionary<Type, IDictionary<Type, Expression>> Add<TSource>(this IDictionary<Type, IDictionary<Type, Expression>> Projections, IDictionary<Type, Expression> Dictionary) {
Projections.Add(typeof(TSource), Dictionary);
return Projections;
}
static void Add<TDest>(this IDictionary<Type, Expression> Dictionary, Expression Expression) {
Dictionary.Add(typeof(TDest), Expression);
return Dictionary;
}
Thus we can call them:
d2
.Add<Dest1>(e1)
.Add<Dest2>(e2)
...
;
d1
.Add<Source1>(d2)
.Add<Source2>(d3)
...
;
Okay, one last update to the Add
function and we'll be close to done. In our haste to refactor, we've committed the cardinal programmer sin. Optimizing at the start. We've made things a little TOO generic. I've always found the generic method syntax (f<t>()) to be a little verbose. The other great thing about extension methods is that if we play our cards right, the 'this' parameter can actually imply the type of the generic, abstracting it away so that it looks like a normal function. Once our projection goes into the dictionary it loses its type and becomes a standard Expression rather than an Expression<> so we can't actually do anything for the outer dictionary Add method, but we can certainly clean up the inner Add.
static Dictionary<Type, Expression> Add<TSource, TDest>(this Dictionary<Type, Expression> Dictionary, Expression<Func<TSource, TDest>> Expression) {
Dictionary.Add(typeof(TDest), Expression);
return Dictionary;
}
Now for one final cleanup method:
static Expression GetProjection(Type Source, Type Dest) {
return Projections[Source][Dest];
}
Altogether our code is looking pretty nice so far:
public static class Mapper {
private static Dictionary<Type, Expression> Add<TDest>(this Dictionary<Type, Expression> Dictionary, Expression Expression) {
Dictionary.Add(typeof(TDest), Expression);
return Dictionary;
}
private static Dictionary<Type, Expression> Add<TSource, TDest>(this Dictionary<Type, Expression> Dictionary, Expression<Func<TSource, TDest>> Expression) {
Dictionary.Add(typeof(TDest), Expression);
return Dictionary;
}
private static Dictionary<Type, Dictionary<Type, Expression>> Add<TSource>(this Dictionary<Type, Dictionary<Type, Expression>> Dictionary, Dictionary<Type, Expression> Projections) {
Dictionary.Add(typeof(TSource), Projections);
return Dictionary;
}
private static Expression GetProjection(Type Source, Type Dest) {
return Projections[Source][Dest];
}
private static Dictionary<Type, Dictionary<Type, Expression>> Projections {
get {
return new Dictionary<Type, Dictionary<Type, Expression>>()
.Add<Source1>(Source1Projections)
.Add<Source2>(Source2Projections)
;
}
}
private static Dictionary<Type, Expression> Source1Projections{
get {
return new Dictionary<Type, Expression>()
.Add(Dest1.Projection)
.Add(Dest2.Projection)
;
}
}
private static Dictionary<Type, Expression> Source2Projections{
get {
return new Dictionary<Type, Expression>()
.Add(Dest3.Projection)
.Add(Dest4.Projection)
;
}
}
}
You can define your actual projections in your view models:
public class Dest1 {
<properties>
<constructors>
<methods>
public static Expression<Func<Source1, Dest1>> Projection {
get {
return o => new Dest1() {
...
};
}
}
}
There's probably more I could do to refactor this. There really isn't much reason the projections need to be properties rather than standard variables for example but I'll leave it there. This post is becoming pretty long winded, so I'll save the rest for Part 3. We're 2/3rds of the way there so stay tuned for the Actual implementing the projection methods that will let you use the mapper.