【发布时间】:2016-10-12 16:50:27
【问题描述】:
这个问题是关于 .Net Framework 4.5 MVC Web 应用程序的。
我有一段我们继承并使用多年的代码块,它通常将 DataTable 转换为 List
原始代码
private static Dictionary<Type, IList<PropertyInfo>> typeDictionary = new Dictionary<Type, IList<PropertyInfo>>();
public static IList<PropertyInfo> GetPropertiesForType<T>()
{
//variables
var type = typeof(T);
//get types
if (!typeDictionary.ContainsKey(typeof(T)))
{
typeDictionary.Add(type, type.GetProperties().ToList());
}
//return
return typeDictionary[type];
}
那里没有什么令人兴奋的事情,它只是确保 typeDictionary 尚未包含键(类型)并将其添加到字典中(键=类型,值=属性),以便我们以后可以访问它们。
我们通常将此代码用于任何类型的“模型”对象,但对于这个特定示例,这是在 2 个不同场合给我带来麻烦的那个。
模型对象
public class GetApprovalsByUserId
{
// constructor
public GetApprovalsByUserId()
{
TicketId = 0;
ModuleName = string.Empty;
ModuleIcon = string.Empty;
ApprovalType = string.Empty;
VIN = string.Empty;
StockNumber = string.Empty;
Year = 0;
Make = string.Empty;
Model = string.Empty;
Services = string.Empty;
RequestedDate = DateTime.MinValue;
}
// public properties
public int TicketId { get; set; }
public string ModuleName { get; set; }
public string ModuleIcon { get; set; }
public string ApprovalType { get; set; }
public string VIN { get; set; }
public string StockNumber { get; set; }
public int Year { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public string Services { get; set; }
public DateTime RequestedDate { get; set; }
}
同样,在那个特定的模型类中没有什么真正重要的事情发生,与我们在任何其他类中使用的没有什么不同。
就像我说的那样,我们在多个项目中普遍使用此代码,并且从未遇到过问题,但在过去一天的 2 次不同的情况下,我们让它抛出了以下异常:
已添加具有相同密钥的项目。
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at Utilities.Extensions.GetPropertiesForType[T]()
at Utilities.Extensions.ToObject[T](DataRow row)
at Utilities.Extensions.ToList[T](DataTable table)
如果有帮助,您可以在此处查看扩展方法所在的完整 Extensions.cs 类(静态):
https://pavey.azurewebsites.net/resources/Extensions.txt
我的问题是:
考虑到代码已经在进行 !typeDictionary.ContainsKey(typeof(T)) 检查,它怎么可能通过该测试,却失败了typeDictionary.Add(type, type.GetProperties().ToList()); 调用?
为什么会如此零星?它似乎 99% 的时间都在工作,使用相同的代码、相同的类(GetApprovalsByUserId,如上所示),并且在任何其他项目或任何其他模型类中从未失败过。
我们无法在任何环境中使用完全相同的代码、模型、数据或其他完全相同的设置重现此问题,因此不知道如何再保护此代码比现在要好。
我的一个想法是将代码更改为:
提议的代码更改
private static Dictionary<Type, IList<PropertyInfo>> typeDictionary = new Dictionary<Type, IList<PropertyInfo>>();
public static IList<PropertyInfo> GetPropertiesForType<T>()
{
//variables
var type = typeof(T);
IList<PropertyInfo> properties = null;
//get types
try
{
if (!typeDictionary.ContainsKey(type))
{
typeDictionary.Add(type, type.GetProperties().ToList());
}
}
catch
{
}
// try get value
typeDictionary.TryGetValue(type, out properties);
// return
return properties;
}
但是由于我一开始就无法重现该错误,所以我也不完全确定这是否是防弹的。我的想法是 ContainsKey 有点奇怪,特别是使用 typeof(T) 作为“键”时,它允许它在奇怪的情况下通过测试,而实际上它不应该通过,但是 Add 失败,因为它知道密钥已经存在。因此,如果我尝试/捕获它,如果 ContainsKey 错误地告诉我它不存在,而实际上是,则 Add 仍然会失败,但我会捕获它并继续前进,然后我可以 TryParse 获取值出来了,一切都会好起来的。
感谢任何想法、想法,或具体如何使用上面显示的原始代码重现问题,并建议改进以保护它。
【问题讨论】:
-
Given the fact that the code is already doing a !typeDictionary.ContainsKey(typeof(T)) check, how is it possible that it could ever pass that test, yet fail on the typeDictionary.Add(type, type.GetProperties().ToList()); call?-- 比赛条件? -
Why would it be so sporadic? It seemingly works 99% of the time, using the same code, the same class (GetApprovalsByUserId, shown above), and never has failed otherwise in any other project or any other model class.-- 比赛条件? -
绝对是竞争条件。您需要在检查密钥是否已存在并插入它的代码周围
lock -
罗伯特和马特,对此非常有帮助的见解。我想在代码更改之前/之后没有很好的方法来模拟/测试它以证明它排除了合理怀疑?
-
哦,模拟竞态条件非常容易,只是不在您的应用程序中。下面的大部分代码都是正统的,可以放心使用,但如果你真的需要证明,把它放在一个小的控制台应用程序中,然后抛出几个线程。
标签: c# .net generics dictionary static