FieldManager.UpdateChildren(Me) not calling Child_Update

FieldManager.UpdateChildren(Me) not calling Child_Update

Old forum URL: forums.lhotka.net/forums/t/7035.aspx


alex.williamson@grampianfasteners.com posted on Tuesday, June 02, 2009

Hi All,

I've been searching the forums and the net and haven't found the answer to the following couple of questions. If you know where they are, please accept my apologies and link me there - I expect the answers to be simple to CSLA experts. I am using the latest version of CSLA and I am following the 2008 VB.net book. I thought I had solved the following problem, but now it does not seem to work, and I've no idea what I've changed.

I have the following objects:
Report (BusinessBase)
-> AffectedDepartmentsList (BusinessListBase)
---> AffectedDepartment (BusinessBase)

Root object (Report) which holds a list of affected departments (AffectedDepartmentsList). The list of affected departments holds affected departments. I need a user (when editing the Report) to be able to add an AffectedDepartment to the AffectedDepartmentsList.

When I call FieldManager.UpdateChildren(Me) in the (root) Report BO, I expect the FieldManager.UpdateChildren function to go through and apply updates to each child. The Report (root BO) updates/inserts itself fine and calls the FieldManager.UpdateChildren(Me) method, but the update never gets to Child_Update in AffectedDepartment.

From the book, I expect the objects to be syonymous to Project (Report), ProjectResources (AffectedDepartmentsList) and ProjectResource (AffectedDepartment).

Using the BO VB.net 2005 book (is that CSLA 2.0?) on page 435, in the child root (AffectedDepartmentsList) I would have created a:

Friend Sub Update(ByVal report As Report)
' call deleteself on each item in deletedlist
' then
' call insert on each new object in me that's new, and update otherwise
' etc.
End Sub

I've read around and I thought that the FieldManager.UpdateChild(Me) from Report would go through the properties and call update on each item and update children on each list as necessary - is this where I'm going wrong? Do I need to implement a Child_Update method in the child list (AffectedUserList)?

I tried experimenting by putting a Child_Update in the AffectedDepartmentsList and this was called by the field manager - but this did not follow the old code, for example child.insert was not available.

I'll try and explain it below with some source from the project:

Report:
_
Public Class Report
Inherits BusinessBase(Of Report)

'The property:
Private Shared AffectedDepartmentsProperty As PropertyInfo(Of AffectedDepartmentList) = RegisterProperty(New PropertyInfo(Of AffectedDepartmentList)("AffectedDepartments", "Affected Departments"))
Public ReadOnly Property AffectedDepartments() As AffectedDepartmentList
Get
Return GetProperty(AffectedDepartmentsProperty)
End Get
End Property

'... further down:

_
Protected Overrides Sub DataPortal_Insert()
Using cn As New SqlConnection(My.Resources.Connection)
cn.Open()
Using cm As SqlCommand = cn.CreateCommand
cm.CommandText = "AddReport"
DoInsertUpdate(cm)
End Using
End Using
End Sub

Private Sub DoInsertUpdate(ByVal cm As SqlCommand)
cm.CommandType = CommandType.StoredProcedure
With cm
'stored query code for Report creation (insert statement)

FieldManager.UpdateChildren(Me)
End With
End Sub

End Sub

This is all the code for my AffectedDepartmentList class. I followed the ProjectResources code in the 2008 book to achieve this (p.g. 551-552). Am I expecting too much of the fieldmanager to call update on the children?

Imports Csla
Imports Csla.Data
Imports Csla.Security
Imports System.Data.SqlClient


_
Public Class AffectedDepartmentList
Inherits BusinessListBase(Of AffectedDepartmentList, AffectedDepartment)


#Region "Business Methods"
Public Sub Assign(ByVal DepartmentId As Integer)
If Not Contains(DepartmentId) Then
Dim item As AffectedDepartment = _
AffectedDepartment.GetAffectedDepartment(DepartmentId)
Me.Add(item)
Else
Throw New InvalidOperationException("User already in the list")
End If
End Sub
Public Function GetItem(ByVal DepartmentId As Integer) As AffectedDepartment
Return Me.SingleOrDefault(Function(ae) ae.Id = DepartmentId)
End Function

Public Overloads Sub Remove(ByVal userid As Integer)
For Each item As AffectedDepartment In Me
If item.Id = userid Then
Me.Remove(item)
Exit For
End If
Next
End Sub
Public Shadows Function Contains(ByVal userid As Integer) As Boolean
For Each item As AffectedDepartment In Me
If item.Id = userid Then
Return True
End If
Next
Return False
End Function
Public Shadows Function ContainsDeleted(ByVal userid As Integer) As Boolean
For Each item As AffectedDepartment In Me.DeletedList
If item.Id = userid Then
Return True
End If
Next
Return False
End Function
#End Region
#Region "Factory Methods"
Friend Shared Function NewAffectedDepartmentList() As AffectedDepartmentList
Return DataPortal.CreateChild(Of AffectedDepartmentList)()
End Function
Friend Shared Function GetAffectedDepartmentList(ByVal dr As SafeDataReader) As AffectedDepartmentList
Return DataPortal.FetchChild(Of AffectedDepartmentList)(dr)
End Function
Private Sub New()
'require use of factory methods
End Sub
#End Region
#Region "Data Access"
Private Sub Child_Fetch(ByVal dr As SafeDataReader)
Me.RaiseListChangedEvents = False
While dr.Read
Me.Add(AffectedDepartment.GetAffectedDepartment(dr))
End While
Me.RaiseListChangedEvents = True
End Sub

#End Region

End Class

And the code/pseudo code for the AffectedDepartment class:

