Thursday, October 06, 2005

Here's a "basic" CSLA .NET 2.0 editable root class. It pretty much illustrates all the things you can do in a class under the upcoming version of the framework. In particular note the way validation rules are handled and all the transactional options in the DataPortal_XYZ methods (presumably you'd pick one for your app :) ).

A CustomerTypes class is also included at the bottom, illustrating how to implement a name-value list that works with data binding.

Imports CSLA

 

<Serializable()> _

Public Class Customer

  Inherits BusinessBase(Of Customer)

 

#Region " Business Methods "

 

  Private mID As Integer

  Private mLastName As String = ""

  Private mFirstName As String = ""

  Private mLastActivity As SmartDate

  Private mType As Integer

  Private mCity As String = ""

 

  Private Shared mCustomerTypes As CustomerTypes

 

  Public Property City() As String

    Get

      If CanReadProperty() Then

        Return mCity

      Else

        Throw New System.Security.SecurityException( _

          "Property read not allowed")

      End If

    End Get

    Set(ByVal value As String)

      If CanWriteProperty() Then

        If Not mCity.Equals(value) Then

          mCity = value

          PropertyHasChanged()

        End If

      Else

        Throw New System.Security.SecurityException( _

          "Property write not allowed")

      End If

    End Set

  End Property

 

  Public Shared ReadOnly Property CustomerTypes() As CustomerTypes

    Get

      If mCustomerTypes Is Nothing Then

        mCustomerTypes = Library.CustomerTypes.GetCustomerTypes

      End If

      Return mCustomerTypes

    End Get

  End Property

 

  Public ReadOnly Property ID() As Integer

    Get

      Return mID

    End Get

  End Property

 

  Public Property LastName() As String

    Get

      If CanReadProperty() Then

        Return mLastName

      Else

        Throw New System.Security.SecurityException( _

          "Property read not allowed")

      End If

    End Get

    Set(ByVal value As String)

      If CanWriteProperty() Then

        If mLastName <> value Then

          mLastName = value.ToUpper

          PropertyHasChanged()

        End If

      Else

        Throw New System.Security.SecurityException( _

          "Property write not allowed")

      End If

    End Set

  End Property

 

  Public Property FirstName() As String

    Get

      If CanReadProperty() Then

        Return mFirstName

      Else

        Throw New System.Security.SecurityException( _

          "Property read not allowed")

      End If

    End Get

    Set(ByVal value As String)

      If CanWriteProperty() Then

        If mFirstName <> value Then

          mFirstName = value

          PropertyHasChanged()

        End If

      Else

        Throw New System.Security.SecurityException( _

          "Property write not allowed")

      End If

    End Set

  End Property

 

  Public Property LastActivity() As String

    Get

      If CanReadProperty() Then

        Return mLastActivity.Text

      Else

        Throw New System.Security.SecurityException( _

          "Property read not allowed")

      End If

    End Get

    Set(ByVal value As String)

      If CanWriteProperty() Then

        If mLastActivity <> value Then

          mLastActivity.Text = value

          PropertyHasChanged()

        End If

      Else

        Throw New System.Security.SecurityException( _

          "Property write not allowed")

      End If

    End Set

  End Property

 

  Public Property CustomerType() As Integer

    Get

      If CanReadProperty() Then

        Return mType

      Else

        Throw New System.Security.SecurityException( _

          "Property read not allowed")

      End If

    End Get

    Set(ByVal value As Integer)

      If CanWriteProperty() Then

        If mType <> value Then

          mType = value

          PropertyHasChanged()

        End If

      Else

        Throw New System.Security.SecurityException( _

          "Property write not allowed")

      End If

    End Set

  End Property

 

  Public ReadOnly Property CustomerTypeText() As String

    Get

      If CanReadProperty() Then

        Return CustomerTypes.Value(mType)

      Else

        Throw New System.Security.SecurityException( _

          "Property read not allowed")

      End If

    End Get

  End Property

 

#End Region

 

#Region " Business Rules "

 

  Protected Overrides Sub AddBusinessRules()

    AddRule(AddressOf Validation.CommonRules.StringRequired, _

      "FirstName")

    AddRule(AddressOf Validation.CommonRules.StringRequired, _

      "LastName")

  End Sub

 

#End Region

 

#Region " Object ID Value "

 

  Protected Overrides Function GetIdValue() As Object

    Return mID

  End Function

 

#End Region

 

#Region " Constructors "

 

  Private Sub New()

 

    ' don't allow a Guest to see or change city data

    AuthorizationRules.DenyRead("City", "Guest")

    AuthorizationRules.DenyWrite("City", "Guest")

 

  End Sub

 

#End Region

 

