Is assigning an object to itself a good idea? - asp.net

I have two classes, RecordSet and Record. RecordSet has a Generic List(Of Record).
I can add objects to the list by calling my RecordSet.AddRecord(ObjRecord) function, which returns RecordSet. When the list has a count of 200, some processing occurs and a new RecordSet object is returned, otherwise itself is returned and the application can carry on adding Record objects to the list.
My concern is that there will be 200 objects of RecordSet until garbage collection does it's sweep. Is this a good idea?
Public Class RecordSet
Private lstRecords As New List(Of Record)
Public Function AddRecord(SomeVariable) AS RecordSet
lstRecords.Add(New Record())
If lstRecords.Count = 200 Then
Me.ProcessTheRecords()
Return New RecordSet()
Else
Return Me
End If
End Function
Private Sub ProcessTheRecords()
'Do stuff in here
End Sub
Private Class Record
Public Sub New()
End Sub
End Class
End Class
Then in my application I call:
Dim objRecordSet AS New RecordSet
For Each VariableName In SomeList
objRecordSet = objRecordSet.AddRecord(VariableName)
Next
'Process the remaining objects in objRecordSet here.

First of all, this is really bad pratice, it's hard to follow the code for someone new and is a potential bug source. Instead of returning urself every time, change your design.
Change your function to this:
Public Sub AddRecord(SomeVariable)
lstRecords.Add(New Record()) <--- should't you be doing something with SomeVariable?!
If lstRecords.Count = 200 Then
Me.ProcessTheRecords()
end if
End Function
Private Sub ProcessTheRecords()
'Do stuff in here
Me.lstRecords.clear()
End Sub
Now AddRecord does exactly what it says it does - it adds a new record and modifies the recordSet. ProcessTheRecords does the processing, as its supposed to do, and if u need to clear the list container - oh well, just clear it.
I strongly recommed to read this wiki article about
Cohesion.
Just as a proposiontion, the AddRecord could be a function of return type Boolean, which indicates the success of the operation (maybe an error or exception can be raised by the processing function?).
It's much cleaner now, isn't it?

Related

DataContext Disposal error resolved but why?

I am working with VB.Net. I have resolved a behavior where the code returns with an error of "Cannot access a disposed object. Object name: 'DataContext accessed after Dispose.'."
My problem : I simply don't understand why it works !
Here is my code when happens the error :
Public Function Element_Import(ByVal id_element As Integer) As elements
Using db As New GlobalDataContext(MyConnexion)
Return (From element In db.elements
Select element
Where element.id_element = id_element).First
End Using
End Function
Then I try to retreave the data :
Dim myElement As elements = _MyConnection.Element_Import(id_element)
Dim myLocal As List(Of elements_localisation) = myElement.elements_localisation.ToList '-- ObjectDisposedException !
When I import my element then try to access sub-tables called "elements_localisation" and "elements_files" an ObjectDisposedException occures. That's fair as the DataContext is not available.
So I have done something different :
Public Function Element_ImportContext(ByVal id_element As Integer) As elements
Dim myElement As elements
Dim myElementLocalisation As New List(Of elements_localisation)
Dim myElementFiles As New List(Of elements_files)
Using db As New GlobalDataContext(MyConnexion)
myElement = (From element In db.elements
Select element
Where element.id_element = id_element).First
myElementLocalisation = myElement.elements_localisation.ToList
myElementFiles = myElement.elements_files.ToList
End Using
Return myElement
End Function
Then I try to retreave the data :
Dim myElement As elements = _MyConnection.Element_ImportContext(id_element)
Dim myLocal As List(Of elements_localisation) = myElement.elements_localisation.ToList '-- IT WORKS !
I really wanna understand what happened as, in both case, the DataContext is disposed.
Can anyone explain ?
When you write LINQ, you write a query to be executed against some data, that query is only executed when another part of the code needs the data.
In your case, you are returning the myElement variable because you have used .First(), which forces the query to be executed and the first item to be returned.
The properties elements_localisation and elements_files are likely to be virtual properties that are only loaded when you request them. (This is certainly the case with Entity Framework, I'm not sure what you're using here).
Your program will try to access the virtual property which will then go off to the data context to get the next bit of data you requested, but in your case the data context is disposed.
Your second approach works because you have used ToList() on the virtual properties, which forces the data to be retrieved then and there, before your data context has been disposed.

