Base Classes & Partial ClassesBase Classes & Partial Classes
Old forum URL: forums.lhotka.net/forums/t/4963.aspx
Pieter posted on Wednesday, June 11, 2008
Hi All,
How do I implement a base class, that keeps all the properties and generated code, and then inherit from that class where I keep all the custom methods?
Also, how would you handle any child objects within that class e.g. Order with Order Details?
Thanks!
JoeFallon1 replied on Wednesday, June 11, 2008
Here is a sample root object which may help you get the idea:
<Serializable()> _
Public MustInherit Class CostcenterGen
Inherits MyBusinessBase(Of Costcenter)
#Region
" Class Variable Declarations "
Protected mCostcode As String = ""
Protected mCcname As String = ""
Protected mStatus As String = ""
Protected mUserid As Long = 0
Protected mTstamp As New Csla2.SmartDate
#End
Region
#Region
" Constructor "
Protected Sub New()
'require use of factory methods
End Sub
#End
Region
#Region
" Business Properties and Methods "
Public Overridable Property Costcode() As String
Get
Return mCostcode
End Get
Set(ByVal Value As String)
If mCostcode <> Value Then
mCostcode = Value
PropertyHasChanged("Costcode")
End If
End Set
End Property
etc.
#Region
" Validation Rules "
Protected Overrides Sub AddBusinessRules()
'Costcode
ValidationRules.AddRule(AddressOf StringRequired, New MyRuleArgs("Costcode"))
ValidationRules.AddRule(AddressOf StringMaxLength, New MaxLengthRuleArgs("Costcode", 15))
ValidationRules.AddRule(AddressOf IsValidSyntax, New MyRuleArgs("Costcode"))
ValidationRules.AddRule(AddressOf IsValidCodeSyntax, New MyRuleArgs("Costcode"))
ValidationRules.AddRule(AddressOf RecordDoesNotExist, New RecordExistsRuleArgs("Costcode", "costcenter", "", True), 1)
'Ccname
ValidationRules.AddRule(AddressOf StringMaxLength, New MaxLengthRuleArgs("Ccname", 60))
ValidationRules.AddRule(AddressOf IsValidSyntax, New MyRuleArgs("Ccname"))
'Status
ValidationRules.AddRule(AddressOf StringMaxLength, New MaxLengthRuleArgs("Status", 1))
ValidationRules.AddRule(AddressOf ContainsValidValue, New ValidValuesRuleArgs("Status", GetValidStatusValues))
ValidationRules.AddRule(AddressOf IsValidSyntax, New MyRuleArgs("Status"))
'Tstamp
ValidationRules.AddRule(AddressOf DateRequired, New MyRuleArgs("Tstamp"))
End Sub
Public Overridable Function GetValidStatusValues() As String()
Return New String() {"A", "D"}
End Function
#End
Region
#Region
" Factory Methods "
Public Shared Function NewCostcenter(ByVal costcode As String) As Costcenter
Return DataPortal.Create(Of Costcenter)(New CriteriaCode(GetType(Costcenter), "NewCostcenter", costcode))
End Function
Public Shared Function GetCostcenter(ByVal costcode As String) As Costcenter
Return DataPortal.Fetch(Of Costcenter)(New CriteriaCode(GetType(Costcenter), "GetCostcenter", costcode))
End Function
Public Shared Sub DeleteCostcenter(ByVal costcode As String)
DataPortal.Delete(New CriteriaCode(GetType(Costcenter), "DeleteCostcenter", costcode))
End Sub
#End
Region
#Region
" BusinessBase Overrides "
Protected Overrides Function GetIdValue() As Object
Return "costcenter." & mCostcode.ToString
End Function
#End
Region
#Region
" Data Access "
Protected Overridable Sub SetDefaults()
IsOwner = False
Table = "Cost Center"
mTstamp.Date = Date.Now
End Sub
#Region
"DataPortal_Create"
<RunLocal()> _
Protected Overridable Overloads Sub DataPortal_Create(ByVal criteria As Object)
If TypeOf criteria Is CriteriaCode Then
Dim crit As CriteriaCode = DirectCast(criteria, CriteriaCode)
mCostcode = crit.Code
Else
Throw New ApplicationException("Invalid criteria passed to DataPortal_Create method.")
End If
SetDefaults()
MarkNew()
ValidationRules.CheckRules()
End Sub
#End
Region
#Region
" DataPortal_Fetch "
Protected Overrides Sub DataPortal_Fetch(ByVal criteria As Object)
If TypeOf criteria Is CriteriaCode Then
Dim crit As CriteriaCode = DirectCast(criteria, CriteriaCode)
If CostcenterDAO.RecordDoesNotExist(crit.Code) Then
Me.DataPortal_Create(criteria)
Else
DoFetch(criteria)
End If
Else
Throw New ApplicationException("Invalid criteria passed to DataPortal_Fetch method.")
End If
End Sub
Protected Overridable Sub DoFetch(ByVal criteria As Object)
SetDefaults()
Dim cn As IDbConnection = Nothing
Try
cn = DAL.CreateConnection
cn.Open()
FetchData(criteria, cn)
PostFetchData(criteria, cn)
FetchChildren(criteria, cn)
Finally
cn.Close()
End Try
MarkOld()
ValidationRules.CheckRules()
End Sub
Protected Overridable Sub FetchData(ByVal criteria As Object, ByVal cn As IDbConnection)
Dim dr As SafeDataReader
If TypeOf criteria Is CriteriaCode Then
Dim crit As CriteriaCode = DirectCast(criteria, CriteriaCode)
dr = New SafeDataReader(DAL.ExecuteReader(cn, CommandType.Text, CostcenterDAO.Select(crit.Code)))
Else
Throw New ApplicationException("Invalid criteria passed to FetchData method.")
End If
Try
With dr
If .Read() Then
mCostcode = Trim(.GetString("costcode"))
mCcname = Trim(.GetString("ccname"))
mStatus = Trim(.GetString("status"))
mUserid = CLng(.GetValue("userid"))
mTstamp = .GetSmartDate("tstamp")
End If
End With
Finally
dr.Close()
End Try
End Sub
Protected Overridable Sub PostFetchData(ByVal criteria As Object, ByVal cn As IDbConnection)
'marker method that can be overridden in child class
End Sub
Protected Overridable Sub FetchChildren(ByVal criteria As Object, ByVal cn As IDbConnection)
'marker method that can be overridden in child class
End Sub
#End
Region
#Region
" DataPortal_Insert "
Protected Overrides Sub DataPortal_Insert()
Dim tr As IDbTransaction = Nothing
Try
tr = Me.BeginTransaction()
DoInsert(tr)
Me.EndTransaction(tr)
Catch ex As Exception
Me.RollbackTransaction(tr)
Throw
End Try
End Sub
Protected Overridable Sub DoInsert(ByVal tr As IDbTransaction)
Dim user As MyUser
user = DirectCast(Thread.CurrentPrincipal, MyUser)
InsertData(tr, user)
PostInsertData(tr, user)
UpdateChildren(tr)
End Sub
Protected Overridable Sub InsertData(ByVal tr As IDbTransaction, ByVal user As MyUser)
DAL.ExecuteNonQuery(tr, CommandType.Text, CostcenterDAO.Insert(mCostcode, mCcname, mStatus, user.Userkey))
End Sub
Protected Overridable Sub PostInsertData(ByVal tr As IDbTransaction, ByVal user As MyUser)
'marker method that can be overridden in child class
End Sub
Protected Overridable Sub UpdateChildren(ByVal tr As IDbTransaction)
'marker method that can be overridden in child class
End Sub
#End
Region
#Region
" DataPortal_Update "
Protected Overrides Sub DataPortal_Update()
Dim tr As IDbTransaction = Nothing
Try
tr = Me.BeginTransaction()
DoUpdate(tr)
Me.EndTransaction(tr)
Catch ex As Exception
Me.RollbackTransaction(tr)
Throw
End Try
End Sub
Protected Overridable Sub DoUpdate(ByVal tr As IDbTransaction)
Dim user As MyUser
user = DirectCast(Thread.CurrentPrincipal, MyUser)
UpdateData(tr, user)
PostUpdateData(tr, user)
UpdateChildren(tr)
End Sub
Protected Overridable Sub UpdateData(ByVal tr As IDbTransaction, ByVal user As MyUser)
DAL.ExecuteNonQuery(tr, CommandType.Text, CostcenterDAO.Update(mCostcode, mCcname, mStatus, user.Userkey))
End Sub
Protected Overridable Sub PostUpdateData(ByVal tr As IDbTransaction, ByVal user As MyUser)
'marker method that can be overridden in child class
End Sub
#End
Region
#Region
" DataPortal_Delete "
Protected Overrides Sub DataPortal_DeleteSelf()
DataPortal_Delete(New CriteriaCode(GetType(Costcenter), "DataPortal_DeleteSelf", mCostcode))
End Sub
Protected Overrides Sub DataPortal_Delete(ByVal criteria As Object)
Dim user As MyUser
user = DirectCast(Thread.CurrentPrincipal, MyUser)
Dim strSQL As String
If TypeOf criteria Is CriteriaCode Then
Dim crit As CriteriaCode = DirectCast(criteria, CriteriaCode)
strSQL = CostcenterDAO.Delete(crit.Code)
Else
Throw New ApplicationException("Invalid criteria passed to DataPortal_Delete method.")
End If
Dim tr As IDbTransaction = Nothing
Try
tr = Me.BeginTransaction()
'Delete child objects before deleting the parent.
DeleteChildren(tr, criteria)
'delete the parent
DAL.ExecuteNonQuery(tr, CommandType.Text, strSQL)
PostDeleteData(tr, user)
Me.EndTransaction(tr)
Catch ex As Exception
Me.RollbackTransaction(tr)
Throw
End Try
End Sub
Protected Overridable Sub DeleteChildren(ByVal tr As IDbTransaction, ByVal criteria As Object)
'marker method that can be overridden in child class
End Sub
Protected Overridable Sub PostDeleteData(ByVal tr As IDbTransaction, ByVal user As MyUser)
'marker method that can be overridden in child class
End Sub
#End
Region
#End
Region
End Class
=================================================================
Rather than embed Criteria objects inside each BO, I put them in their own namepsace and then re-use them.
e.g. This is the criteria class used above:
<Serializable()> _
Public Class CriteriaCode
Inherits Csla.CriteriaBase
Public MethodName As String = ""
Public Code As String = ""
Public Sub New(ByVal BOtype As Type, ByVal methodName As String, ByVal code As String)
MyBase.New(BOtype)
Me.MethodName = methodName
Me.Code = code
End Sub
End Class
=================================================================
The custom code is then placed in a class which inherits the root class above. Notice how little code goes in the custom level. The idea is to code gen as much as needed.
Like this:
<Serializable()> _
Public Class Costcenter
Inherits CostcenterGen
#Region
" Validation Rules "
Protected Overrides Sub AddBusinessRules()
MyBase.AddBusinessRules()
'SomeCode
ValidationRules.AddRule(AddressOf RecordExists, New RecordExistsRuleArgs("SomeCode", "xyz", ""), 1)
End Sub
#End Region
#Region " Data Access "
Protected Overrides Sub SetDefaults()
MyBase.SetDefaults()
mStatus = "A"
End Sub
#End
Region
End Class
HTH
Joe
Pieter replied on Wednesday, June 11, 2008
Thanks for this Joe.
When generating the base class and partial class using the codesmith templates, both classes contain the Data access code. I'm not sure which one to remove?
JoeFallon1 replied on Wednesday, June 11, 2008
Well, your original question does not match your original thread title.
There is a big difference between having a Base class which you inherit from and 2 partial classes where one is code gened and the other is hand written.
You asked about Base classes and inheritance which is the example I gave you.
I do not use partial classes. They have their own set of issues you need to resolve - like the inability to have the same method name twice. e.g. no overriding. There are ways around that issue but I am no expert on it.
I built my own codesmith templates (a long time ago) and can code gen a huge amount of my BOs and as you can see when I inherit from them there is almost no code in the derived class.
You are going to have to figure out which style you will use and then you can decide which pieces of code to "remove".
Joe
Copyright (c) Marimer LLC