【问题标题】:Cannot return type IQueryable or IEnumerable无法返回类型 IQueryable 或 IEnumerable
【发布时间】:2013-10-22 18:12:36
【问题描述】:

由于我无法从 Linq to Entities 查询中返回这两种类型中的任何一种,所以我坚持返回一个列表。

从 DB 调用(位于单独的 WCF 服务或 DLL 中)返回时,控制器中的代码将失败,因为 dbcontext 连接已关闭。

注意下面的代码。对于 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;
    }
}

我什至尝试在 DB 调用之前建立一个 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 方法实际放置在 Controller 中,这显然不是一个好的做法。

这是正常工作的列表约定:

控制器:

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 时正确的模型应该是什么,那很好。但是,在阅读了这个链接之后,看起来 List 绝对是要走的路: IEnumerable vs IQueryable for Business Logic or DAL return Types

【问题讨论】:

    标签: c# asp.net-mvc linq wcf entity-framework


    【解决方案1】:

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

      1234563尝试枚举或具体化集合将失败。
    • 即使您在没有 using 块的情况下创建了 DBContext,然后您在执行查询之前继续使用 Dispose'd - 因此会出现相同的错误。

    • 这是不从 DAL/存储库返回 IQueryable 的主要原因之一 - 因为接收者无法知道查询所依赖的基础上下文是否之前已关闭他们试图消耗结果。 IEnumerable 更好(List&lt;T&gt; 当然实现了IEnumerable&lt;T&gt;),恕我直言 List 更好,因为它强制您返回一个完全物化的集合,并让调用者清楚地知道结果没有连接到数据库更多。

    • 所以您拥有的工作版本可能没问题 - 创建您的上下文,执行查询然后立即实现它,它执行实际的 SQL,然后断开结果集与数据库的连接.所以这个模式对你很有效。

    • 不过,我建议您考虑使用 IoC 容器(例如 Ninject)为您注入 DbContext 之类的东西,因为这样您就不必担心 DbContext 的生命周期了。

    希望对您有所帮助。

    【讨论】:

    • 同样将 DbContext 注入到 IOC 容器中,如果我们使用 using 块,它还能工作吗?我想一旦你离开 using 块,Dispose() 就会被调用并且上下文被破坏?
    • 很高兴这是有道理的!@Raj,如果您使用 IoC 容器来注入 DbContext,您将不会有 using 块 - 当您使用依赖注入时发生的一件事就是您投降控制注入对象的生命周期——在这种情况下,IoC 容器会选择何时处置 DbContext。这是一个很小的代价!
    • 有道理。谢谢:)
    • +1 好点。更像是地雷而不是定时炸弹,因为它只有被踩到才会爆炸。
    【解决方案2】:

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

    这里的关键是要准确了解IQueryable 的工作原理:

    IQueryable 接口继承了 IEnumerable 接口,因此 如果它代表一个查询,则可以枚举该查询的结果。 枚举导致与 IQueryable 关联的表达式树 要执行的对象。 “执行表达式”的定义 树”特定于查询提供程序。例如,它可能涉及 将表达式树翻译成适当的查询语言 底层数据源。

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

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

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

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

    在您最后一次尝试中,一切正常,因为当您枚举 Linq 查询时您的 DbContext 仍然存在(使用 project.ToList();)

    在我看来,将您的 DbContext 调用和实例隔离到 Data Access Layer 并返回列表或简单的断开连接的对象根本不是一个坏习惯。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-04
      • 2011-04-03
      • 2016-10-26
      • 1970-01-01
      • 2013-04-27
      • 1970-01-01
      相关资源
      最近更新 更多