Concurrency issues when using a member variable for caching in ASP.NET

I'm using a static member to cache some data and I believe I'm having some kind of concurrency issue when the cached data is first populated. This problem is sporadic and seems to follow the recycling of the app pool (though often times there will not be a problem).
Initially my _CachedData member is null. When the FindAll() method is called it will use the cached data when _CachedData isn't null, otherwise it will load the data from the database. The _CachedData member is set to null whenever an insert, update, or delete is performed to the data, though this should never happen when the application is first loaded after an app pool recycle.
The error that I'm getting is an object reference error when I'm adding a TestClass object to result from _CachedData. I just don't see how this is possible since I'm performing a null check on _CachedData before I enter the for each loop.
Public Class TestClass
Private Shared _CachedData As Collection(Of TestClass) = Nothing
Sub New()
End Sub
Public Shared Function FindAll() As Collection(Of Content.TestClass)
Dim result As New Collection(Of TestClass)()
If _CachedData IsNot Nothing Then
' retrieve from cache
For Each tc As TestClass In _CachedData
result.Add(CType(tc.MemberwiseClone(), TestClass))
Next
Else
result = GetFromDatabase()
' save to cache
_CachedData = New Collection(Of TestClass)()
SyncLock _CachedData
' make sure the cache wasn't populated while we were aquiring the lock
If _CachedData.Count = 0 Then
For Each tc As TestClass In result
_CachedData.Add(CType(tc.MemberwiseClone(), TestClass))
Next
End If
End SyncLock
End If
Return result
End Function
faf
For starters, you should not actually be locking on the object itself, as you will be making changes to that object. You should update your code as such:
Private Shared _CachedData As Collection(Of TestClass) = Nothing
Private Shared ReadOnly _CachedDataLock As Object = New Object()
Public Shared Function FindAll() As Collection(Of Content.TestClass)
Dim result As New Collection(Of TestClass)()
If _CachedData IsNot Nothing Then
' retrieve from cache, using a local instance to avoid race-conditions
Dim localCachedData As Collection(Of TestClass) = _CachedData
For Each tc As TestClass In localCachedData
result.Add(CType(tc.MemberwiseClone(), TestClass))
Next
Else
result = GetFromDatabase()
' save to cache
SyncLock _CachedDataLock
' make sure the cache wasn't populated while we were aquiring the lock
If _CachedData Is Nothing Then
_CachedData = New Collection(Of TestClass)()
For Each tc As TestClass In result
_CachedData.Add(CType(tc.MemberwiseClone(), TestClass))
Next
End If
End SyncLock
End If
Return result
End Function

Same Function Running Parallel will override values?

I am Using same function from many places...
for example below function
Public Sub getUser(ByVal Name as string)
dim myName=Name
.......
insert(myName)
End Sub
I am using this function from so many places...
I have doubt should this function override this myName values with latest function call?
Suppose i called getUser("ABC") so value of myName is now ABC now sudden all call getUser("XYZ") so at insert(myName) will it insert("ABC") or insert("XYZ")??
I need it to be insert("ABC") and then insert("XYZ")
You can use locking to make sure only one thread does something at a time
//declare an object for locking
Dim lockObjcect As New [Object]()
Public Sub getUser(ByVal Name as string)
SyncLock lockObjcect
dim myName=Name
.......
insert(myName)
End SyncLock
End Sub
With the locking, now only one thread will be able to execute the code between SyncLock and End SyncLock this means First ABC will be inserted and then XYZ will be inserted

asp.net multithreading with synclock

