Monday, March 14, 2016

Homemade Lightweight Automapper Part 4

I promised in the last blog post that I would demonstrate a legitimate use for a type dictionary. Well, it has been over 2 years at this point since my last post. It's a good thing I haven't given up my day job to become a professional blogger. Anyway. I cannot remember what exactly I had in mind, so while I can confirm that there are legitimate uses, the best demonstration I could give actually has nothing to do with projections at all, so I feel is a little beyond the scope of this post. I will, however, do a post on it... at some point... maybe in 2 years...

So where we left off we were getting pretty close to a decent projection engine, with the slight caveat that we have to build up some atrociously long, dependency ridden projection dictionaries for the whole thing to work. Well I have gone through a few more iterations at this point and those dependencies are now gone, as well as a slight cleanup of usage syntax. The entire thing is really simple, so lets dig into the code:
First of all we need some sort of Projection Profile that we can tuck into our model.

public abstract class Profile<TSource, TDest> {
 public Expression<Func<TSource, TDest>> Projection { get; private set; }

 public void CreateMap(Expression<Func<TSource, TDest>> Projection) {
  this.Projection = Projection;
 }
}
This lets us create a nested class in our model, like so:

public class ProjectionModel {
 ...

 public class Profile : Profile<Model, ProjectionModel> {
  public Profile() {
   CreateMap(o => new ProjectionModel {
    ...
   });
  }
 }
}
Lastly, our revised 3 projection methods for converting IQueryables, IEnumerables and instances of our projection models wrapped into a neat extension class:

public static class ProjectionModelExtension {
 public static TDest Project<TSource, TDest>(this TSource Model, Profile<TSource, TDest> Profile) {
  if (null == Model || null == Profile) {
   return default(TDest);
  }

  return Profile.Projection.Compile().Invoke(Model);
 }

 public static IEnumerable<TDest> Project<TSource, TDest>(this IEnumerable<TSource> List, Profile<TSource, TDest> Profile) {
  if (null == List || null == Profile) {
   return null;
  }

  Func<TSource, TDest> p = Profile.Projection.Compile();

  return List.Select(p);
 }

 public static IQueryable<TDest> Project<TSource, TDest>(this IQueryable<TSource> Query, Profile<TSource, TDest> Profile) {
  if (null == Query || null == Profile) {
   return null;
  }

  Expression<Func<TSource, TDest>> p = Profile.Projection;

  return Query.Select(p);
 }
}
When you put all that together you can use the following syntax:

IEnumerables

IEnumerables<Model> list;
IEnumerables<ProjectionModel> plist = list.Project(new ProjectionModel.Profile());
IQueryables

IQueryable<Model> query;
IQueryable<ProjectionModel> pquery = query.Project(new ProjectionModel.Profile());
Single Models

Model m;
ProjectionModel p = m.Project(new ProjectionModel.Profile());
It's that simple! So enjoy and have fun!

No comments:

Post a Comment