【问题标题】:DataReader already opened and must be closed. I don't understandDataReader 已打开,必须关闭。我不明白
【发布时间】:2017-07-27 19:33:21
【问题描述】:

我正在建模一个数据库以在影院中插入电影和电影的会话。我知道一个剧院有很多场,一个场有电影和剧院。

public class Movie
    {
        public int MovieID { get; set; }
        public String MovieName { get; set; }
        public int Duration { get; set; }
        public int Rate { get; set; }
    }

public class Theater
    {
        public int TheaterID { get; set; }
        public String TheaterName { get; set; }
        public virtual ICollection<Session> Sessions { get; set; }
    }

public class Session
    {
        public int SessionID { get; set; }
        public int MovieID { get; set; }
        public int TheaterID { get; set; }
        public virtual Movie Movie { get; set; }
        public virtual Theater Theater { get; set; }
    }

我有一个接收一组会话的视图。在这个视图中,我使用一个 foreach 来显示剧院名称和其中的所有电影:

这是来自控制器的代码:

 public ActionResult MoviesSessions(int id)
        {
            Theater theater = db.Theater.Find(id);
            ViewBag.TheaterName = Theater.TheaterName;
            var sessions = from s in db.Sessions where s.TheaterID == id orderby s.MovieID select s;
            return View(sessions);
        }

这是在视图内部:

@{
        string theaterName = "";
        foreach (var item in Model)
        {
            if (theaterName != item.Theater.TheaterName)
            {
                theaterName = item.Theater.TheaterName;
                <tr>
                    <td>@Html.DisplayFor(modelItem => item.Movie.MovieName)</td>
                </tr>
            }
        }
    }

因为我使用的是item.Movie.MovieName,所以我收到以下错误:

“已经有一个打开的 DataReader 与此命令关联,必须先关闭。”

Movie 对象是否尚未填充到控制器查询中?我不明白为什么我会收到这个错误。这是我第一次使用 asp.net

【问题讨论】:

    标签: c# asp.net-mvc linq session


    【解决方案1】:

    item.Movie 属性是一个延迟查询,它正在尝试使用从第一个查询返回结果的同一连接执行另一个数据库查询。某些数据库连接一次只允许一个活动的结果流,因此您必须先读取所有行/实体,然后才能开始探索。在将查询(会话)传递给 View 函数之前,您可以通过在查询(会话)上调用 .ToList 轻松做到这一点。此外,如果您使用的是 SQL 服务器,您可以在连接字符串中设置“MultipleActiveResultSets=True”。

    【讨论】:

    • 我不知道 MultipleActiveResultSets。执行另一个查询会损害性能?有没有其他人可以在一个查询中做到这一点?
    • 如果您使用的是 EF,那么 Include 可能会在一个查询中执行此操作,或者只是为您缓冲结果(相当于您使用 .ToList)。我不确定实现是做什么的。 L2S 等效于 Include 会使用外连接完成一个查询。
    【解决方案2】:

    您只需在连接字符串中设置MultipleActiveResultSets=true。由于您正在循环查询,因此您正在读取第一个对象,然后您尝试加载 item.Theater.TheaterName ,因为它具有导航属性并且不包含在查询中,EF 将触发第二个查询以加载 item.Theater.TheaterName ,因为之前的查询尚未完成,因为您的枚举尚未完成。如果 MultipleActiveResultSet 未设置为 true,它将引发错误。将其设置为 true 将允许 EF 在同一连接上打开多个打开的查询。

      connectionString="Data Source=MATT-PC\SQLEXPRESS;" + 
                 "Initial Catalog=Raise;Integrated Security=True;" + 
                 "MultipleActiveResultSets=true;" 
    

    其他方法是使用ToListinclude

     // .ToList() will force entire query to load and close data reader
     foreach(var m in Model.ToList()){
          ....
     }
    

     // since you are only interested in Theater, you could eager load 
     // Theater, this will cause EF to load Theater along with Movies in single
     // query
     foreach(var m in Model.Include("Theater")){
          ....
     }
    

    推荐

     foreach(var m in Model.Include("Theater").ToList()){
          ....
     }
    

    许多人会争辩说这会导致代码中的双重枚举,但这也减少了 sql server 上的打开查询时间,这有助于减少事务死锁和超时。在底层,SQL 查询对整个枚举都是开放的,并且在事务内部,它肯定会损害 SQL 的性能。

    【讨论】:

    • 我不知道 MultipleActiveResultSets。插入 ToList 比 Include 好?
    • 我会一直使用ToList(),这样可以减少数据库死锁,因为查询未对整个枚举打开,您也可以使用Include("Theater").ToList(),这是首选,因为所有内容都将在单个查询中加载。如果您的数据库在不同的机器或远程机器(Sql Azure)上,使用Include("Theater") 将需要单程带数据,建议使用Include("Theater").ToList()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-10
    • 2015-02-03
    • 1970-01-01
    • 1970-01-01
    • 2019-01-17
    • 2015-04-02
    • 2011-08-29
    相关资源
    最近更新 更多