#Region " Factory Methods "

 

  Public Shared Function NewCustomer() As Customer

    Return DataPortal.Create(Of Customer)(Nothing)

  End Function

 

  Public Shared Function GetCustomer(ByVal id As Integer) As Customer

    Return DataPortal.Fetch(Of Customer)(New Criteria(id))

  End Function

 

  Public Shared Sub DeleteCustomer(ByVal id As Integer)

    DataPortal.Delete(New Criteria(id))

  End Sub

 

#End Region

 

#Region " Criteria "

 

  <Serializable()> _

  Private Class Criteria

    Public ID As Integer

 

    Public Sub New(ByVal id As Integer)

      Me.ID = id

    End Sub

  End Class

 

#End Region

 

#Region " Data Access "

 

  ' Forced to run on client

  <RunLocal()> _

  Protected Overrides Sub DataPortal_Create(ByVal criteria As Object)

 

    ' get default values from db

    ' we get here via DataPortal.Create()

 

    mID = New System.Random().Next(100, 999)

    mType = CustomerTypes.DefaultKey

    CheckRules()

 

  End Sub

 

  ' Runs on client or server based on DataPortal config

  Protected Overrides Sub DataPortal_Fetch(ByVal criteria As Object)

 

    ' get data from db

    ' we get here via DataPortal.Fetch()

 

    Dim crit As Criteria = DirectCast(criteria, Criteria)

    mID = crit.ID

    mType = CustomerTypes.Key("Hybrid")

    mLastName = "Lhotka"

    mFirstName = "Rocky"

    CheckRules()

 

  End Sub

 

  ' COM+ transactions

  <Transactional()> _

  Protected Overrides Sub DataPortal_Insert()

 

    ' insert data into db

    ' we get here via obj.Save()

 

  End Sub

 

  ' System.Transactions namespace

  Protected Overrides Sub DataPortal_Update()

 

    ' update data in db

    ' we get here via obj.Save()

 

    Using tr As New System.Transactions.TransactionScope

      ' do updates

      tr.Complete()

    End Using

 

  End Sub

 

  ' ADO.NET transactions

  Protected Overrides Sub DataPortal_DeleteSelf()

 

    ' do deferred delete of self

    ' we get here via obj.Save()

 

    Using cn As New SqlClient.SqlConnection

      Using tr As SqlClient.SqlTransaction = cn.BeginTransaction

        ' do delete

      End Using

    End Using

 

  End Sub

 

  ' No transactions

  Protected Overrides Sub DataPortal_Delete(ByVal criteria As Object)

 

    Dim crit As Criteria = DirectCast(criteria, Criteria)

 

    ' do immediate delete based on criteria

    ' we get here via DataPortal.Delete()

 

  End Sub

 

#End Region

 

End Class

 

======================================================

Imports CSLA

 

Public Class CustomerTypes

  Inherits NameValueListBase(Of Integer, String)

 

  Public Function DefaultKey() As Integer

    Return 1

  End Function

 

  Public Function DefaultValue() As String

    Return Value(DefaultKey)

  End Function

 

  Private Sub New()

 

  End Sub

 

  Public Shared Function GetCustomerTypes() As CustomerTypes

    Return DataPortal.Fetch(Of CustomerTypes) _

      (New Criteria(GetType(CustomerTypes)))

  End Function

 

  Protected Overrides Sub DataPortal_Fetch(ByVal criteria As Object)

    Me.IsReadOnly = False

    Add(New NameValuePair(1, "Domestic"))

    Add(New NameValuePair(2, "International"))

    Add(New NameValuePair(3, "Hybrid"))

    Me.IsReadOnly = True

  End Sub

 

End Class

 

Thursday, October 06, 2005 2:18:16 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [7]  | 

 Wednesday, October 05, 2005
Bill McCarthy has come up with a nice extender control that wraps the solution to the data binding issue I've been discussing. Thanks Bill!!
Wednesday, October 05, 2005 3:09:52 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  | 

 Wednesday, September 28, 2005

This is the conclusion (for now) of the issue with Windows Forms data binding that I discussed in an earlier entry.

 

To recap, the issue occurs in detail forms built using data binding. The current control’s value isn’t refreshed when the user tabs off the control. If the data source (business object or smart DataTable with a partial class) changed the value due to business logic that change is not reflected in the UI, even though the changed value is in the data source.

 

In talking to the data team at Microsoft, it turns out that this is a bug and will likely be fixed in some future service pack. At this late stage of the game however, it won’t be fixed for release of VS 2005.

 

Fortunately they were able to come up with a decent workaround in the meantime. The workaround is done in the UI and involves hooking an event from each Binding object, then in the event handler forcing the current value to be refreshed from the data source.

 

To set this up, add the following code to your form:

 

  Private Sub HookBindings()

 

    For Each ctl As Control In Me.Controls

      For Each b As Binding In ctl.DataBindings

        AddHandler b.BindingComplete, AddressOf ReadBinding

      Next

    Next