gpt4 book ai didi

asp.net - 缓存 LINQ-SQL 对象和 DataContext 线程安全

转载 作者:行者123 更新时间:2023-12-01 06:33:27 25 4
gpt4 key购买 nike

我们使用 LINQ-SQL 查询数据库,然后将生成的主表对象存储在 HTTP 缓存中。
后来,主对象被用来查询它的子对象,使用延迟加载。以下是相关的代码段 - 我在一个新的概念验证应用程序中重新创建了该场景:

        if (HttpRuntime.Cache["c"] == null)
{
LockApp.Models.DBDataContext db = new Models.DBDataContext();

var master = db.Masters.ToList();
HttpRuntime.Cache.Add("c", master,
null, DateTime.Now.AddMonths(1),
TimeSpan.Zero, CacheItemPriority.Normal, null);

}

ViewBag.Data = (List<LockApp.Models.Master>)HttpRuntime.Cache["c"];

这是迭代主对象和细节对象的 Razor View :
    @foreach(var m in ViewBag.Data){
@m.Id<nbsp></nbsp>
foreach(var d in m.Details){
@d.Id<nbsp></nbsp>
}
<br />
}

它工作得很好并正确缓存数据。但是,在清除缓存后有多个请求试图访问网站时,它会失败 - 我正在使用 JMeter 进行测试,基本上用许多(50)个并行线程访问网站,然后触摸 web.config - 我立即开始看到以下两个错误之一:

索引超出数组范围

在 foreach(var d in m.Details)

此错误永远不会消失,即缓存中的某些数据已损坏

具有以下堆栈:
  System.Collections.Generic.List`1.Add(T item) +34
System.Data.Linq.SqlClient.SqlConnectionManager.UseConnection(IConnectionUser user) +305
System.Data.Linq.SqlClient.SqlProvider.Execute(Expression query, QueryInfo queryInfo, IObjectReaderFactory factory, Object[] parentArgs, Object[] userArgs, ICompiledSubQuery[] subQueries, Object lastResult) +59
System.Data.Linq.SqlClient.SqlProvider.ExecuteAll(Expression query, QueryInfo[] queryInfos, IObjectReaderFactory factory, Object[] userArguments, ICompiledSubQuery[] subQueries) +118
System.Data.Linq.SqlClient.CompiledQuery.Execute(IProvider provider, Object[] arguments) +99
System.Data.Linq.DeferredSourceFactory`1.ExecuteKeyQuery(Object[] keyValues) +402
System.Data.Linq.DeferredSourceFactory`1.Execute(Object instance) +888
System.Data.Linq.DeferredSource.GetEnumerator() +51
System.Data.Linq.EntitySet`1.Load() +107
System.Data.Linq.EntitySet`1.GetEnumerator() +13
System.Data.Linq.EntitySet`1.System.Collections.IEnumerable.GetEnumerator() +4
ASP._Page_Views_Home_Index_cshtml.Execute() in c:\Users\prc0092\Documents\Visual Studio 2012\Projects\LockApp\LockApp\Views\Home\Index.cshtml:16

或者这个错误

ExecuteReader 需要一个打开且可用的连接。连接的当前状态是打开的。

在同一行 foreach(var d in m.Details)

如果我停止使用并行请求访问站点,此错误会在一段时间后消失

有以下堆栈
   System.Data.SqlClient.SqlConnection.GetOpenConnection(String method) +5316460
