无法返回类型IQueryable或IEnumerable

5

由于Linq to Entities查询不能返回这两种类型中的任何一种,因此我选择返回List。

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

请注意以下代码。对于IEnumerable和IQueryable,由于上述原因,数据无法返回。

MVC Controller中的方法

// 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方法直接放在Controller中,但这显然不是好的实践。

以下是可以正常工作的List约定:

控制器(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
2个回答

9
当使用IEnumerable/IQueryable时,你的代码失败的原因是EF查询被延迟执行;也就是说,直到你开始枚举(foreach等)或"实体化"(ToList()等)结果之前,它才不会真正针对数据库执行。
因此,当你使用using{}创建了一个有效的临时DbContext时,从函数传出的IEnumerable是一个地雷——使用块后基础DbContext就不存在了,并且任何尝试枚举或实体化集合的尝试都会失败。
即使在没有使用using块创建DBContext时,你在执行查询之前Dispose了它,因此得到了相同的错误。
这是不从DAL/Repository返回IQueryable的主要原因之一——因为接收者无法知道查询所依赖的基础上下文是否已在他们尝试使用结果之前关闭。 IEnumerable更好,(当然,List<T>实现了IEnumerable<T>)而我认为List更好,因为它强制你返回一个完全实体化的集合,并向调用者明确表示结果不再与数据库连接。
因此,你拥有的工作版本可能很好——创建你的上下文,执行查询,然后立即实体化它,这将执行实际的SQL并将结果集从数据库断开连接。所以这个模式对你来说是可以正常工作的。
然而,我建议你查看是否可以使用IoC容器(如Ninject)为你注入类似DbContext这样的东西,因为它使你不必担心DbContext的生命周期。
希望这能帮到你。

如果在IOC容器中注入DbContext,如果我们使用using语句块,它是否仍然有效?我认为一旦你退出using语句块,Dispose()就会被调用并销毁上下文。 - Spock
1
很高兴它讲得通!@Raj,如果你使用IoC容器来注入DbContext,你就不需要使用using块了。当你使用依赖注入时,其中一件事情是你放弃了对注入对象的生命周期控制 - 在这种情况下,IoC容器会选择何时处理DbContext的释放。这只是一个小代价! - Stephen Byrne
+1 个好观点。它更像是地雷而不是定时炸弹,因为它只有在被踩到时才会爆炸。 - Amy B

2
正如您所知,DbContext允许您查询数据库,因此当向SGBD发送SQL请求时,它必须处于活动状态。
关键点在于确切了解IQueryable的工作原理:

IQueryable接口继承IEnumerable接口,因此如果它表示一个查询,则可以枚举该查询的结果。枚举会导致与IQueryable对象关联的表达式树被执行。 "执行表达式树"的定义对于查询提供程序是特定的。例如,它可能涉及将表达式树转换为适合底层数据源的适当查询语言。

这意味着只要您的Linq查询没有被枚举(使用.ToList()或foreach),就不会向数据库发送任何查询。执行被延迟!
在您的第一次尝试中,您正在:
  • 调用GetProjectDropDownList方法
  • 返回未“枚举”的IEnumerable(即Ling查询)
  • 处理DbContext
  • 通过new SelectList在生成视图时枚举Linq查询,但是...此时您的DbContext已被处理。
对于您的第二次尝试,DbContext稍后被处理,但在生成视图时也已经被处理。
在您的最后一次尝试中,一切正常,因为在枚举Linq查询(使用project.ToList();)时,您的DbContext仍然存在。
在我看来,将DbContext调用和实例隔离到数据访问层中,并返回列表或简单的断开连接对象并不是一个坏的做法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接