我有以下扩展方法:
public static decimal? GetValue(this Member member)
{
return member.Readings.SelectMany(r => r.Measurements).GetLatestValue();
}
GetLatestValue
是另一个仅使用其他LINQ扩展程序的扩展程序:OrderBy
,Where
,Select
和First
。
我希望这会执行JOIN
查询。相反,当我查看SQL事件探查器时,它正在执行单独的查询来为每次读取选择所有度量。
我从this和this question了解到,如果我传入数据库上下文并使用它,我可以获得JOIN
,但这对我来说不是一个选项。
这里发生了什么?为什么Readings
属性为ICollection
,而不是IQueryable
?如何在此处获取单个查询,而无需更改扩展方法签名?
答案 0 :(得分:1)
这里发生了什么?
您对问题的描述是准确的。
“为什么Readings属性是ICollection,而不是IQueryable?”
这是Entity Framework中的一个设计错误。
如何在此处获取单个查询,而无需更改扩展方法签名?
这是不可能的。您的方法强制要求查询查询。即使member.Readings
为IQueryable
,您仍然会在此处强制进行评估。
注意,EF永远不能远程GetLatestValue
到SQL(我假设它是你的自定义函数)。没有解决方法。 EF无法为任意C#函数生成SQL。
不幸的是,你的情况没有很好的解决方案。您将不得不重构您的代码,以便它与Entity Framework及其限制很好地协作。您链接的帖子与此相关。
答案 1 :(得分:0)
我想出了一个解决方案,允许我使用我的调用代码,因为我希望它可以工作,但有一点点黑客:
在我的扩展方法和需要类似查询的任何其他地方,我只需要确保调用此代码:
EvilHackyContextUtilities.SetReadingsQueryableHack(member);
这样做会将member.Readings
属性替换为我自己的ICollection
,同时也是IQueryable
。 IQueryable
方面的内容使用与我链接的其他问题中建议的相同查询。我已设法使用反射获取ObjectContext
,然后将其传递到我的新MyDbContext
的构造函数中。我必须稍微更改.tt
源代码,以添加使用DbContext(ObjectContext objectContext, bool dbContextOwnsObjectContext)
作为基础的构造函数。
public static class EvilHackyContextUtilities
{
private static MyDbContext GetDbContext(object entity)
{
var entityWrapper = entity.GetType().GetField("_entityWrapper").GetValue(entity);
var objectContext = entityWrapper.GetType().GetProperty("Context").GetValue(entityWrapper, null) as ObjectContext;
return new MyDbContext(objectContext, false);
}
public static void SetReadingsQueryableHack(Member entity)
{
if (entity.Readings is EvilHackyQueryableCollection<Reading>)
return;
IQueryable<Reading> query = GetDbContext(entity).Readings.Where(r => r.MemberID == entity.MemberID);
entity.Readings = new EvilHackyQueryableCollection<Reading>(entity.Readings, query);
}
}
internal class EvilHackyQueryableCollection<TEntity> : ICollection<TEntity>, IQueryable<TEntity>
where TEntity : class
{
private readonly IQueryable<TEntity> _baseQuery;
private readonly ICollection<TEntity> _baseCollection;
public EvilHackyQueryableCollection(ICollection<TEntity> baseCollection, IQueryable<TEntity> baseQuery)
{
_baseQuery = baseQuery;
_baseCollection = baseCollection;
}
#region ICollection members
//All middle-man methods wrapping up the _baseCollection field.
#endregion
#region IQueryable members
//All middle-man methods wrapping up the _baseQuery field.
#endregion
}