我想创建可以将IQueryable数据库模型映射到select的应用程序模型的类。我已经使用了此页面中的代码devtrends并对其进行了更改,以增加使用自定义绑定规则的可能性,如下所示:
var db = new Models.DBContext();
var a = db.User.Map().
Rule<AppModel.AppModel>(dest => dest.Address1, src => src.Address.Select(v => v.Address1).FirstOrDefault()).
Rule<AppModel.AppModel>(dest => dest.Number, src => src.Number.Num).
To<AppModel.AppModel>().ToList();
但是当我使用上面的代码时,我遇到了错误:
参数“x”未绑定在指定的LINQ to Entities查询表达式中。
我知道这是因为我的自定义绑定字典在查询中具有与主表达式中的参数不同的参数对象,但我不知道如何在方法 Rule 中更改此参数以重用< em> parameterExpression object。
全班来映射:
public static class QueryableExtensions
{
/// <summary>
/// Map source object to another type as IQueryable.
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
public static MapExpression<TSource> Map<TSource>(this IQueryable<TSource> source)
{
return new MapExpression<TSource>(source);
}
}
public class MapExpression<TSource>
{
static readonly Dictionary<string, Expression> ExpressionCache = new Dictionary<string, Expression>();
readonly IQueryable<TSource> _source;
static Dictionary<MemberInfo, Expression> maps = new Dictionary<MemberInfo, Expression>();
static ParameterExpression parameterExpression = Expression.Parameter(typeof(TSource), "src");
public MapExpression(IQueryable<TSource> source)
{
_source = source;
}
/// <summary>
/// Type to cast
/// </summary>
/// <typeparam name="TDest"></typeparam>
/// <returns></returns>
public IQueryable<TDest> To<TDest>()
{
var queryExpression = GetCachedExpression<TDest>() ?? BuildExpression<TDest>();
return _source.Select(queryExpression);
}
public MapExpression<TSource> Rule<TDest>(Expression<Func<TDest, object>> dest, Expression<Func<TSource, object>> src)
{
var d = (dest.Body as MemberExpression).Member;
maps.Add(d, src);
return this;
}
static Expression<Func<TSource, TDest>> GetCachedExpression<TDest>()
{
var key = GetCacheKey<TDest>();
return ExpressionCache.ContainsKey(key) ? ExpressionCache[key] as Expression<Func<TSource, TDest>> : null;
}
static Expression<Func<TSource, TDest>> BuildExpression<TDest>()
{
var sourceProperties = typeof(TSource).GetProperties();
var destinationProperties = typeof(TDest).GetProperties().Where(dest => dest.CanWrite);
var bindings = destinationProperties
.Select(destinationProperty => BuildBinding(parameterExpression, destinationProperty, sourceProperties))
.Where(binding => binding != null);
var expression = Expression.Lambda<Func<TSource, TDest>>(Expression.MemberInit(Expression.New(typeof(TDest)), bindings), parameterExpression);
var key = GetCacheKey<TDest>();
ExpressionCache.Add(key, expression);
return expression;
}
static MemberAssignment BuildBinding(Expression parameterExpression, MemberInfo destinationProperty, IEnumerable<PropertyInfo> sourceProperties)
{
var sourceProperty = sourceProperties.FirstOrDefault(src => src.Name == destinationProperty.Name);
if (sourceProperty != null && ((PropertyInfo)destinationProperty).PropertyType == sourceProperty.PropertyType)
return Expression.Bind(destinationProperty, Expression.Property(parameterExpression, sourceProperty));
if (maps.ContainsKey(destinationProperty))
{
var x = ((LambdaExpression)maps[destinationProperty]).Body;
return Expression.Bind(destinationProperty, x);
}
var propertyNames = SplitCamelCase(destinationProperty.Name);
if (propertyNames.Length == 2)
{
sourceProperty = sourceProperties.FirstOrDefault(src => src.Name == propertyNames[0]);
if (sourceProperty != null)
{
var sourceChildProperty = sourceProperty.PropertyType.GetProperties().FirstOrDefault(src => src.Name == propertyNames[1]);
if (sourceChildProperty != null)
return Expression.Bind(destinationProperty, Expression.Property(Expression.Property(parameterExpression, sourceProperty), sourceChildProperty));
}
}
return null;
}
static string GetCacheKey<TDest>() =>
string.Concat(typeof(TSource).FullName, typeof(TDest).FullName);
static string[] SplitCamelCase(string input) =>
Regex.Replace(input, "([A-Z])", " $1", RegexOptions.Compiled).Trim().Split(' ');
}