Is this to do with csla?
Old forum URL: forums.lhotka.net/forums/t/8054.aspx
Nemisis posted on Friday, November 27, 2009
Hi all, i am getting this error in my application when people try to edit a business object. I am using version 3.6.1 with .net 3.5. The stacktrace would suggest that the problem would lye within CanReadProperty, but when i look up this error online, people say that mscor.lib is something to do with user code not csla? We have many business objects, some of which have the same property names, which shouldnt be a problem. We are using managed fields as well.
Anyone got any clues?
Message : An item with the same key has already been added.
StackTrace : at
System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value,
Boolean add)
at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
at Csla.Core.BusinessBase.CanReadProperty(String propertyName)
at Csla.Core.BusinessBase.CanReadProperty(String propertyName, Boolean
throwOnFalse)
at Csla.Core.BusinessBase.GetProperty[P](PropertyInfo`1 propertyInfo,
NoAccessBehavior noAccess)
at Csla.Core.BusinessBase.GetProperty[P](PropertyInfo`1 propertyInfo)
at BSL.Library.Configuration.Module.Module.get_ClassName()
at BSL.Library.Configuration.Module.ModuleList.get_GetModule(String pName)
at BSL.Library.Core.CompanyItem.CanGetObject()
at BSL.Web.eContrack.Page_Load(Object sender, EventArgs e)
at System.Web.UI.Control.OnLoad(EventArgs e)
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint,
Boolean includeStagesAfterAsyncPoint)ajj3085 replied on Monday, November 30, 2009
If you're registerpropety calls are not setup correctly, this may happen. Are you using the overloads which take a System.Type as the first parameter? If you are, don't.Nemisis replied on Thursday, December 17, 2009
Hi,
No i am not using that overload, i do not pass the System.Type
Here is an example of one of my properties
Private Shared NameProperty As PropertyInfo(Of String) = RegisterProperty(New PropertyInfo(Of String)("Name"))
Public Property Name() As String
Get
Return GetProperty(NameProperty)
End Get
Set(ByVal value As String)
SetProperty(NameProperty, value)
End Set
End Property
The stacktrace seems to imply that the error is being caused within the CanReadProperty? The code for the CanReadProperty(propertyName) method. If i load this in BusinessBase (which is what my object inherits from) you will see
public virtual bool CanReadProperty(string propertyName)
{
bool result = true;
VerifyAuthorizationCache();
if (!_readResultCache.TryGetValue(propertyName, out result))
{
result = true;
if (AuthorizationRules.HasReadAllowedRoles(propertyName))
{
// some users are explicitly granted read access
// in which case all other users are denied
if (!AuthorizationRules.IsReadAllowed(propertyName))
result = false;
}
else if (AuthorizationRules.HasReadDeniedRoles(propertyName))
{
// some users are explicitly denied read access
if (AuthorizationRules.IsReadDenied(propertyName))
result = false;
}
// store value in cache
_readResultCache.Add(propertyName, result);
}
return result;
}
I have searched to posts about this on the forum and found this http://forums.lhotka.net/forums/post/19091.aspx
The forum is to do with another method called "SharedAuthorisationRules.GetManager()" but i wonder if the problem is happening here. Our servers are multi-core and if two cores try to execute
if (!_readResultCache.TryGetValue(propertyName, out result))
and they are both false, then both will execute
_readResultCache.Add(propertyName, result);
which i believe is where the error is. Shouldnt this all be wrapped in a lock?
ajj3085 replied on Thursday, December 17, 2009
Is your class subclassing another class which has its own property info objects?Nemisis replied on Thursday, December 17, 2009
no not this one
ajj3085 replied on Thursday, December 17, 2009
Hmm... I'm at a loss. Can you post all of the classes' code?
Nemisis replied on Friday, December 18, 2009
Its all very simple, i am using a lasy load as one property, which is below, but i guess there is nothing wrong with this is there?
The stacktrace suggests that there maybe a problem
Normal
0
false
false
false
EN-GB
X-NONE
X-NONE
MicrosoftInternetExplorer4
/* Style Definitions */
table.MsoNormalTable
{mso-style-name:"Table Normal";
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-qformat:yes;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:11.0pt;
font-family:"Calibri","sans-serif";
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:minor-latin;
mso-fareast-font-family:"Times New Roman";
mso-fareast-theme-font:minor-fareast;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:minor-latin;
mso-bidi-font-family:"Times New Roman";
mso-bidi-theme-font:minor-bidi;}
Message : An item with the same key has already been added.
StackTrace : at
System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value,
Boolean add)
at Csla.Core.BusinessBase.CanReadProperty(String propertyName)
at Csla.Core.BusinessBase.CanReadProperty(String propertyName, Boolean
throwOnFalse)
at Csla.Core.BusinessBase.GetProperty[P](PropertyInfo`1 propertyInfo,
NoAccessBehavior noAccess)
at Csla.Core.BusinessBase.GetProperty[P](PropertyInfo`1 propertyInfo)
at BSL.Library.Configuration.Database.get_ModuleList()
at BSL.Library.Scoring.ScorecardItem.CanGetObject()
at BSL.Web.ScoringDataEntry.ScoringWizard.Page_Load(Object sender, EventArgs e)
at System.Web.UI.Control.OnLoad(EventArgs e)
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint,
Boolean includeStagesAfterAsyncPoint)
Namespace Configuration
<Serializable()> _
Public Class Database
Inherits BusinessBase(Of Database)
#Region " Business Methods "
#Region " Basic "
Private Shared SamProperty As PropertyInfo(Of String) = RegisterProperty(New PropertyInfo(Of String)("Sam"))
Public ReadOnly Property Sam() As String
Get
Return GetProperty(SamProperty)
End Get
End Property
Private Shared NameProperty As PropertyInfo(Of String) = RegisterProperty(New PropertyInfo(Of String)("Name"))
Public Property Name() As String
Get
Return GetProperty(NameProperty)
End Get
Set(ByVal value As String)
SetProperty(NameProperty, value)
End Set
End Property
Private Shared ShortNameProperty As PropertyInfo(Of String) = RegisterProperty(New PropertyInfo(Of String)("ShortName"))
Public Property ShortName() As String
Get
Return GetProperty(ShortNameProperty)
End Get
Set(ByVal value As String)
SetProperty(ShortNameProperty, value)
End Set
End Property
Private Shared ImageProperty As PropertyInfo(Of Byte()) = RegisterProperty(New PropertyInfo(Of Byte())("Image"))
Public Property Image() As Byte()
Get
Return GetProperty(ImageProperty)
End Get
Set(ByVal value As Byte())
SetProperty(ImageProperty, value)
End Set
End Property
Private Shared ConfigurationProperty As PropertyInfo(Of String) = RegisterProperty(New PropertyInfo(Of String)("Configuration"))
Public Property Configuration() As String
Get
Return GetProperty(ConfigurationProperty)
End Get
Set(ByVal value As String)
SetProperty(ConfigurationProperty, value)
End Set
End Property
Private Shared ThemeProperty As PropertyInfo(Of String) = RegisterProperty(New PropertyInfo(Of String)("Theme"))
Public Property Theme() As String
Get
Return GetProperty(ThemeProperty)
End Get
Set(ByVal value As String)
SetProperty(ThemeProperty, value)
End Set
End Property
Private Shared LocaleProperty As PropertyInfo(Of String) = RegisterProperty(New PropertyInfo(Of String)("Locale"))
Public Property Locale() As String
Get
Return GetProperty(LocaleProperty)
End Get
Set(ByVal value As String)
SetProperty(LocaleProperty, value)
End Set
End Property
Private Shared ModuleListProperty As PropertyInfo(Of ModuleList) = RegisterProperty(New PropertyInfo(Of ModuleList)("ModuleList"))
Public ReadOnly Property ModuleList() As ModuleList
Get
If Not FieldManager.FieldExists(ModuleListProperty) Then
LoadProperty(ModuleListProperty, ModuleList.GetModuleList(ReadProperty(SamProperty)))
End If
Return GetProperty(ModuleListProperty)
End Get
End Property
#End Region
''' <summary>
''' Used this function to find a name. You must pass in the fullname, module and property name separated by a full stop. e.g. CompanyItem.Name
''' This function must be used to return a property within a module!
''' </summary>
''' <param name="pName"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function GetName(ByVal pName As String) As Name.Name
For Each lModule As [Module].Module In ModuleList
If pName.Contains(".") AndAlso lModule.ClassName = pName.Substring(0, pName.IndexOf(".")) Then
Return lModule.NameGroupList.GetName(pName.Substring(pName.IndexOf(".") + 1))
End If
Next
Throw New ArgumentException(pName & " does not match or contain the name of any module within this database.")
End Function
#End Region
#Region " Factory Methods "
Public Shared Function NewDatabase() As Database
Return DataPortal.Create(Of Database)()
End Function
Public Shared Function GetDatabase(ByVal pName As String) As Database
Return DataPortal.Fetch(Of Database)(New SingleCriteria(Of Database, String)(pName))
End Function
Private Sub New()
'Require use of factory methods
End Sub
#End Region
#Region " Data Access "
<RunLocal()> _
Protected Overrides Sub DataPortal_Create()
MyBase.DataPortal_Create()
End Sub
Private Overloads Sub DataPortal_Fetch(ByVal criteria As SingleCriteria(Of Database, String))
Using ctx = ConnectionManager(Of SqlClient.SqlConnection).GetManager(BSL.Library.Configuration.Current.Security)
Using cm = ctx.Connection.CreateCommand
..............
End Using
End Using
End Sub
<Transactional(TransactionalTypes.TransactionScope)> _
Protected Overrides Sub DataPortal_Insert()
' insert values
End Sub
<Transactional(TransactionalTypes.TransactionScope)> _
Protected Overrides Sub DataPortal_Update()
Using ctx = ConnectionManager(Of SqlConnection).GetManager(BSL.Library.Configuration.Current.Data)
If IsSelfDirty Then
Using cm = ctx.Connection.CreateCommand
..............
End Using
End If
FieldManager.UpdateChildren(Me)
End Using
End Sub
<Transactional(TransactionalTypes.TransactionScope)> _
Protected Overrides Sub DataPortal_DeleteSelf()
End Sub
#End Region
End Class
End Namespace
rasupit replied on Friday, December 18, 2009
Nemisis: Our servers are multi-core and if two cores try to execute
if (!_readResultCache.TryGetValue(propertyName, out result))
and they are both false, then both will execute
_readResultCache.Add(propertyName, result);
which i believe is where the error is. Shouldnt this all be wrapped in a lock?
Agree, this is more likely a race condition issue. I ran into this issue when using csla object with multi-threaded process on windows service. At that time took a shortcut and just remove the CanReadProperty since not really applicable in windows service.
This can be wrapped with lock, but locking will pay performance penalty. I think the better fix is to just let it override the value when propName already exist since the overriding value will be the same anyway. So the dictionary add should just look like:
_readResultCache[propertyName] = result;
Ricky
ajj3085 replied on Friday, December 18, 2009
Ricky is correct... either add locking or just always overwrite the value.
Csla isn't threadsafe by default, just like most of the .Net framework.
Nemisis replied on Friday, December 18, 2009
That is a much better suggestion thenthe lock, i didnt even think of that too be honest. Cheers guys!
Rocky, is there any chance this could be done, in future versions as i dont want to update our copy and then get the same error in a future version?
It appears that this change has been done in the CanWriteProperty
public virtual bool CanWriteProperty(string propertyName)
{
bool result = true;
VerifyAuthorizationCache();
if (!_writeResultCache.TryGetValue(propertyName, out result))
{
result = true;
if (this.AuthorizationRules.HasWriteAllowedRoles(propertyName))
{
// some users are explicitly granted write access
// in which case all other users are denied
if (!AuthorizationRules.IsWriteAllowed(propertyName))
result = false;
}
else if (AuthorizationRules.HasWriteDeniedRoles(propertyName))
{
// some users are explicitly denied write access
if (AuthorizationRules.IsWriteDenied(propertyName))
result = false;
}
_writeResultCache[propertyName] = result;
}
return result;
}
Copyright (c) Marimer LLC