Imports Csla
Imports Csla.Data
Imports System.Data.SqlClient

_
Public Class AffectedDepartment
Inherits Csla.BusinessBase(Of AffectedDepartment)

#Region "Business Methods"
Private Shared IdProperty As PropertyInfo(Of Integer) = RegisterProperty(New PropertyInfo(Of Integer)("Id"))
Private Shared NameProperty As PropertyInfo(Of String) = RegisterProperty(New PropertyInfo(Of String)("Username"))
Private _lastChanged(8) As Byte

Public ReadOnly Property Id() As Integer
Get
Return GetProperty(IdProperty)
End Get
End Property
Public ReadOnly Property Name() As String
Get
Return GetProperty(NameProperty)
End Get
End Property
#End Region

#Region "Factory Methods"
Friend Shared Function AffectedDepartment(ByVal DepartmentId As Integer)
Return DataPortal.CreateChild(Of AffectedDepartment)(DepartmentId)
End Function
Friend Shared Function GetAffectedDepartment(ByVal dr As SafeDataReader)
Return DataPortal.FetchChild(Of AffectedDepartment)(dr)
End Function
Friend Shared Function GetAffectedDepartment(ByVal DepartmentId As Integer)
Return DataPortal.FetchChild(Of AffectedDepartment)(DepartmentId)
End Function
Private Sub New()
MarkAsChild()
End Sub
#End Region

#Region "Data Access"
Public Shadows Sub Child_Create(ByVal DepartmentId As Integer)
Dim tempDepartment As Department = Department.GetDepartment(DepartmentId)
LoadProperty(IdProperty, tempDepartment.Id)
LoadProperty(NameProperty, tempDepartment.Name)
End Sub
Private Shadows Sub Child_Fetch(ByVal DepartmentId As Integer)
Dim tempDepartment As Department = Department.GetDepartment(DepartmentId)
LoadProperty(IdProperty, tempDepartment.Id)
LoadProperty(NameProperty, tempDepartment.Name)
End Sub
Private Shadows Sub Child_Fetch(ByVal dr As SafeDataReader)
With dr
LoadProperty(IdProperty, .GetInt32("Id"))
LoadProperty(NameProperty, .GetString("Name"))
.GetBytes("LastChanged", 0, _lastChanged, 0, 8)
End With
MarkOld()
End Sub
Private Sub Child_Insert(ByVal report As Report)
Using cn As SqlConnection = New SqlConnection(My.Resources.Connection)
cn.Open()
Using cm As SqlCommand = cn.CreateCommand
cm.CommandText = "AddAffectedDepartment"
DoAddUpdate(cm, ReadProperty(IdProperty), report)
End Using
End Using
End Sub
Private Shadows Sub Child_Update(ByVal report As Report)
Using cn As SqlConnection = New SqlConnection(My.Resources.Connection)
Using cm As SqlCommand = cn.CreateCommand
cm.CommandText = "UpdateAffectedDepartment"
cm.Parameters.AddWithValue("@lastChanged", _lastChanged)
DoAddUpdate(cm, ReadProperty(IdProperty), report)
End Using
End Using
End Sub
Private Sub DoAddUpdate(ByVal cm As SqlCommand, ByVal departmentid As Integer, ByVal report As Report)
cm.CommandType = CommandType.StoredProcedure
cm.Parameters.AddWithValue("@departmentid", departmentid)
cm.Parameters.AddWithValue("@reportid", report.Id)
Dim param As New SqlParameter("@newlastchanged", SqlDbType.Timestamp)
param.Direction = ParameterDirection.Output

cm.Parameters.Add(param)
cm.ExecuteNonQuery()

_lastChanged = CType(cm.Parameters("@newlastchanged").Value, Byte())
End Sub

#End Region
End Class

Sorry for the extremely long post.

Alex

alex.williamson@grampianfasteners.com replied on Tuesday, June 02, 2009

I should probably mention this is the code I am testing with: Dim report As report = report.Newreport() report.Description = "new report for testing" report.AffectedDepartments.Assign(1) Dim cl As report = report.Clone cl.Save() This does enough to get the report to initialise saving, but posted to show I'm using the Assign to add the item into the collection (which works fine and I can see it's in the AffectedDepartmentsList in the Report when I try to save the report).

JoeFallon1 replied on Tuesday, June 02, 2009

Did you read page 405 of the Vb 2008 book?
Then pp 427 -428.
Then pp 154-155.
It explains it all pretty clearly.

Bottom line:
1. the child collection needs no code for this. It is all in the Base class.

2. Each child BO needs Child_Update, Child_Insert and Child_DeleteSelf methods.

Not sure why you would look at the 2005 book for child code - things have changed!

Joe

alex.williamson@grampianfasteners.com replied on Wednesday, June 03, 2009

Thanks for your reply.

I've re-read the pages you suggested, thank you. I am now sure that that I have created the objects as described in the book. I've not got an update child in the child list.

I can't see any code that is incorrect, I've implemented the methods already, they're just not being called as far as I can tell.

From my posted code above, I've got the Child_CRUD methods up there.

I've tried changing the Get function to call CreateChild instead, but this doesn't make a difference.

Friend Shared Function GetAffectedDepartmentList(ByVal dr As SafeDataReader) As AffectedDepartmentList
Return DataPortal.CreateChild(Of AffectedDepartmentList)(dr)
End Function

Am I missing something blindingly obvious you can see?

alex.williamson@grampianfasteners.com replied on Thursday, June 04, 2009

The Solution was a mixture of needing to call NewAffectedDepartment (which would call DataPortal.CreateChild which set the child item to new automatically) and a corrupt DLL.

Thank you for looking at it anyway.

Copyright (c) Marimer LLC