[英]Linq 2 Sql - Already open datareader Issue
我的应用程序中出现了一个奇怪的错误。 它以级别或错误消息的形式出现:
让我先解释一下我的代码在做什么:我正在为我的 Linq-Sql 应用程序使用存储库模式。 从那个存储库我调用这个方法
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 两个表中选取行,并将结果作为 class 的新集合返回
public class ParentChild
{
public Category Parent { get; set; }
public IEnumerable<SubCategory> Childs { get; set; }
}
有时它工作正常但是当流量增加和并发时,在那种情况下我开始收到这些错误。 谈到这个问题,我正在使用IEnumerable<ParentChild> GetAllCategoriesAndSubcategories()
在 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)
。 请帮我。
需要的建议:我正在使用它作为我的回购模式实现:
internal sealed class LeadsRepository : IRepository<BuySell>
{
private static readonly BusinessBazaarDataContext Context;
static LeadsRepository()
{
Context = new BusinessBazaarDataContext();
}
}
我认为这不是使用 Datacontext 的好方法。 请建议我...谢谢
切换到List<ParentChild>
并从您的GetAllCategoriesAndSubcategories
返回一个完全填充的List
。 然后你的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();
}
}
您的代码中有两个问题。
该错误的直接原因是 Brett Veenstra 确定的错误。 如果没有ToList()
调用,您将返回一个查询(类型为 IQueryable)。 查询直到被枚举(即循环遍历)后才会执行。 您不会在lock
块内执行此操作,而是在使用结果时执行此操作。
布雷特对此的补救措施是正确的。 在查询上调用ToList()
将运行查询并将结果存储在using
块内memory 中的列表中。 不过,您需要向realtedChilds
语句再添加一个ToList()
查询。 否则你会在那里遇到同样的问题。
另一个更大的问题是您在线程之间共享一个数据上下文。 数据上下文从来不是为此设计的。 相反,数据上下文是一个工作单元。 在需要时创建一个并快速处理:
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()
。 我前段时间写了一篇关于它的博客文章。
是的,同意 ToList
原因:
“IEnumerable”执行 C# 代码中的所有操作,即 linq-to-objects。 这意味着所有操作都将在 C# 中发生,因此它本质上是已连接的
使用 IEnumerable 调用数据后,所有数据将从数据库中提取并发送到 .net。这会大大降低性能(例如,linq-to-objects 不会使用数据库索引),但另一方面linq-to-objects 更灵活,因为它可以执行任意 C# 代码,而不是受限于您的 linq 提供程序可以转换为 SQL。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.