How-to: Use partial methods to preserve custom CSLA code and promote active regeneration.

One of the best features of code generation is the reality of being able to actively generate you code while preserving custom changes. This allows you to implement custom logic while constantly making changes to your metadata and getting the latest template changes. CodeSmith Generator has worked hard on making this happen in both the PLINQO and CSLA Templates. The CSLA templates offer many partial method overrides to make your life easier. It is recommended that you place any partial methods that you implement in the non-generated partial class; this is always the ClassName.cs or ClassName.vb source code file.

A list of partial methods can be found at the bottom of the every generated partial class. Here is an example of the generated partial methods.
 
C#
 
#region DataPortal partial methods
partial void OnCreating(ref bool cancel);
partial void OnCreated();
partial void OnFetching(CalendarCriteria criteria, ref bool cancel);
partial void OnFetched();
partial void OnMapping(SafeDataReader reader, ref bool cancel);
partial void OnMapped();
partial void OnUpdating(ref bool cancel);
partial void OnUpdated();
partial void OnAddNewCore(ref Calendar item, ref bool cancel);
#endregion
 
Visual Basic
 
#Region "DataPortal partial methods"
   
Partial Private Sub OnCreating(ByRef cancel As Boolean)
End Sub
Partial Private Sub OnCreated()
End Sub
Partial Private Sub OnFetching(ByVal criteria As CalendarCriteria, ByRef cancel As Boolean)
End Sub
Partial Private Sub OnFetched()
End Sub
Partial Private Sub OnMapping(ByVal reader As SafeDataReader, ByRef cancel As Boolean)
End Sub
Partial Private Sub OnMapped()
End Sub
Partial Private Sub OnUpdating(ByRef cancel As Boolean)
End Sub
Partial Private Sub OnUpdated()
End Sub
Partial Private Sub OnAddNewCore(ByVal item As Calendar, ByRef cancel As Boolean)
End Sub
   
#End Region
 
In this example we will take an EditableRootList called CalendarList, whose purpose is to hold a list of Calendar business objects. The default generated code will select all calendar entries with a specific criterion. The code below is the code that is generated in the CalendarList.DataAccess partial class:
 
C#
 
private void DataPortal_Fetch(CalendarCriteria criteria)
{
    bool cancel = false;
    OnFetching(criteria, ref cancel);
    if (cancel) return;
    RaiseListChangedEvents = false;
    // Fetch Child objects.
    string commandText = string.Format("SELECT [ID], [CalendarName], [Name], [EventStart], [EventEnd], [Resource] FROM [dbo].[Calendar] {0}", ADOHelper.BuildWhereStatement(criteria.StateBag));
    using (SqlConnection connection = new SqlConnection(ADOHelper.ConnectionString))
    {
        connection.Open();
        using (SqlCommand command = new SqlCommand(commandText, connection))
        {
            command.Parameters.AddRange(ADOHelper.SqlParameters(criteria.StateBag));
            using(var reader = new SafeDataReader(command.ExecuteReader()))
            {
                if(reader.Read())
                {
                    do
                    {
                        this.Add(new Calendar(reader));
                    } while(reader.Read());
                }
            }
        }
    }
    RaiseListChangedEvents = true;
    OnFetched();
}
 
Visual Basic
 
Private Shadows Sub DataPortal_Fetch(ByVal criteria As CalendarCriteria)
    Dim cancel As Boolean = False
    OnFetching(criteria, cancel)
    If (cancel) Then
        Return
    End If
    RaiseListChangedEvents = False
    ' Fetch Child objects.
    Dim commandText As String = String.Format("SELECT [ID], [CalendarName], [Name], [EventStart], [EventEnd], [Resource] FROM [dbo].[Calendar] {0}", ADOHelper.BuildWhereStatement(criteria.StateBag))
    Using connection As New SqlConnection(ADOHelper.ConnectionString)
        connection.Open()
        Using command As New SqlCommand(commandText, connection)
            command.Parameters.AddRange(ADOHelper.SqlParameters(criteria.StateBag))
            Using reader As SafeDataReader = New SafeDataReader(command.ExecuteReader())
                If reader.Read() Then
                    Do
                        Me.Add(New Calendar(reader))
                    Loop While reader.Read()
                End If
            End Using
        End Using
    End Using
    RaiseListChangedEvents = True
    OnFetched()
End Sub
 
As you can see that this isn't optimal because we only want to select all Calendar entries from the first of the current month. In the real word we would only want to select maybe a month of two or data... This can easily be done by overriding the partial method and modifying the sql statement. We will now go to the partial non generated class (CalendarList.cs or CalendarList.vb):
 
C#
 
#region Custom Data Access
partial void OnFetching(CalendarCriteria criteria, ref bool cancel)
{
    RaiseListChangedEvents = false;
    // Fetch Child objects.
    string commandText = string.Format("SELECT [ID], [CalendarName], [Name], [EventStart], [EventEnd], [Resource] FROM [dbo].[Calendar] {0} AND [EventStart] >= CONVERT(varchar,DATEADD(MONTH,DATEDIFF(MONTH,0,GETDATE()),0),101)", ADOHelper.BuildWhereStatement(criteria.StateBag));
    using (SqlConnection connection = new SqlConnection(ADOHelper.ConnectionString))
    {
        connection.Open();
        using (SqlCommand command = new SqlCommand(commandText, connection))
        {
            command.Parameters.AddRange(ADOHelper.SqlParameters(criteria.StateBag));
            using (var reader = new SafeDataReader(command.ExecuteReader()))
            {
                if (reader.Read())
                {
                    do
                    {
                        this.Add(new Calendar(reader));
                    } while (reader.Read());
                }
            }
        }
    }
    RaiseListChangedEvents = true;
    // Cancel the existing Dal method.
    cancel = true;
}
#endregion
 
Visual Basic
 
#Region "Custom Data Access"
Private Sub OnFetching(ByVal criteria As CategoryCriteria, ByRef cancel As Boolean)
    RaiseListChangedEvents = False
    ' Fetch Child objects.
    Dim commandText As String = String.Format("SELECT [ID], [CalendarName], [Name], [EventStart], [EventEnd], [Resource] FROM [dbo].[Calendar] {0} AND [EventStart] >= CONVERT(varchar,DATEADD(MONTH,DATEDIFF(MONTH,0,GETDATE()),0),101)", ADOHelper.BuildWhereStatement(criteria.StateBag))
    Using connection As New SqlConnection(ADOHelper.ConnectionString)
        connection.Open()
        Using command As New SqlCommand(commandText, connection)
            command.Parameters.AddRange(ADOHelper.SqlParameters(criteria.StateBag))
            Using reader As SafeDataReader = New SafeDataReader(command.ExecuteReader())
                If reader.Read() Then
                    Do
                        Me.Add(New Calendar(reader))
                    Loop While reader.Read()
                End If
            End Using
        End Using
    End Using
    RaiseListChangedEvents = True
    'Cancel the existing Dal method.
    cancel = True
End Sub
#End Region
 
As you can see all we needed to do was implement the OnFetching partial method and override the sql query that was then sent to the database server. You can do anything you want in this partial method. The last thing to do is set the reference cancel variable to true if you wish to have everything after the partial method call ignored. If you set this to false then the existing generated code will also run. In this case, we want to cancel out. A scenario where you would want to return false, is if you wanted to do some processing before accessing the database.
 
If you come across something that you wish had a partial method, please log it on the CodeSmith template issue tracker.

1 comment(s)