从DbContext检索DbSet 的通用方法

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

尝试创建一个返回特定表TDbSet的generics方法(即类,可以是TableA )。

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

 public partial class sqlEntities : DbContext { public virtual DbSet TableA { get; set; } public virtual DbSet TableB { get; set; } public virtual DbSet TableC { get; set; } ... // other methods } 

我的主要课程是这样的

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

我必须设置一个约束where T : EntityObject T上的EntityObjectEntityObject范围内。 如果没有这样的约束,那么DbSet dbSet会抱怨(即T必须是引用类型 ),它可能比类型方面的预期要多( 基于此)。

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

  [TestMethod] public void Test_GetTest() { TableModifier t_modifier = new TableModifier(); // The get method now only accepts types of type EntityObject IQueryable i_q = t_modifier.GetFromDatabase(); } 

它给出了一个错误:

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

如果我知道TableA类型存在于该实体模型中,我如何(转换?)将TableA类型作为EntityObject

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

  t_modifier.GetFromDatabase(); 

如何将TableA (以及所有其他200个表)类型定义为EntityObject的一部分?


潜在的解决方案

事实certificate我的约束太具体了,我需要改变的是从where T : IEntity到的地方

 where T : class 

所以TDbSet最初期望的类,类型

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

然后当然还有其他问题,比如将返回类型从IQueryable更改为List因为否则会在DbContext (即sqlEntities )范围之外返回IQueryable使其无效。

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

 public IQueryable GetFromDatabase() where T : class 

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

 public static void GetEntitiesGeneric()// 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("[" + name + "]"); // Work with your query ... } catch (Exception ex) { throw new ArgumentException("Invalid Entity Type supplied for Lookup", ex); } } 

代码取自Entity Framework中的Using Generics for Lookup Tables,并使用DbContext(方法的第一部分,从dbcontext提取objectcontext )改编为EF 6

希望能帮助到你

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

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

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

 public interface IEntity {}; 

然后:

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

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

问题

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

 public class TableA : IContextEntity { ... } 

然后使用相同的方法,但使用新的接口而不是EntityObject ,您可以轻松地模拟/测试它

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

用法(测试)

第二个重要的是你想要使用这种方法的方式。 在entity framework上下文中,在集成unit testing之间进行分离非常重要 。 在您提供的代码中,您尝试访问数据库,这意味着此测试将是集成:

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

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

对于任何未来的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 (ie DB) Dim dbSet = ctx.Set(Of T)() Return dbSet.ToList End Using End Function End Class