【发布时间】:2015-03-31 07:35:45
【问题描述】:
我有以下方法。我使用匿名投影来过滤包含 EF 的内容。我从这篇博文中学到了这个方法: http://thedatafarm.com/data-access/use-projections-and-a-repository-to-fake-a-filtered-eager-load/
public IEnumerable<Entities.Nutrient> FindAllForSpecificLanguage(bool overridePossibleLogicalDelete)
{
using (var context = CreateObjectContext())
{
context.ContextOptions.LazyLoadingEnabled = false;
Entities.Nutrient[] result;
var list = context.Nutrients
.Select(nut => new
{
Entity = nut,
Descriptions = nut.Descriptions.Where(desc => desc.LanguageCode.Equals(DataLanguageContext.Current.DataLanguageCode))
}).ToList(); //perform query
var resultList = list
.Select(entity => entity.Entity);
return resultList;
}
}
这个方法应该内置到所有服务中(api支持大约30种语言,目前我们有很多数据库开销......)。 我正在尝试以通用方式构建它,但我在表达式树方面非常缺乏经验。我以为我完全重新创建了该功能,但我遗漏了一些东西,因为它不起作用。 这是我到目前为止所拥有的:
public virtual IEnumerable<TEntity> FindAllForSpecificLanguage(bool overridePossibleLogicalDelete, Expression<Func<TEntity, IEnumerable<object>>> selectEntityDescriptions)
{
using (var context = CreateObjectContext())
{
context.ContextOptions.LazyLoadingEnabled = false;
ObjectQuery<TEntity> queryObjectSet = GetObjectSet(context);
TEntity[] result;
Type anonType = new {Entity = default(TEntity), Descriptions = Enumerable.Empty<object>()}.GetType();
// (entityManagerBaseEntity) => new { Entity = entityManagerBaseEntity, Descriptions = selectEntityDescriptions(entityManagerBaseEntity) }
// 1) "(entityManagerBaseEntity) =>"
var pe = Expression.Parameter(typeof(TEntity), "entityManagerBaseEntity");
// 2) "selectEntityDescriptions(entityManagerBaseEntity)"
var exprFunc = Expression.Invoke(selectEntityDescriptions, pe);
// get constructor for anonymous type
var constructorInfo = anonType.GetConstructor(new[] { typeof(TEntity), typeof(IEnumerable<object>) });
// 3) "new AnonType(entityManagerBaseEntity, exprFunc(entityManagerBaseEntity))"
var constructAnonType = Expression.New(constructorInfo, pe, exprFunc);
// 4) combine all to a lambda
// {entity => new <>f__AnonymousType0`2(entity, Invoke(entity => entity.Descriptions.Where(desc => desc.LanguageCode.Equals(DataLanguageContext.Current.DataLanguageCode)), entity))}
var cooleExpression = Expression.Lambda<Func<TEntity, dynamic>>(constructAnonType, pe);
//var bla = cooleExpression.Compile();
//var list = queryObjectSet.AsQueryable().Provider.CreateQuery<dynamic>(cooleExpression).ToList();
var list = queryObjectSet.Select(cooleExpression).ToList(); //perform query
var resultList = list
.Select(entity => entity.Entity as TEntity);
return resultList;
}
}
(注意:CreateObjectContext 和 GetObjectSet 是完美的工作方法)
应该这样称呼:
_nutrientManager.FindAllForSpecificLanguage(true, (entity) => entity.Descriptions.Where(desc => desc.LanguageCode.Equals(DataLanguageContext.Current.DataLanguageCode)))
在 cmets 中输入构建的表达式。我猜它看起来不错,但从未执行过连接。 如果我调试,我会得到以下堆栈跟踪:
System.NotSupportedException:LINQ to Entities 中仅支持无参数构造函数和初始化程序。 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.NewTranslator.TypedTranslate(ExpressionConverter 父级,NewExpression linq) 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
【问题讨论】:
标签: entity-framework generics linq-to-entities expression-trees projection