System.Data.SqlClient.SqlConnection.ValidateConnectionForExecute(String method, SqlCommand command) +7
System.Data.SqlClient.SqlCommand.ValidateCommand(String method, Boolean async) +155
System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task&amp; task, Boolean asyncWrite) +82
System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) +53
System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) +134
System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) +41
System.Data.Common.DbCommand.ExecuteReader() +12
System.Data.Linq.SqlClient.SqlProvider.Execute(Expression query, QueryInfo queryInfo, IObjectReaderFactory factory, Object[] parentArgs, Object[] userArgs, ICompiledSubQuery[] subQueries, Object lastResult) +1306
System.Data.Linq.SqlClient.SqlProvider.ExecuteAll(Expression query, QueryInfo[] queryInfos, IObjectReaderFactory factory, Object[] userArguments, ICompiledSubQuery[] subQueries) +118
System.Data.Linq.SqlClient.CompiledQuery.Execute(IProvider provider, Object[] arguments) +99
System.Data.Linq.DeferredSourceFactory`1.ExecuteKeyQuery(Object[] keyValues) +402
System.Data.Linq.DeferredSourceFactory`1.Execute(Object instance) +888
System.Data.Linq.DeferredSource.GetEnumerator() +51
System.Data.Linq.EntitySet`1.Load() +107
System.Data.Linq.EntitySet`1.GetEnumerator() +13
System.Data.Linq.EntitySet`1.System.Collections.IEnumerable.GetEnumerator() +4
ASP._Page_Views_Home_Index_cshtml.Execute() in c:\Users\prc0092\Documents\Visual Studio 2012\Projects\LockApp\LockApp\Views\Home\Index.cshtml:16

我尝试过的不同的东西

双锁

没有帮助
    private static object ThisLock = new object();

public ActionResult Index()
{

if (HttpRuntime.Cache["c"] == null)
{
lock (ThisLock)
{
if (HttpRuntime.Cache["c"] == null)
{

预先加载子数据

有效,但需要持续维护,因为并非所有 child 都应预先装载,另外请参阅下一条说明
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Master>(b => b.Details);
db.LoadOptions = dlo;

尝试访问其子对象时锁定主对象

同样,需要维护,因为所有 初始 需要找到访问 child 的地方 - 我们正在努力解决这个问题,因为进入该网站的入口路径不同
    @foreach(var m in ViewBag.Data){
@m.Id<nbsp></nbsp>
lock (m){
foreach(var d in m.Details){
@d.Id<nbsp></nbsp>
}
}
<br />
}

切换到 Entity Framework

这似乎仍然存在(有时 - 比 linq-sql 好得多)在特定数量的并行请求(核心 i7 上超过 50 个)下的“开放连接”问题 - 正如我提到的,它会在一段时间后消失,但我没有看到数据损坏了。

我们最终可能会完全切换到 EF,因为这似乎是唯一可行的路径 - 假设没有出现数据损坏 - 这将在我的实际项目中进行测试。

不过我并不乐观,因为 EF 数据上下文也不是线程安全的,而且我认为 EF 数据对象带有它们的上下文。 这可能是我唯一还没有答案的问题。

关于它为什么被破坏的理论

看起来像在 http 缓存中存储 LINQ-SQL 对象一样带有数据上下文。当多个线程稍后使用此上下文访问子对象时,会出现某种类型的并发问题,表现为临时连接问题或子对象的完全数据损坏。由于无法从 LINQ 对象断开/重新连接上下文,因此似乎唯一的建议是不要缓存需要延迟加载其子项的 LINQ 对象 - 我所做的大量谷歌搜索似乎没有给你那个忠告,实际上有时是相反的。

我已经上传了完整的项目(适用于 Visual Studio 2012 和 SQL Server 2012)

https://docs.google.com/file/d/0B8CQRA9dD8POb3U5RGtCV3BMeU0/edit?usp=sharing
和一个简单的 JMeter 脚本,它将通过并行请求访问您的本地机器:
https://docs.google.com/file/d/0B8CQRA9dD8POd1VYdGRDMEFQbEU/edit?usp=sharing

测试,启动站点并运行测试 - 然后触摸站点上的 web.config

最佳答案

LockApp.Models.DBDataContext db = new Models.DBDataContext();

var master = db.Masters.ToList();

您应该调用 db.ObjectTrackingEnabled = false在这两个调用之间。否则数据上下文将跟踪所有对象,以便将更改写回数据库。由于您正在缓存这些对象以供多个线程读取,因此您不希望这样做。 (即使在单线程情况下跟踪您不会更改的对象也更昂贵,因此在其他地方值得这样做)。

另外,使用 LoadWith急切地加载您可能想要访问这些缓存实体的任何属性,因此它们都加载在初始缓存线程上,而不是尝试访问它们的(可能是多个)线程。

关于asp.net - 缓存 LINQ-SQL 对象和 DataContext 线程安全,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17885906/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com