问题 从DbContext检索DbSet 的通用方法


我正在使用Entity Framework和一个大型数据库(由200多个表组成)。

试图创建一个返回的泛型方法 DbSet<T> 特定的表格 T (即上课,可以是 TableA)。

使用实体数据模型(自动)创建的实体类如下所示:

public partial class sqlEntities : DbContext
{

    public virtual DbSet<TableA> TableA { get; set; }
    public virtual DbSet<TableB> TableB { get; set; }
    public virtual DbSet<TableC> TableC { get; set; }
    ... // other methods

}

我的主要课程是这样的

public class TableModifier
{
   // Should return first 10 elements from a table of that type T
   public IQueryable<T> GetFromDatabase<T>() where T : EntityObject
   {
       try
       {
           using (sqlEntities ctx = new sqlEntities())
           {
               // Get the DbSet of the type T from the entities model (i.e. DB)
               DbSet<T> dbSet = ctx.Set<T>();
               return dbSet.Take(10);
           }
       }
       catch (Exception ex)
       {
           // Invalid type was provided (i.e. table does not exist in database)
           throw new ArgumentException("Invalid Entity", ex);
       }
   }
   ... // other methods
}

我必须设置一个约束 where T : EntityObject 上 T 在...内 EntityObject 界限。如果没有这样的约束那么 DbSet<T> dbSet 会抱怨(即 T必须是引用类型)它在类型方面可能会超出预期(基于 在此)。

当我尝试实际调用具有特定类型的方法时,会出现问题。

 [TestMethod]
 public void Test_GetTest()
 {
     TableModifier t_modifier = new TableModifier();

     // The get method now only accepts types of type EntityObject
     IQueryable<TableA> i_q = t_modifier.GetFromDatabase<TableA>();
 }

它给出了一个错误:

There is no implicit reference conversion from 'TableMod.TableA' to
'System.Data.Entity.Core.Objects.DataClasses.EntityObject'.

我怎么能(演员?) TableA 输入 EntityObject 如果我知道它存在于该实体模型中?

虽然这是不正确的语法(和逻辑),但这就是我所追求的:

 t_modifier.GetFromDatabase<(EntityObject)TableA>();

我该如何定义 TableA (以及所有其他200个表)类型是其中的一部分 EntityObject


潜在的解决方案

事实证明我的约束太具体了,我需要改变的是 where T : IEntity 至

where T : class

所以 T 是什么的 DbSet<T> 最初预期,类型

省去了必须向200多个表类添加实现的麻烦, TableATableB,...

当然还有其他问题,例如更改返回类型 IQueryable<T> 至 List<T> 自从 IQueryable 否则将被退回到范围之外 DbContext (即 sqlEntities)使它无用。


5870
2017-07-08 08:16


起源

你想测试什么? - Anatolii Gabuza
猜猜看 TableMod.TableA 不继承自 EntityObject? - DavidG
您可能正在瞄准不再需要所有实体类型派生的EF版本 EntityObject。运用 where T : class 将满足您的目的。现在没有必要更改现有类型的继承映射以适应这种情况 新 你写的方法。 - Kirill Shlenskiy


答案:


为什么不尝试将约束更改为类而不是EntityObject

public IQueryable<T> GetFromDatabase<T>() where T : class

6
2018-03-11 19:11





我有相同的要求并使用以下方法解决了它:

public static void GetEntitiesGeneric<TEntity>()// where TEntity : System.Data.Entity.Core.Objects.DataClasses.EntityObject  <-- NO LONGER NEEDED
{
    try
    {
        var key = typeof(TEntity).Name;
        var adapter = (IObjectContextAdapter)MyDbContext;
        var objectContext = adapter.ObjectContext;
        // 1. we need the container for the conceptual model
        var container = objectContext.MetadataWorkspace.GetEntityContainer(
            objectContext.DefaultContainerName, System.Data.Entity.Core.Metadata.Edm.DataSpace.CSpace);
        // 2. we need the name given to the element set in that conceptual model
        var name = container.BaseEntitySets.Where((s) => s.ElementType.Name.Equals(key)).FirstOrDefault().Name;
        // 3. finally, we can create a basic query for this set
        var query = objectContext.CreateQuery<TEntity>("[" + name + "]");

        // Work with your query ...
    }
    catch (Exception ex)
    {
        throw new ArgumentException("Invalid Entity Type supplied for Lookup", ex);
    }
}

代码取自 在实体框架中使用泛型用于查找表  并使用DbContext改编为EF 6(方法的第一部分,其中 objectcontext 是从中提取的 dbcontext

希望能帮助到你


3
2018-06-19 12:34



有什么理由我错过了为什么内置 context.Set<T>() 不行吗? - Chris Pratt


我不知道你是如何创建模型的,以及你的实体是如何形成的。但是,如果它是Code First,则实体类不会从公共基类继承,因此您无法向泛型添加类型约束。

我不建议使用基类来指定约束。使用界面做它会好得多。空接口允许您指定约束而无需更改类。

那么,你可以做的是定义这样的接口:

public interface IEntity {};

接着:

  • 在所有类中实现它,可以在部分类文件中完成,修改T4模板或以其他方式实现,具体取决于模型的外观
  • 用它来指定泛型类型约束 where IEntity

这是最干净的方式,不会对您的课程造成任何干扰。


0
2017-07-08 08:28



OP的模型是通过实体数据模型创建的,从而生成EDMX文件。这是数据库的第一种方法,所以不是代码优先。 - jaycer


问题 

我想你的 TableA class没有实现 EntityObject。这就是你得到这个错误的原因。要解决这个问题,你可以有一个抽象的类/接口,它将是所有上下文实体的基础(即具有唯一Id定义的IContextEntity):

public class TableA : IContextEntity
{
   ...
}

然后相同的方法,但用新的界面而不是 EntityObject 你可以轻松地模拟/测试它

public IQueryable<T> GetFromDatabase<T>() where T : IContextEntity
{
     ...
}


用法(测试)

第二个重要的是你想要使用这种方法的方式。的情况下 实体框架 这是真的 重要 分开 积分 和 单元测试。在您提供的代码中,您尝试访问数据库,这意味着此测试将是集成:

using (sqlEntities ctx = new sqlEntities()) // This will open a DB connection

连接到数据库或外部源通常是一种不好的做法,除非你知道你做了什么,这正是它。如果你只需要一些伪/假数据来对它执行操作 - 使用 存根


0
2017-07-08 08:26





对于任何未来的googlers,我的同事和我刚刚用Visual Basic(EF 6版本)破解了这个。它适用于我们简单地返回列表的用例,但可能适用于其他用例。不要尝试抓住或检查这一个。

Private Class getList(Of T As Class)
    Public Shared Function getList() As List(Of T)
        Using ctx As New MVPBTEntities()
            ' Get the DbSet of the type T from the entities model (i.e. DB)
            Dim dbSet = ctx.Set(Of T)()
            Return dbSet.ToList
        End Using
    End Function
End Class

0
2017-12-01 12:09



顺便提一下,当您使用Linq to Sql时,可以使用DBML文件。您只需将ctx更改为DBML上下文而不是EF上下文,并且需要将Set替换为GetTable。 - Richard Griffiths