无法返回类型IQueryable或IEnumerable

时间:2013-10-22 18:12:36

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

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

从DB调用返回时(在单独的WCF服务或DLL中),Controller中的代码失败,因为 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();

DB方法:

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中,这显然不是很好的做法。

以下是适用的List约定:

控制器:

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

DB方法:

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

2 个答案:

答案 0 :(得分:9)

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

  • 因此,因为您使用DbContext创建了有效的临时using{},所以您从该函数中传出的IEnumerable是一个地雷 - 潜在的DbContext不再存在使用块,任何枚举或具体化集合的尝试都将失败。

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

  • 这是不从DAL / Repository返回IQueryable的主要原因之一 - 因为收件人无法知道查询所依赖的基础上下文是否已经在尝试使用结果之前关闭。 IEnumerable要好得多(当然List<T>实现IEnumerable<T>)并且恕我直言列表更好,因为它会强制您返回完全物化的集合并使调用者明白结果是没有连接到数据库了。

  • 所以你的工作版本可能很好 - 创建你的上下文,执行查询然后立即实现它执行实际的SQL然后从数据库中断开结果集。所以这种模式对你来说很好。

  • 但是,我建议您使用Ninject之类的IoC容器为您注入DbContext等内容,因为它可以让您不必担心DbContexts的生命周期。

    < / LI>

希望有所帮助。

答案 1 :(得分:2)

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

这里的关键是确切知道IQueryable的工作原理:

  

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

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

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

  • 调用GetProjectDropDownList方法
  • 在没有“枚举”它的情况下返回IEnumerable(这是一个Ling查询)
  • 处置DbContext
  • 在生成视图时,通过new SelectList枚举您的Linq查询,但......当时DbContext已被处置。

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

在您的上一次尝试中,一切正常,因为当您枚举Linq查询时,DbContext仍处于活动状态(project.ToList();

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