从DbContext </t>检索DbSet <t>的通用方法

时间:2014-07-08 08:16:39

标签: c# entity-framework generics

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

尝试创建一个返回特定表DbSet<T>的{​​{1}}的泛型方法(即类,可以是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会抱怨(即 T必须是引用类型),它可能会比类型(based所获得的更多。在此)。

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

DbSet<T> dbSet

它出错了:

 [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>(); (以及所有其他200个表格)类型定义为TableA的一部分?


潜在的解决方案

原来我的约束太具体了,我需要改变的是从EntityObject

where T : IEntity

因此where T : class T最初预期的类型

省去了必须向200多个表类添加实现的麻烦,DbSet<T>TableA,...

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

5 个答案:

答案 0 :(得分:7)

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

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

答案 1 :(得分:4)

我有相同的要求,并使用以下方法解决了这个问题:

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);
    }
}

代码取自Using Generics for Lookup Tables in Entity Framework ,并使用DbContext进行了适用于EF 6(方法的第一部分,objectcontextdbcontext

中提取

希望有所帮助

答案 2 :(得分:0)

问题

我认为您的TableA课程没有实现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

除非您知道自己在做什么,否则连接到数据库或外部源通常是一种不好的做法。如果您只需要一些伪/假数据来执行操作 - 请使用存根

答案 3 :(得分:0)

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

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

所以,你可以做的是定义一个这样的界面:

public interface IEntity {};

然后:

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

这是最干净的方式,不会对你的班级造成任何干扰。

答案 4 :(得分:0)

对于任何未来的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