关于c#:无法返回类型IQueryable或IEnumerable

Cannot return type IQueryable or IEnumerable

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

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

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

MVC控制器中的方法

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

wcf服务或dll中的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 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方法时,该实例仍然不起作用。

控制器:

1
2
3
4
5
DbContext = new YeagerTechEntities();
IEnumerable<ProjectDescription> projectDdl = db.GetProjectDropDownList(DbContext).AsEnumerable();
ViewBag.ProjectsCust = new SelectList(projectDdl,"ProjectId","Name");

DbContext.Dispose();

DB方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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方法实际放在控制器中,这显然不是什么好的实践。

下面是适用的列表约定:

控制器:

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

DB方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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时,正确的模型应该是什么,那就好了。然而,在阅读了这个链接之后,显然列表是一种方式:用于业务逻辑或DAL返回类型的IEnumerable与iQueryable


使用IEnumerable/iqueryable时,代码失败的原因是因为ef查询被延迟;也就是说,在开始枚举(foreach等)或"物化"(ToList()等)结果之前,不会对数据库执行它。

  • 因此,由于您使用using{}创建了一个有效的临时DbContext,您从函数中传递出去的IEnumerable是一个地雷-在使用块之后,基础dbContext将不再存在,任何枚举或物化集合的尝试都将失败。

  • 即使您在没有使用块的情况下创建了DbContext,然后在执行查询之前继续执行并删除了Dispose,因此得到了相同的错误。

  • 这是不从DAL/存储库返回IQueryable的主要原因之一,因为接收者无法知道查询所依赖的基础上下文在尝试使用结果之前是否已关闭。IEnumerable更好(当然,List实现IEnumerable,imho list更好,因为它强制您返回完全物化的集合,并向调用者明确结果不再与数据库连接。

  • 因此,您拥有的工作版本可能很好——创建您的上下文,执行查询,然后立即具体化它,执行实际的SQL,然后断开结果集与数据库的连接。所以这个模式对你很有用。

  • 不过,我建议您使用一个ioc容器(如ninject)来为您注入dbContext,因为它可以让您不必担心dbContext的生命周期。

希望有帮助。


正如您已经知道的,您的DbContext允许您查询数据库,因此当SQL请求发送到您的sgbd时,它必须是活动的。

这里的关键点是准确了解iQuery的工作原理:

The IQueryable interface inherits the IEnumerable interface so that
if it represents a query, the results of that query can be enumerated.
Enumeration causes the expression tree associated with an IQueryable
object to be executed. The definition of"executing an expression
tree" is specific to a query provider. For example, it may involve
translating the expression tree to an appropriate query language for
the underlying data source.

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

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

  • 调用GetProjectDropDownList方法
  • 返回IEnumerable(这是一个ling查询),而不"枚举"它
  • 处理DbContext
  • 生成视图时通过new SelectList枚举LINQ查询,但…当时你的DbContext已经被处理了。

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

在上一次尝试中,一切都很好,因为当您枚举LINQ查询时,您的DbContext仍处于活动状态(使用project.ToList();)。

在我看来,将您的DbContext调用和实例隔离到数据访问层中,并返回列表或简单的断开连接的对象,这一点都不是一个坏做法。