无法返回 IQueryable 或 IEnumerable 类型

本文关键字:IEnumerable 类型 IQueryable 返回 | 更新日期: 2023-09-27 18:36:37

由于我无法从 Linq 到实体查询返回这些类型中的任何一种,因此我坚持返回列表。

从数据库调用(位于单独的 WCF 服务或 DLL 中)返回时,控制器中的代码将失败,因为数据库上下文连接已关闭

请注意下面的代码。对于 IEnumerable 和 IQueryable,由于上述描述,数据不会返回。

MVC 控制器中的方法

// Controller
IEnumerable<ProjectDescription> projectDdl = db.GetProjectDropDownList().AsEnumerable();
// Error coming back because dbcontext connection was closed.
ViewBag.ProjectsCust = new SelectList(projectDdl, "ProjectId", "Name");

WCF 服务或 DLL 中的方法

// WCF Service or DLL 
public IEnumerable<ProjectDescription> GetProjectDropDownList()
{
    try
    {
        //IQueryable<ProjectDescription> project = null;
        using (YeagerTechEntities DbContext = new YeagerTechEntities())
        {
            DbContext.Configuration.ProxyCreationEnabled = false;
            DbContext.Database.Connection.Open();
            IEnumerable<ProjectDescription> project = DbContext.Projects.Select(s =>
                new ProjectDescription()
                {
                    ProjectID = s.ProjectID,
                    Description = s.Description
                }
            );

            return project;
        }

    }
    catch (Exception ex)
    {
        throw ex;
    }
}

我什至尝试了以下方法,在数据库调用之前建立一个 DbContext 实例,但实际上尝试将 DbContext 传递给 DB 方法仍然不起作用。

控制器:

DbContext = new YeagerTechEntities();
IEnumerable<ProjectDescription> projectDdl = db.GetProjectDropDownList(DbContext).AsEnumerable();
ViewBag.ProjectsCust = new SelectList(projectDdl, "ProjectId", "Name");
DbContext.Dispose();

数据库方法:

public IEnumerable<ProjectDescription> GetProjectDropDownList(YeagerTechEntities DbContext)
{
    try
    {
        //IQueryable<ProjectDescription> project = null;

            DbContext.Configuration.ProxyCreationEnabled = false;
            DbContext.Database.Connection.Open();
            IEnumerable<ProjectDescription> project = DbContext.Projects.Select(s =>
                new ProjectDescription()
                {
                    ProjectID = s.ProjectID,
                    Description = s.Description
                }
            );

            return project;
        }

    catch (Exception ex)
    {
        throw ex;
    }
}

除了使用 List 之外,唯一有效的方法是将 DB 方法实际放置在控制器中,这显然不是好的做法。

以下是工作正常的列表约定:

控制器:

IEnumerable<ProjectDescription> projectDdl = db.GetProjectDropDownList();
                ViewBag.ProjectsCust = new SelectList(projectDdl, "ProjectId", "Name");

数据库方法:

public List<ProjectDescription> GetProjectDropDownList()
{
    try
    {
        using (YeagerTechEntities DbContext = new YeagerTechEntities())
        {
            DbContext.Configuration.ProxyCreationEnabled = false;
            DbContext.Database.Connection.Open();
            IEnumerable<ProjectDescription> project = DbContext.Projects.Select(s =>
                new ProjectDescription()
                {
                    ProjectID = s.ProjectID,
                    Description = s.Description
                }
            );
            List<ProjectDescription> myProjects = new List<ProjectDescription>();
            myProjects = project.ToList();
            return myProjects;
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

如果有人能向我解释在使用IQueryable或IEnumerable时正确的模型应该是什么,那就好了。但是,阅读此链接后,似乎列表绝对是要走的路:IEnumerable vs IQueryable for Business Logic或DAL返回类型

无法返回 IQueryable 或 IEnumerable 类型

使用 IEnumerable/IQueryable 时代码失败的原因是 EF 查询被延迟;也就是说,在您开始枚举(foreach等)或"具体化"(ToList()等)结果之前,它实际上不会针对数据库执行。

    因此,
  • 由于您使用using{}创建了有效的临时DbContext,因此从函数中传递的IEnumerable是地雷 - 在use块之后,底层DbContext将不复存在,并且任何枚举或具体化集合的尝试都将失败。

  • 即使您在没有 using 块的情况下创建了DBContext,您也可以在执行查询之前继续Dispose它 - 因此得到相同的错误。

  • 这是不从 DAL/存储库返回IQueryable的主要原因之一 - 因为收件人在尝试使用结果之前无法知道查询所依赖的基础上下文是否已关闭。 IEnumerable要好得多(当然List<T>实现IEnumerable<T>),恕我直言,列表甚至更好,因为它迫使您返回一个完全具体的集合,并向调用者明确结果不再连接到数据库。

  • 因此,您拥有的工作版本可能很好 - 创建上下文,执行查询,然后立即实现它,从而执行实际的SQL,然后将结果集与数据库断开连接。因此,该模式将适合您。

  • 但是,我建议您考虑使用诸如Ninject之类的IoC容器为您注入DbContext之类的东西,因为它使您不必担心DbContext的生命周期。

希望有帮助。

如您所知,您的DbContext使您能够查询数据库,因此当 SQL 请求发送到您的 SGBD 时,它必须处于活动状态。

这里的关键点是要确切地知道IQueryable是如何工作的:

IQueryable 接口继承了 IEnumerable 接口,以便 如果它表示查询,则可以枚举该查询的结果。 枚举导致表达式树与 IQueryable 相关联 要执行的对象。"执行表达式"的定义 树"特定于查询提供程序。例如,它可能涉及 将表达式树转换为适当的查询语言 基础数据源。

这意味着只要您的 Linq 查询尚未枚举(使用 .ToList() 或 foreach),不会向数据库发送任何查询。执行被推迟!

在您的第一次尝试中,您是:

  • 调用GetProjectDropDownList方法
  • 返回一个IEnumerable(这是一个 Ling 查询)而不"枚举"它
  • 处置DbContext
  • 生成视图时通过new SelectList枚举 Linq 查询,但...那时您的DbContext已经处理完毕。

这同样适用于您的第二次尝试,DbContext稍后会释放,但在生成视图时也已经处置。

在最后一次尝试中,一切正常,因为在枚举 Linq 查询时,DbContext仍然处于活动状态(使用 project.ToList();

在我看来,将DbContext调用和实例隔离到数据访问层并返回列表或简单的断开连接的对象根本不是一种糟糕的做法。