接口和存储库抽象是否会破坏Entity Framework 4.1 DbContext API中的子查询?

时间:2011-05-13 17:24:54

标签: .net c#-4.0 entity-framework-4.1 abstraction

目标:
我尝试使用新的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()'方法,此方法无法转换为商店表达式。

我以前成功地将此设计与旧的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不会翻译查询。

存储&#34;内部&#34;存储库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>仍然失败。

修改
如果没有一些查询拦截和翻译,我认为这是不可能的。如果我确实在将来找到解决方案,我一定会分享它。

2 个答案:

答案 0 :(得分:1)

我没有EF的经验,但基于使用NHibernate及其不断发展的LINQ支持,我怀疑你的问题的答案是你不喜欢的 - 听起来这个特殊的构造不是(但?)由EF LINQ提供程序支持,您需要更改查询。

答案 1 :(得分:1)

在创建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方法。用你的创造力;)