Entity Framework 4.1 DbContext API 中的接口和存储库抽象中断子查询?
Posted
技术标签:
【中文标题】Entity Framework 4.1 DbContext API 中的接口和存储库抽象中断子查询?【英文标题】:Interface and repository abstraction break subqueries in Entity Framework 4.1 DbContext API? 【发布时间】:2011-05-13 17:24:53 【问题描述】:目标: 我正在尝试使用新的 Entity Framework 4.1 DbContext API(使用 Database First 和 POCO 类的新 ADO.NET DbContext Generator)并使用基本的通用存储库提供抽象层。
问题: 如果我尝试对我的存储库使用子查询,EF 无法完成翻译并引发错误:System.NotSupportedException: LINQ to Entities 无法识别方法 'System.Linq.IQueryable`1[EntityFramework41Test. Data.Entity.Table2] Query()'方法,该方法不能翻译成store表达式。
过去我已经成功地将这个设计与旧的 4.0 ObjectContext 一起使用,但我想使用新的 API。同样的方法也失败了 4.0 ObjectContext API(测试使用生成的 POCO 实体)。
注意:我认为发布数百行代码并不现实,但我有一个示例解决方案,其中包含一个使用 SQL Server CE 4.0 的 ASP.NET MVC 3 项目和一个演示结果的基本单元测试项目如果有帮助,可以上传或通过电子邮件发送各种方法。
我使用的存储库接口非常简单:
public interface IRepository<TEntity> : IDisposable where TEntity : class, ITestEntity
TEntity GetById(int id);
IQueryable<TEntity> Query();
void Add(TEntity entity);
void Remove(TEntity entity);
void Attach(TEntity entity);
上下文接口更加简单:
public interface ITestDbContext : IDisposable
IDbSet<TEntity> Set<TEntity>() where T: class, ITestEntity;
void Commit();
以下是不工作的示例用法,使用存储库和上下文实例的接口:
using (ITestDbContext context = new TestDbContext())
using (IRepository<Table1> table1Repository = new Repository<Table1>(context))
using (IRepository<Table2> table2Repository = new Repository<Table2>(context))
// throws a NotSupportedException
var results = table1Repository.Query()
.Select(t1 => new
T1 = t1,
HasMatches = table2Repository.Query()
.Any(t2 => t2.Table1Id == t1.Id)
)
.ToList();
上面的代码是我想使用的方法。具体的类最终会被注入。
请忽略这样一个事实,即编写此特定查询的方法比使用子查询更好。我特意简化了代码以关注实际问题:EF 不会翻译查询。
将“内部”存储库 Query() 方法存储在局部变量中确实有效,但并不理想,因为您必须始终记住这样做。
using (ITestDbContext context = new TestDbContext())
using (IRepository<Table1> table1Repository = new Repository<Table1>(context))
using (IRepository<Table2> table2Repository = new Repository<Table2>(context))
var table2RepositoryQuery = table2Repository.Query();
// this time, it works!
var results = table1Repository.Query()
.Select(t1 => new
T1 = t1,
HasMatches = table2RepositoryQuery
.Any(t2 => t2.Table1Id == t1.Id)
)
.ToList();
我还注意到其他一些方法会失败或成功,例如无视存储库并调用TestDbContext.Set<TEntity>()
有效,但ITestDbContext.Set<TEntity>()
不会翻译。将 ITestDbContext.Set<TEntity>()
的定义更改为返回 DbSet<TEntity>
而不是 IDbSet<TEntity>
仍然失败。
编辑: 如果没有一些查询拦截和翻译,我认为这是不可能的。如果我将来真的找到解决方案,我一定会分享它。
【问题讨论】:
不知何故,您的第一种方法不起作用,我并不感到惊讶。就像您要编写:HasMatches = SomeFunction()
与 bool value = SomeFunction(); ... HasMatches = value
相比,第一个在 LINQ to Entities 中不起作用,但第二个可以。 LTE 可以在您的子查询中使用IQueryable
,但不能使用返回IQueryable
的函数。但我也很惊讶这在 EF 4.0 中与 ObjectContext API 一起工作。也可以在这里 (social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/…) 提出您的问题,希望得到开发人员的关注。
@Slauma:您的局部变量/方法调用比较非常有意义。注意:我忽略了我的旧 ObjectContext API 项目在我的 Repository 类中使用查询拦截器逻辑来执行一些自动 Enum 转换(以解决 EF 中缺乏 Enum 支持的问题)。我没有做任何特定的事情来转换任何IQueryable
方法调用,但我想知道查询包装是否有导致上述场景起作用的副作用。我将看看我是否可以首先使用香草 EF 4.0 ObjectContext 来完成上述工作(这应该是我的第一种方法)。感谢 MSDN 论坛链接。
当您有结果时告诉我们。如果您的查询确实适用于 ObjectContext,这意味着我们使用 DbContext 的查询能力比使用 ObjectContext 的要少,这对我来说非常令人惊讶,因为我和您写的一样理解 - 所有的辛勤工作最终都从 DbContext 委托给了 ObjectContext .
@Slauma:我创建了一个更好的测试解决方案,它同时使用了旧的 ObjectContext API 和新的 DbContext API。两种情况都未能通过第一个示例并通过第二个示例。但是,它们都失败/通过了不同的测试用例,具体取决于上下文和查询集的转换方式。我不认为 DbContext 会牺牲任何功能,我更喜欢新的 API。我将暂时使用上面的第二个示例,稍后再探讨查询拦截/翻译。我会更新我的问题以反映我的新发现。
【参考方案1】:
我没有使用 EF 的经验,但基于使用 NHibernate 及其不断发展的 LINQ 支持,我怀疑您的问题的答案是您不喜欢的答案——听起来这个特定的构造不是(还没有?)受 EF LINQ 提供程序支持,您需要更改查询。
【讨论】:
嗯,作为“没有 EF 经验”的人,您在提问者收到“NotSupportedException”后“怀疑”查询“不支持”?你真的认为这是一个答案吗? @Slauma,中肯的批评。我愿意,因为我的回答是基于我对开发综合 LINQ 提供程序的(非常重要的)复杂性的经验以及我对另一个 ORM 实现的经验。我认为 OP 查询没有任何根本性的错误,除了他遇到了不受支持的边缘情况。当然,我同意 EF 专家很有可能会提出不同(更好)的答案。 这个答案在技术上是正确的。它根本不(还?)受支持。【参考方案2】:在创建 IQuerable 之前,您需要在 DbSet<SomeEntity>
上包含相关对象的所有属性。
public class SomeEntity
public Guid Id get; set;
public virtual SomeOtherEntity Other get; set;
public class Repository
public IQueryable<SomeEntity> Query()
_context.Set<SomeEntity>().Include("Other").AsQueryable();
您可以通过使用 func 或其他方式将其提供给您的 Query 方法。发挥你的创造力;)
【讨论】:
以上是关于Entity Framework 4.1 DbContext API 中的接口和存储库抽象中断子查询?的主要内容,如果未能解决你的问题,请参考以下文章
Entity Framework 4.1 InverseProperty 属性和ForeignKey
Entity Framework 4.1 Fluent API 属性
卸载 Entity Framework 4.1 六月 CTP
通过 Entity Framework 4.1 中的用户定义函数进行热切加载