问题描述
我想在架构中传播实体框架包含属性。
1.从服务层开始,其中包含属性作为参数
I would like to propagate entity framework include properties across architecture.
1. Starting at the service layer where these include properties come as an argument
IEnumerable<UserVM> GetUsers(user => user.Classes);
2.方法getUsers必须将此表达式从ViewModel转换为Model
2. The method getUsers must translate this expressions from ViewModel to Model
IEnumerable<UserVM> GetUser(Expression<Func<UserVm, object>> includePropertyVM)
{
Expression<Func<User, object>> includeProperty Mapper.Map<Expression<Func<User, object>>>(includePropertyVM);
Repository.GetUsers(includeProperty);
}
但它不起作用!
Autommaper无法处理它。你有这方面的经验吗?我想将这个lamda表达式互相翻译。
But it does not work!
Autommaper can not handle it. Have you any experience with it? I want to translate this lamda expressions to each other.
推荐答案
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); // writes "Paul"
Console.WriteLine(testNonGenericMapping); // writes "Paul"
}
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);
}
}
}
编辑:映射方法期望成员名称的完全匹配。如果您依靠AutoMappers将用户等成员名称模糊匹配到UserName的能力,那么您必须自己完成此操作。您必须从((MemberExpression)lambda.Body).Member.Name
中获取source-member-name,找到目标成员的最佳匹配成员名称type(参见 typeof(TTarget).GetMembers()
)并将该成员名称放在((MemberExpression)lambda.Body).Member.Name
现在。
编辑2:按照评论的要求:允许嵌套成员访问的映射,如 user => user.Foo.Bar.Baz
:
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); // writes "Paul"
Console.WriteLine(streetMappingResult); // writes "Freeway"
Console.WriteLine(barMappingResult); // writes "Baz"
Console.ReadLine();
}
static Expression<Func<TTarget, object>> MapMemberLambda<TSource, TTarget>(Expression<Func<TSource, object>> sourceLambdaExpr)
{
// We need to build the mapped expression in the reverse order we
// can traverse the source expression. So we use a stack to push
// the names of the members that are being accessed onto it and
// then pop them off the stack again while building the mapped
// expression.
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);
}
}
}
这篇关于映射表达式表达式< Func< UserVM,object>>到表达式< Func< UserVM,object>>的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!