i have some test code which i run at every load of a page in my asp.net website
this is the code
Sub TryThreads()
Dim t1 = New Thread(AddressOf TryLock)
t1.Priority = ThreadPriority.Lowest
t1.Start()
Dim t2 = New Thread(AddressOf TryLock)
t2.Priority = ThreadPriority.Lowest
t2.Start()
End Sub
Sub TryLock()
Dim LockObject = New Object
SyncLock LockObject
DoTrace("entered locker")
For x = 0 To 10000
Next
DoTrace("exiting locker")
End SyncLock
DoTrace("exited locker")
End Sub
the "dotrace" simply add a record to a log table in the db
now the right result would be that i should have the entries in the db in order "entered","exiting","exited"
but actually when i look in the db i see first 2 "entered" then 2 "exiting" etc.
meaning that the multithreading is working ok, but not the synclock
is that correct?
and how can this be fixed?
the real code will be adding records to the db and might be called from several pages of different sessions, but the same code must not run twice concurrently
i do appreciate anybodys help
thank you very much!!!
EDIT:
in response to Sashas wonderful post i changed my code to a class (it was in a module) and now it looks like this:
Public Class CheckClass
Property LockObject As Object
Get
If HttpRuntime.Cache("CheckSessionsLock") Is Nothing Then HttpRuntime.Cache("CheckSessionsLock") = New Object
Return HttpRuntime.Cache("CheckSessionsLock")
End Get
Set(ByVal value As Object)
If value Is Nothing Then
HttpRuntime.Cache.Remove("CheckSessionsLock")
Else
HttpRuntime.Cache("CheckSessionsLock") = value
End If
End Set
End Property
Sub TryThreads()
Dim t1 = New Thread(AddressOf TryLock)
t1.Priority = ThreadPriority.Lowest
t1.Start()
Dim t2 = New Thread(AddressOf TryLock)
t2.Priority = ThreadPriority.Lowest
t2.Start()
End Sub
Sub TryLock()
SyncLock LockObject
DoTrace("entered locker")
For x = 0 To 10000
Next
DoTrace("exiting locker")
End SyncLock
DoTrace("exited locker")
End Sub
End Class
now it works 80-90% of the time.
on page load i have:
Dim cc = New CheckClass
cc.TryThreads()
if i open multiple pages at once, they still clash some times. but if i'm correct, the issue is now not with the synclock as much as with the httpruntime.cache, because when using a standard property, on one page, the code works 100%.
so how can i make sure that 2 threads, even from totally different sessions never run the trylock simultaneously?
thank you all for helping out
You are creating a new object instance when the TryLock method is called, and use that for locking. If you want mutual exclusion between the two threads, you need to use a common object instance for locking, e.g. a static member of your class or a parameter that you pass to both threads.

Must I use parameters with an ObjectDataSource update?

I have a business object that I've been using, which has a bunch of properties and a Save method, which inserts/updates to the database. The save method is NOT status, so the object needs to be instantiated, and the properties for the DB update/insert get pulled from the object.
Now I'm trying to bind the object to a FormView with the ObjectDataSource. I have it working so it instantiates based on the QueryString parameter, no problem, and populates the textboxes just fine. The UpdateMethod I have set to the Save function. Now it gets stuck.
It seems the ObjectDataSource needs a method with all the fields/properties/textboxes as parameters. I would have thought it would update the object's properties and then call the parameterless Save function. Is this wishful thinking?
Do I now need to change my Save function to include parameters, and change all the instances where it's getting used to this new method, just for this reason?
Thanks
Sean
Unfortunatly it does require params.
I overloaded my insert/update methods to include a few params. Attach the ObjectDataSource to the method with params.
The overloaded Update method calls the original Update method saving all the data. Seems kind of hackish to me, but it works.
Public Sub Update()
Dim isUpdated As Boolean = False
sql = "UPDATE AudioFiles SET Title = #Title, [desc] = #desc, Active = #Active WHERE fileID = #fileID"
conn = New SqlConnection(connString)
conn.Open()
...
End Sub
Public Sub Update(ByVal upFileID As Integer, ByVal upTitle As String, ByVal upDesc As String, ByVal upActive As Boolean)
Dim isUpdated As Boolean = False
Dim audioFile As New AudioFiles(fileID)
If Len(upTitle) > 0 Then
_title = title
End If
...
audioFile.Update()
End Sub

Resources