gpt4 book ai didi

c# - 如何避免 DataView.ToTable() 中的 KeyNotFoundException?

转载 作者:太空宇宙 更新时间:2023-11-03 21:48:16 28 4
gpt4 key购买 nike

我正在处理遗留的 ASP.NET 代码库,该代码库有 一些 信息存储在 DataTable 对象的缓存中(通过企业库)。它是一个多用户、Intranet 环境,在 .NET 4.0 中运行。生产中存在一个问题,它指向一个先前的“已修复”问题作为其可能的根本原因:在调用 DataView.ToTable() 时发生的 KeyNotFoundException。此特定代码是验证的一部分,可以在从应用程序中的大多数页面加载页面期间发生。

var table = GetSomeDataTableFromCache();
var view = table.DefaultView;
view.RowFilter = "Foo = 'Bar'";
var filteredTable = view.ToTable();

这是代码的简化。发生的事情是,在过去的 long 天里,这段代码显然抛出了上述异常。先前的开发人员通过捕获并吞下异常并返回 null 来“修复”它。此行为是已曝光的其他问题的推定罪魁祸首。

我曾尝试重现原始异常,但没有成功。我觉得如果我能重现它,我就能理解它并尝试形成一个比忽略它更好的原始问题的解决方案,从而也避免了忽略它 已创建。

我注意到 Google 搜索 DataView.ToTable() + KeyNotFoundException 产生的结果表明验证列名是否存在。示例位于此处:

http://forums.asp.net/t/1695676.aspx/1

但是,我观察到当将 RowFilter 设置为无效的列名时,该行会发生 EvaluateException。我还尝试通过使用空表以及使用产生空结果的过滤器来解决错误。每个场景都被证明是正常的。

那么,如果不是建议的无效列名,KeyNotFoundException 会在哪里出现?一旦我们知道它是如何发生的,我们又该如何避免呢?

最佳答案

请注意,ASP.NET 本质上是一个多线程系统。 DataView被记录为读取线程安全,但不是写入。考虑这一点很重要。

在给出的片段中,从缓存中提取 DataTable 的实例并访问其 DefaultView 以进行过滤并写入新的 DataTable 是可能会出现不安全的情况,这不是因为过滤器中的列,而是可能同时执行相同代码的多个线程。

作为实现细节,DataView 使用了多个内部状态位。在 ToTable() 执行期间,涉及多个非本地状态。特别是,有一个以 DataRow 为键的字典字段,还有一个用作键的 DataRow 字段!这个字典被清除,添加到,行被引用行的值覆盖,然后设置为 null,这都是过程的一部分。当多个线程同时执行此操作时,一个线程正在覆盖并使另一个线程所依赖的状态无效并不是不可想象的。这可能会导致问题中提到的异常,以及其他潜在的有害后果。

无论如何,让我们尝试使用代码片段作为起点来重现问题,同样是在可以利用多次执行的环境中。

static void Main()
{
var table = new DataTable();
table.Columns.Add("Foo");
table.Columns.Add("ID", typeof(int));

for (int i = 0; i < 100; i++)
{
table.Rows.Add(i.ToString(), i);
}

for (int j = 0; j < 100; j++)
{
Enumerable
.Range(0,100)
.AsParallel()
.ForAll(item => ExecuteToTable(table, item));
}
}

static void ExecuteToTable(DataTable table, int item)
{
var view = table.DefaultView;
view.RowFilter = string.Format("Foo = '{0}'", item);
var filteredTable = view.ToTable();
}

这会产生异常吗?运行一下看看!它可能需要多次执行,但如果运行代码的机器与我的相似,则不需要多次。 (通过并行查询循环,我真的只需要一次。)

我已在 LinqPad 中运行此代码,并且已生成“所需”异常。由于这是一个并行查询执行,它将被包装在一个 AggregatedException 中,但 InnerException 将讲述这个故事。

KeyNotFoundException: The given key was not present in the dictionary.

at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at System.Data.DataView.CopyTo(DataRowView[] array, Int32 index)
at System.Data.DataView.GetEnumerator()
at System.Data.DataView.ToTable(String tableName, Boolean distinct, String[] columnNames)
at System.Data.DataView.ToTable()

所以我们已经复制了它,并希望能够理解它。怎么避免呢?有几种可能的解决方案,根据您的用例,一些解决方案比其他解决方案更可口,范围从全面重写到只是细微的更改。

你可以

使用 table.Copy() 复制 DataTable 以访问 DefaultView。这将为每个请求提供自己的表格(在上面的代码片段中)。但是,如果表很大,复制可能会很昂贵。尝试使用 Copy() 运行上面的重现代码,看看是否避免了异常。

完全避免使用 DataView。 Linq 对数据表也很有用。下面的代码片段可用于生成过滤后的 DataTable 输出。但是,另请注意,如果没有行通过过滤器,CopyToDataTable() 可能会抛出它自己的 异常。如果有可能发生这种情况,请在调用最后一部分之前拆分代码并检查结果(使用 .Any())。与 DataView 相比的另一个缺点是,如果您使用可用的 ToTable,使用 DataView 允许您指定希望包含在输出表中的列> 过载。

var filteredTable
= table.AsEnumerable()
.Where(row => string.Equals(row.Field<string>("Foo"), item.ToString(), StringComparison.InvariantCultureIgnoreCase))
.CopyToDataTable();

当然,您可以进一步探索重新设计代码,将您自己的线程安全措施添加到缓存策略等,尽管这些将花费更多时间来实现改变实际表的过滤方式。

关于c# - 如何避免 DataView.ToTable() 中的 KeyNotFoundException?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15801934/

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