Linq 2 Sql - 已经打开的datareader问题

时间:2012-04-06 09:47:07

标签: c# linq-to-sql

我在我的应用程序中遇到了一个奇怪的错误。它有级别或错误消息:

  1. 已经和开放的datareader相关联。
  2. 读取器关闭时无效尝试调用Read。
  3. 索引超出了数组的范围。
  4. 指定的演员表无效。
  5. 首先让我解释一下我的代码在做什么: 我正在为我的Linq-Sql应用程序使用Repository Pattern。我从该存储库调用此方法

     internal static IEnumerable<ParentChild> GetAllCategoriesAndSubcategories()
            {
                lock (Context) // lock is implemented just before asking question, to check whether it can solve the issue or not...
                {
                    return from p in Context.Categories
                           let relatedchilds = (from c in Context.SubCategories
                                                where c.CategoryId == p.Id
                                                select c).Take(5)
                           select new ParentChild
                           {
                               Parent = p,
                               Childs = relatedchilds
                           };
                }
    
            }
    

    此方法从两个表(Parent和Child)中选取行,并将结果作为类

    的新集合返回
    public class ParentChild
        {
            public Category Parent { get; set; }
            public IEnumerable<SubCategory> Childs { get; set; }
        }
    

    有时它工作正常,但是当流量增加和并发时,那么在这种情况下我开始得到这些错误。来到这个问题,我从消费者IEnumerable<ParentChild> GetAllCategoriesAndSubcategories()消费,以heirarchy显示它。

    在UI我正在使用此方法呈现文本:

     /// <summary>
            /// Write categories Jquery html to the Category usercontrol
            /// </summary>
            private void WriteCategories()
            {
    
                // retrieves all categories and its subcategories as a generic list of ParentChild
                var dt = CategoryRepository.GetAllCategoriesAndSubcategories();
    
                //Conversion of dynamic jquery html string starts here
                var sb = new StringBuilder();
                sb.AppendLine(" <div class='widget_box' id='category'>");
                sb.AppendLine("     <div class='wintitle'>");
                sb.AppendLine("         <div class='inner_wintitle'>Categories</div>");
                sb.AppendLine("     </div>");
                sb.AppendLine("     <div class='winbody'>");
                sb.AppendLine("         <ul class='categories'>");
                var i = 1;
                foreach (ParentChild item in dt) //<--* BUGGY PART*
                {
                    sb.AppendLine(
                        string.Format("<li class='catetitle' id='catetitle{0}'><a href='subcategory.aspx?cid={1}&cname={2}'>{2}</a></li>", i,
                                      item.Parent.Id, item.Parent.Name));
                    sb.AppendLine(
                        string.Format("<li style='display:none;' class='category_sub' id='subcategory{0}' ><div><ul>", i));
                    foreach (var subCategory in item.Childs)
                    {
                        sb.AppendLine(string.Format("<li><a href='subcategory.aspx?cid={0}&cname={1}&scid={2}&scname={3}'>{3}</a></li>", item.Parent.Id,
                                                    item.Parent.Name, subCategory.Id, subCategory.Name));
                    }
                    sb.AppendLine(
                        string.Format(
                            "<li class='catetitle' id='catetitle{0}'><a href='subcategory.aspx?cid={1}&cname={2}'>View all categories</a></li>",
                            i, item.Parent.Id, item.Parent.Name));
                    sb.AppendLine("</ul></div></li>");
                    i++;
                }
                sb.AppendLine("</div></ul></div>");
                //Conversion of dynamic jquery html string ends here
    
                // javascript function to display the subcategories when mouse is hovered to the category
                sb.AppendLine("<script type='text/javascript'>init_categories();</script>");
                ucCategories1.CategoryHtml = sb.ToString(); // Generated text is finally set to the usercontrols property.
            }
    

    我收到了@ foreach (ParentChild item in dt)的错误。请帮我。

    建议要求: 我使用它作为我的repo模式实现:

     internal sealed class LeadsRepository : IRepository<BuySell>
        {
            private static readonly BusinessBazaarDataContext Context;
    
            static LeadsRepository()
            {
                Context = new BusinessBazaarDataContext();
            }
    }
    

    我认为这不是使用Datacontext的好方法。请建议我......谢谢

3 个答案:

答案 0 :(得分:0)

切换到List<ParentChild>并从List返回完全填充的GetAllCategoriesAndSubcategories。那么你的lock会做你想做的事(我假设)。

具体来说,您需要这样做:

internal static IList<ParentChild> GetAllCategoriesAndSubcategories()
    {
        lock (Context) // lock is implemented just before asking question, to check whether it can solve the issue or not...
        {
            return (from p in Context.Categories
                   let relatedchilds = (from c in Context.SubCategories
                                        where c.CategoryId == p.Id
                                        select c).Take(5)
                   select new ParentChild
                   {
                       Parent = p,
                       Childs = relatedchilds
                   }).ToList();
        }

    }

答案 1 :(得分:0)

您的代码中存在两个问题。

该错误的直接原因是Brett Veenstra认定的那个。如果没有ToList()调用,则返回一个查询(类型为IQueryable)。在枚举(即循环)之前,查询不会被执行。您不会在lock块中执行此操作,而是在使用结果时执行此操作。

布雷特对此的补救是正确的。在查询上调用ToList()将运行查询并将结果存储在 using块内的内存中。您需要在ToList()语句中再添加一个realtedChilds查询。否则你会遇到同样的问题。

另一个更大的问题是您在线程之间共享一个数据上下文。数据上下文从未为此设计过。相反,数据上下文是工作单元。在需要时创建一个并快速处理它:

using(context = new MyDataContextClass())
{
  return (from p in Context.Categories
          let relatedchilds = (from c in Context.SubCategories
                               where c.CategoryId == p.Id
                               select c).Take(5).ToList()
          select new ParentChild
          {
            Parent = p,
            Childs = relatedchilds
          }).ToList();
}

在您将数据上下文包装在存储库中的情况下,将存储库作为工作单元并将上下文作为存储库中的成员可能是有意义的。这意味着您将使用您的存储库实现IDisposable并在Dispose()方法中处理成员数据上下文。

作为旁注,您的代码还有另一个问题。它将分两步执行。首先它将获得所有父母,然后为每个父母再次命中数据库要求其子女。有超过几十个父母,这将是一个性能问题。

在linq-to-sql中执行此操作的正确方法是询问所有子项,执行ToList()后跟linq-to-objects GroupBy()。我不久前写了一篇关于它的blog entry

答案 2 :(得分:0)

是,同意ToList

原因:

“IEnumerable”执行C#代码中的所有操作,即linq-to-objects。这意味着所有操作都将在C#中发生,因此它本质上是连接的

使用IEnumerable调用数据后,所有数据都将从数据库中获取并发送到.net。这会严重降低性能(例如,linq-to-objects不会使用数据库索引),但另一方面,linq-to-objects更灵活,因为它可以执行任意C#代码而不受限制您的linq提供程序可以转换为SQL。