Public Enum ERight
ECanInvite = 0
ECanCreate = 1
ECanDelete = 2
etc...
End Enum
Public Enum EUserType
EAdministrator = 0
EPartner_level1 = 1
EPartner_level2 = 2
ENormalUser = 3
...etc
End Enum
此{@ 1}}下方的子信息有时会引发此错误:rights.add
这怎么可能?
An item with the same key has already been added.
答案 0 :(得分:0)
是否有什么阻止你的代码同时在不同的地方调用initRoles?
因为它是一个共享变量,所以一个调用可以进入,清除变量然后另一个调用可以进入,清除变量然后它们都开始向同一个引用添加值。取决于内部处理所有阻止的方式。
让我想起Dijkstra's Semaphores :)这让我退缩了。
无论如何,我可以建议将这个块包装成某个东西,以表明它是关键代码,并且每次应该一次运行吗?我在VB中有点生疏,但SyncLock语句看起来像是票证(链接中来自MSDN的代码和示例)。
答案 1 :(得分:0)
initRoles
是一种实例方法,而rights
是共享/静态字典。
在WebApplication中共享意味着它在所有http请求中共享,因为它们是不同的线程。可能会发生这样的情况:initRoles
来自不同的请求,并且所有请求都在同时更改rights
。
你没有显示你在哪里调用initRoles
,但我确信它是从HttpContext调用的。
如果您希望共享字典,还应该共享initRoles
。
例如,您可以使用类CRights
的共享构造函数来调用它:
Shared Sub New()
initRoles()
End Sub
Private Shared Sub initRoles()
rights = New Dictionary(Of EUserType, List(Of ERight))
' blah... '
End sub
这种方法要求字典在应用程序生命周期中仅初始化一次可能需要或不需要的内容。您也可以将方法设置为public,以便重新初始化它。来自您的用户/角色管理。
答案 2 :(得分:0)
发生错误是因为您在多线程环境中编写了Shared
对象。
修复是添加一个锁(在这种情况下,对Clear()
的调用变得多余):
Private Shared rights As Dictionary(Of ws_garuda.EUserType, List(Of ERight)) = Nothing
Private Shared rightsLock As Object = New Object()
Private Sub initRoles()
SyncLock rightsLock
rights = New Dictionary(Of EUserType, List(Of ERight))
' Set all rights to false for all roles
For Each usertype As EUserType In DirectCast([Enum].GetValues(GetType(EUserType)), EUserType())
rights.Add(usertype, New List(Of ERight))
Next
End SyncLock
End sub
答案 3 :(得分:0)
@RickNZ建议的修复是不够的。 Dictionary类不是线程安全的,任何使其使用线程安全的同步都需要读者和编写者。 .NET 4.0有ConcurrentDictionary类,there exist implementations for .NET 2.0。
对于您的直接问题,最简单的解决方案是避免使用线程安全的Dictionary类,它是在将共享字典分配给共享的rights
字段之前完全构造它。最简单的方法是将InitRoles更改为返回Dictionary
Private Shared rights As Dictionary(Of ws_garuda.EUserType, List(Of ERight)) = InitRoles()
Private Shared Function InitRoles() As Dictionary(Of ws_garuda.EUserType, List(Of ERight))
Dim tempRights As Dictionary(Of ws_garuda.EUserType, List(Of ERight))
tempRights = New Dictionary(Of EUserType, List(Of ERight))
' Set all rights to false for all roles
For Each usertype As EUserType In DirectCast([Enum].GetValues(GetType(EUserType)), EUserType())
tempRights.Add(usertype, New List(Of ERight))
Next
InitRoles = tempRights
End Function
但是,即使这样也不会使您的代码成为线程安全的。还需要同步对字典中List(Of ERight)
值的任何访问。并且您的代码暗示此List不是只读的(它被初始化为空列表,这可能意味着它将被应用程序中其他地方的代码修改)。
我的建议是避免除了不可变对象之外的所有共享字段,除非你真的了解多线程。