Transform object to another type in a LINQ query

Transform object to another type in a LINQ query

I had the following problem: let’s say I have a class called “Song”. I want to load it from the database and transform it to a data transfer object class called “SongDto” which includes only a subset of the original Song class’s properties.

class Song {
  public string Name { get; set; }
  public int Length { get; set; }
  public string Description { get; set; }
  public string Artists { get; set; }
}

class SongDto {
  public string Name { get; set; }
  public int Length { get; set; }
}

Given a queryable of Song objects, I could do a standard LINQ projection:

var songs = new[] {
  new Song { Name = "Nebula", Length = 3939, Description = "Cool Mikuelectro", Artists = "Tripshots feat. Miku" },
  new Song { Name = "Rise to Eternity", Length = 39, Description = "Gumimetal", Artists = "A-DASH feat. Gumi" },
}.AsQueryable();

songs.Select(s => new SongDto { Name = s.Name, Length = s.Length });

This works okay, but gets lengthy when there’s 20+ properties that need to be selected. I wanted a generic way of doing this mapping, which would also work with NHibernate when querying the database.

There’s a result transformer for NHibernate called AliasToBean which does exactly what I want, but as far as I could find, result transformers can’t be used with the LINQ provider. I prefer LINQ over NHibernate’s own query syntax, and I knew it’s possible to do this mapping using reflection, so I decided to try it. It needs to be an object initializer in order to be recognized by NHibernate’s LINQ provider. The result is below:

public static class SongQueryableExtenders { 
  public static IQueryable<TResult> SelectObject<TSource, TResult>(this IQueryable<TSource> query) {

    var t1 = typeof(TSource);
    var t2 = typeof(TResult);
    var properties = t2.GetProperties()
      .Where(p => p.CanWrite && t1.GetProperty(p.Name)?.PropertyType == p.PropertyType)
      .ToArray();
    var param = Expression.Parameter(typeof(TSource), "p");
    var memberBindings = properties.Select(targetProperty => 
      Expression.Bind(targetProperty, Expression.Property(param, t1.GetProperty(targetProperty.Name)))); // Prop = p.Prop
    var memberInit = Expression.MemberInit(Expression.New(typeof(TResult)), memberBindings); // new TResult { Prop = p.Prop, ... }

    return query.Select(Expression.Lambda<Func<TSource, TResult>>(memberInit, param));

  }
}

The code has a lot of reflection and dynamic expression generation. Basically, it creates an object initializer expression using Expression.MemberInit, binding all properties from TSource to TResult using Expression.Bind.

Now, instead of doing the object initializer by hand, I can just write:

songs.SelectObject<Song, SongDto>();

and it would automatically map all properties from Song to SongDto, no matter how many properties there are, where the property exists in both classes and has the same type. Note that I haven’t benchmarked the code, but the amount of reflection will probably slow it down a bit, so you might consider caching the mapping.

This was written with NHibernate database queries in mind, but it should be useful elsewhere as well. I haven’t tested, but it should work with Entity Framework as well.

Comments are closed.