I don't know if it can be done with AutoMapper. But this is how you could do it yourself, not including the Repository-call. There's a generic and non-generic version of the mapping-method; I think you should be able to use the generic one.
using System;
using System.Linq.Expressions;
namespace MemberMapping
{
class User
{
public string Name { get; set; }
}
class UserVM
{
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
User testUser = new User() { Name = "Paul" };
Expression<Func<User, object>> genericMapping = MapMemberLambda<UserVM, User>(user => user.Name);
Expression<Func<User, object>> nonGenericMapping = MapUserMemberLambda(user => user.Name);
string testGenericMapping = (string)genericMapping.Compile().Invoke(testUser);
string testNonGenericMapping = (string)nonGenericMapping.Compile().Invoke(testUser);
Console.WriteLine(testGenericMapping);
Console.WriteLine(testNonGenericMapping);
}
static Expression<Func<TTarget, object>> MapMemberLambda<TSource, TTarget>(Expression<Func<TSource, object>> includePropertyVM)
{
ParameterExpression objectParam = Expression.Parameter(typeof(TTarget), "x");
Expression memberAccess = Expression.PropertyOrField(objectParam, ((MemberExpression)includePropertyVM.Body).Member.Name);
return Expression.Lambda<Func<TTarget, object>>(memberAccess, objectParam);
}
static Expression<Func<User, object>> MapUserMemberLambda(Expression<Func<UserVM, object>> lambda)
{
ParameterExpression objectParam = Expression.Parameter(typeof(User), "x");
Expression memberAccess = Expression.PropertyOrField(objectParam, ((MemberExpression)lambda.Body).Member.Name);
return Expression.Lambda<Func<User, object>>(memberAccess, objectParam);
}
}
}
Edit: The mapping methods expect an exact match of the member names. If you counted on AutoMappers ability to "fuzzy-match" member names like "User" to "UserName" you'll have to do this on your own here. You would have to take the source-member-name from
((MemberExpression)lambda.Body).Member.Name
, find the best matching member name of the members of the target type (see
typeof(TTarget).GetMembers()
) and put that member name where
((MemberExpression)lambda.Body).Member.Name
is now.
Edit 2: As requested per comment: Allowing the mapping of nested member access like
user => user.Foo.Bar.Baz
:
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace MemberMapping
{
class Foo
{
public string Bar { get; set; }
}
class Address
{
public string Street { get; set; }
public Foo Foo { get; set; }
}
class User
{
public string Name { get; set; }
public Address Address { get; set; }
}
class FooVM
{
public string Bar { get; set; }
}
class AddressVM
{
public string Street { get; set; }
public FooVM Foo { get; set; }
}
class UserVM
{
public string Name { get; set; }
public AddressVM Address { get; set; }
}
class Program
{
static void Main(string[] args)
{
User testUser = new User()
{
Name = "Paul",
Address = new Address()
{
Street = "Freeway",
Foo = new Foo() { Bar = "Baz" }
}
};
Expression<Func<User, object>> nameMapping = MapMemberLambda<UserVM, User>(user => user.Name);
Expression<Func<User, object>> streetMapping = MapMemberLambda<UserVM, User>(user => user.Address.Street);
Expression<Func<User, object>> barMapping = MapMemberLambda<UserVM, User>(user => user.Address.Foo.Bar);
string nameMappingResult = (string)nameMapping.Compile().Invoke(testUser);
string streetMappingResult = (string)streetMapping.Compile().Invoke(testUser);
string barMappingResult = (string)barMapping.Compile().Invoke(testUser);
Console.WriteLine(nameMappingResult);
Console.WriteLine(streetMappingResult);
Console.WriteLine(barMappingResult);
Console.ReadLine();
}
static Expression<Func<TTarget, object>> MapMemberLambda<TSource, TTarget>(Expression<Func<TSource, object>> sourceLambdaExpr)
{
Stack<string> memberNames = new Stack<string>();
MemberExpression currentMemberExpr = (MemberExpression)sourceLambdaExpr.Body;
while (true)
{
memberNames.Push(currentMemberExpr.Member.Name);
if (currentMemberExpr.Expression is ParameterExpression)
break;
currentMemberExpr = (MemberExpression)currentMemberExpr.Expression;
}
ParameterExpression objectParamExpr = Expression.Parameter(typeof(TTarget), "x");
Expression currentMappedExpr = objectParamExpr;
while (memberNames.Count > 0)
{
currentMappedExpr = Expression.PropertyOrField(currentMappedExpr, memberNames.Pop());
}
return Expression.Lambda<Func<TTarget, object>>(currentMappedExpr, objectParamExpr);
}
}
}