Base Classes & Partial Classes

Base 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