C# 检索DbSet的通用方法<;T>;来自DbContext

C# 检索DbSet的通用方法<;T>;来自DbContext,c#,entity-framework,generics,C#,Entity Framework,Generics,我将实体框架用于一个大型数据库(由200多个表组成) 试图创建一个泛型方法,返回特定表T(即类,可以是TableA)的DbSet 使用实体数据模型(自动)创建的实体类如下所示: public partial class sqlEntities : DbContext { public virtual DbSet<TableA> TableA { get; set; } public virtual DbSet<TableB> TableB { get;

我将实体框架用于一个大型数据库(由200多个表组成)

试图创建一个泛型方法,返回特定表
T
(即类,可以是
TableA
)的
DbSet

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

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

}
它给出了一个错误:

There is no implicit reference conversion from 'TableMod.TableA' to
'System.Data.Entity.Core.Objects.DataClasses.EntityObject'.
如果我知道该实体模型存在
TableA
类型,我如何(强制转换?)将其作为
EntityObject
类型

虽然这是不正确的语法(和逻辑),但这正是我想要的:

 t_modifier.GetFromDatabase<(EntityObject)TableA>();
所以
T
DbSet
最初期望的类类型

省去了向200多个表类添加实现的麻烦,
TableA
TableB


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

问题

我假设您的
TableA
类没有实现
EntityObject
。这就是为什么你会犯这个错误。为了解决这个问题,您可以有一个抽象类/接口,它将是所有上下文实体的基础(即具有唯一Id定义的IContextEntity):

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

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

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

我不知道您是如何创建模型的,也不知道您的实体是什么样子的。但是,如果是先编写代码,则实体类不会从公共基类继承,因此不能向泛型类添加类型约束

我不建议使用基类来指定约束。最好使用一个接口。空接口将允许您指定约束,而无需更改任何类

因此,您可以这样定义一个接口:

public interface IEntity {};
然后:

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

这是最干净的方法,不会对您的类造成任何干扰。

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

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);
    }
}
public static void GetEntitiesGeneric()//其中tenty:System.Data.Entity.Core.Objects.DataClasses.EntityObject s.ElementType.Name.Equals(key)).FirstOrDefault().Name;
// 3. 最后,我们可以为这个集合创建一个基本查询
var query=objectContext.CreateQuery(“[”+name+“]);
//处理您的查询。。。
}
捕获(例外情况除外)
{
抛出新ArgumentException(“为查找提供的实体类型无效”,ex);
}
}
代码取自EF 6,并使用DbContext(方法的第一部分,其中从
DbContext
中提取
objectcontext


希望能有所帮助,为什么不尝试将约束更改为类而不是EntityObject

public IQueryable<T> GetFromDatabase<T>() where T : class
public IQueryable GetFromDatabase(),其中T:class

对于任何未来的谷歌用户,我的同事和我刚刚在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

你想测试什么?猜测
TableMod.TableA
不是从
EntityObject
继承的?你的目标可能是EF的一个版本,它不再要求所有实体类型都从
EntityObject
派生。使用
where t:class
就可以满足你的需要。没有真正的必要更改inh只是为了适应您正在编写的这个新方法而在现有类型上映射eritance。是否有什么原因我不知道为什么内置的
context.Set()
不起作用?顺便说一句,当您使用Linq to Sql时,这可以与DBML文件一起工作。您只需将ctx更改为DBML上下文而不是EF上下文,并且需要将Set替换为GetTable。OP的模型是通过实体数据模型创建的,这会生成一个EDMX文件。这是数据库优先的方法,而不是代码优先rst。
using (sqlEntities ctx = new sqlEntities()) // This will open a DB connection
public interface IEntity {};
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);
    }
}
public IQueryable<T> GetFromDatabase<T>() where T : class
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