最近在研究动态拼接Select表达式和Where表达式,发现MemberInitExpression 可以达到我的目的,最初实现了单层的表达式生成,很是开心,然而,问题出现了,对于有导航属性的类型,网上找了一圈,没有解决方案,肿么办???终于在梦中想到一个方案,MemberInitExpression 生成的表达式也可以进行bind的啊,试了一下,果真如此,搞定收工!直接上代码,如下:
public class SelectExpressionBase
{/// <summary>
/// 生成查询表达式
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TDto"></typeparam>
/// <param name="parmExpression"></param>
/// <param name="fieldList">所需查询的列的集合,包含导航属性的全名</param>
/// <returns></returns>
public static MemberInitExpression CreateMemberInitExpression<TSource, TDto>(ParameterExpression parmExpression, List<string> fieldList)
{
//排序
fieldList.Sort();
//对应关系的集合,用于最终生成表达式
List<MemberBinding> memberBinding = new List<MemberBinding>();
//获取对应关系的绑定集合
memberBinding = GetMemberBindingList<TSource, TDto>(parmExpression, fieldList, 0);
//生成一个new表达式,例如 new Task()
NewExpression newExpression = Expression.New(typeof(TDto));
//讲new表达式与对应关系进行合并
MemberInitExpression memberInitExpression = Expression.MemberInit(newExpression, memberBinding);
//返回最终select表达式
return memberInitExpression;
}
/// <summary>
/// 获取绑定表达式集合
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TDto"></typeparam>
/// <param name="parmExpression"></param>
/// <param name="fieldList"></param>
/// <param name="index">生成表达式为第几级导航属性,初始为0</param>
/// <returns></returns>
public static List<MemberBinding> GetMemberBindingList<TSource, TDto>(ParameterExpression parmExpression, List<string> fieldList, int index)
{
List<MemberBinding> memberBinding = new List<MemberBinding>();
//表示存储已经生成的列,再次递归时不再生成该列的表达式
List<string> tempField = new List<string>();
foreach (var field in fieldList)
{
int len = field.Split('.').Length;
if (len - 1 == index)
{
//生成当前导航属性级别的基本属性
memberBinding.Add(GetMemberBinding<TSource, TDto>(parmExpression, field));
}
else
{
//生成当前导航属性级别的子级属性
if (string.IsNullOrEmpty(tempField.Where(x => x == field).FirstOrDefault()))//判断该属性是否已生成
{
string[] nameStr = field.Split('.');
//当前导航属性级别的全名
string fieldStr = "";
for (int i = 0; i < nameStr.Length; i++)
{
if (i < index)
{
fieldStr += nameStr[i] + ".";
}
else if (i == index)
{
fieldStr += nameStr[i];
}
}
//需要生成表达式的属性集合
List<string> currentList = fieldList.Where(x => x.Contains('.') && x.StartsWith(fieldStr + ".")).ToList();
//获取所有子级表达式
memberBinding.Add(GetChildMemberBinding<TSource, TDto>(parmExpression, currentList, fieldStr, index));
currentList.ForEach(x => tempField.Add(x));
}
}
}
return memberBinding;
}
/// <summary>
/// 获取子级绑定表达式
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TDto"></typeparam>
/// <param name="parmExpression"></param>
/// <param name="fieldList">所有子级属性集合</param>
/// <param name="childProperty">当前导航属性名称</param>
/// <param name="index">当前导航属性级别</param>
/// <returns></returns>
public static MemberBinding GetChildMemberBinding<TSource, TDto>(ParameterExpression parmExpression, List<string> fieldList, string childProperty, int index)
{
//获取属性信息,目的为生成new表达式
PropertyInfo dtoProperty = GetPropertyInfo<TDto>(null, childProperty);
Type type = dtoProperty.PropertyType;
NewExpression newExpression = Expression.New(type);
//导航属性的对象表达式,用于生成导航属性对象,例如:Task = new P_Task()........(其中前面的Task)
NewExpression dtoNewExpression = Expression.New(typeof(TDto));
MemberInfo dtoMember = GetMemberExpression<TDto>(null, dtoNewExpression, childProperty).Member;
//递归调用,获取子级绑定表达式,最终生成例如:Task = new P_Task()........
MemberBinding memberBinding = System.Linq.Expressions.Expression.Bind(dtoMember,
Expression.MemberInit(newExpression, GetMemberBindingList<TSource, TDto>(parmExpression, fieldList, index + 1)));
return memberBinding;
}
/// <summary>
/// 获取绑定表达式
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TDto"></typeparam>
/// <param name="parmExpression"></param>
/// <param name="propertyName">属性全名,包含导航属性</param>
/// <returns></returns>
public static MemberBinding GetMemberBinding<TSource, TDto>(ParameterExpression parmExpression, string propertyName)
{
//new表达式
NewExpression newExpression = Expression.New(typeof(TDto));
//EF属性表达式
Expression memExpression = GetPropertyExpression<TSource>(null, parmExpression, propertyName);
//Dto属性表达式
MemberInfo dtoMembers = GetMemberExpression<TDto>(null, newExpression, propertyName).Member;
//绑定
MemberBinding memberBinding = System.Linq.Expressions.Expression.Bind(dtoMembers, memExpression);
return memberBinding;
}
/// <summary>
/// 获取导航属性表达式
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <param name="source"></param>
/// <param name="para"></param>
/// <param name="fullName"></param>
/// <returns>最终生成类似x.A.B.C格式的表达式</returns>
public static Expression GetPropertyExpression<TSource>(Expression source, ParameterExpression para, string fullName)
{
string[] propertys = fullName.Split('.');
if (source == null)
{
source = Expression.Property(para, typeof(TSource).GetProperty(propertys.First()));
}
else
{
source = Expression.Property(source, propertys.First());
}
foreach (var item in propertys.Skip(1))
{
source = GetPropertyExpression<TSource>(source, para, item);
}
return source;
}
/// <summary>
/// 通过导航属性获取最后一级的属性信息
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <param name="source"></param>
/// <param name="fullName"></param>
/// <returns></returns>
public static PropertyInfo GetPropertyInfo<TSource>(PropertyInfo source, string fullName)
{
string[] propertys = fullName.Split('.');
if (source == null)
{
source = typeof(TSource).GetProperty(propertys.First());
}
else
{
source = source.PropertyType.GetProperty(propertys.First());
}
foreach (var item in propertys.Skip(1))
{
source = GetPropertyInfo<TSource>(source, item);
}
return source;
}
/// <summary>
/// 获取目标成员表达式
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <param name="source"></param>
/// <param name="newExpression"></param>
/// <param name="fullName"></param>
/// <returns></returns>
public static MemberExpression GetMemberExpression<TSource>(MemberExpression source, Expression newExpression, string fullName)
{
string[] propertys = fullName.Split('.');
if (source == null)
{
source = MemberExpression.MakeMemberAccess(newExpression, typeof(TSource).GetMember(propertys.First())[0]);
}
else
{
source = MemberExpression.MakeMemberAccess(source, Expression.Property(source, propertys.First()).Member);
}
foreach (var item in propertys.Skip(1))
{
source = GetMemberExpression<TSource>(source, source, item);
}
return source;
}
}