CSLA .NET

Vibrant discussion about CSLA .NET and using the framework to build great business applications.

CSLA .NET Resources:
  • CSLA .NET forum
  • CSLA .NET home page
  • Command Object Implementation

    rated by 0 users
    This post has 15 Replies | 1 Follower

    Top 200 Contributor
    Posts 48
    Warren Posted: Thu, Jul 3 2008 5:22 PM
     My application has a use case which is a server side work flow that I have chosen to implement with a Command object.

    The steps in this workflow are:  

    Get Balances
    Verify Balances
    Report Balances
    Load Balances

    If any of the steps fail, the subsequent steps are not run and the process is terminated with an error condition.  The user can opt to cancel  "Load Balances" based on the output produced in "Report Balances".

    I'm not sure if my question is a simple .Net question or CSLA?

    I am coding the Command object based on the design Rocky demonstrates  in Chapter 7 except I have 4 stored procedure calls to make, 1 for each step in the workflow. I intend to create 4 factory methods, 1 for each step but am unsure of how to code the DataPortal_Execute() override for the 4 different calls - how would they be distinguished from each other.                                           

    Should I be implementing 4 Command objects to do this?  Any help is appreciated.

    So far my code looks like this:


    <Serializable()> _
    Public Class ImportBalanceCmd
       Inherits CommandBase

    #Region " Authorization "

       ' See if user is allowed to load Balances
       Public Shared Function CanExecuteObject() As Boolean
          Return Csla.ApplicationContext.User.IsInRole("Administrator")
       End Function

    #End Region

    #Region " Client Side Code "

       Private mResult As Boolean
       Private mReturnCode As Integer = 0
       Private mReturnMessage As String = String.Empty
       

       Public ReadOnly Property Result() As Boolean
          Get
             Return mResult
          End Get
       End Property

       Public ReadOnly Property ReturnCode() As Integer
          Get
             Return mReturnCode
          End Get
       End Property

       Public ReadOnly Property ReturnMessage() As String
          Get
             Return mReturnMessage
          End Get
       End Property


       Public Sub BeforeServer()
          ' implement code to run on client before server code is called
       End Sub

       Public Sub AfterServer()
          ' implement code to run on client after server code is called
       End Sub

    #End Region


    #Region " Factory Methods "

       Public Shared Function GetBalances() As Boolean

          Dim cmd As New ImportBalanceCmd
          cmd.BeforeServer()
          cmd = DataPortal.Execute(Of ImportBalanceCmd)(cmd)
          cmd.AfterServer()

       End Function

       Public Sub New()
          'require use of Factory Methods
       End Sub


       Public Sub VerifyBalances()

       End Sub

       Public Sub ReportVerification()

       End Sub

       Public Sub LoadBalances()

       End Sub

    #End Region


    #Region " Server-Side Code "

       Protected Overrides Sub DataPortal_Execute()

          Using cn As New SqlConnection(Database.DBConnection)
             cn.Open()
             Using cmd As New SqlCommand("usp_GetBalances", cn)
                LocalContext("dpConnection") = cn
                LocalContext("dpCommand") = cmd
                cmd.CommandType = CommandType.StoredProcedure
                cmd.Parameters.AddWithValue("@ReturnCode", mReturnCode).Direction = ParameterDirection.Output
                cmd.Parameters.AddWithValue("@ReturnMessage", mReturnMessage).Direction = ParameterDirection.Output
                cmd.ExecuteNonQuery()
             End Using
          End Using

          mResult = True

       End Sub



    #End Region


    End Class




















    Top 10 Contributor
    Posts 1,244

    A single command object with 4 factory methods seems OK to me.

    Just add a new private variable like mMethodName As String and set it in your constructor call from each factory method.

    Then you can branch inside DP_Execute based on the method name.

    e.g.

    Public Shared Function GetSomething(ByVal someParam As String) As MyCommandBO
     
    Return DataPortal.Execute(New MyCommandBO("GetSomething", someParam ))
    End Function

    Private Sub New(ByVal methodName As String, ByVal someParam As String)
      mMethodName= methodName
      mSomeParam = someParam
    End Sub

    Joe

     

    Top 500 Contributor
    Posts 29
    Dawn replied on Tue, Jul 8 2008 10:53 PM
    sorry, I'm using c#.

    enum WorkflowTypes
    {
       GetBalances,
       VerifyBalances,
       ReportBalances,
       LoadBalances
    }

    private WorkflowTypes workflowType;

    // Constructor
    private  ImportBalanceCmd(WorkflowTypes workflowType)
    {
      this.workflowType = workflowType;
    }

    // Factory method
    public static bool GetBalance()
    {
      ImportBalanceCmd command = new ImprotBalanceCmd(WorkflowTypes.GetBalances);
      ......
    }

    public static bool VerifyBalance()
    {
      ImportBalanceCmd command = new ImprotBalanceCmd(WorkflowTypes.VerifyBlances);
      ........
    }
      ........
    // DataAccess
    protectd override void DataPortal_Execute()
    {
       switch(workflowType)
       {
            case WorkflowTypes.GetBalances:
                .....
                break;
             case .....


       }
    }

    Dawn
    Top 200 Contributor
    Posts 48
    Warren replied on Wed, Jul 9 2008 3:17 PM

    Thanks for the help Dawn and Joe,

    I have been testing out the first process with a Wizard style form which leads the user through each of the 4 processes. Your input comes at a perfect time, I'll carry on implementing the last 3 processes with the single command object.

    Cheers

     

    Top 200 Contributor
    Posts 48
    Warren replied on Wed, Jul 9 2008 6:27 PM

    My command object client side code now looks like this:

       Enum WorkflowTypes
          GetBalances
          VerifyBalances
          ReportBalances
          LoadBalances
       End Enum

       Private mWorkflowType As WorkflowTypes
       Private mReturnCode As Integer
       Private mReturnMessage As String

       Public ReadOnly Property ReturnCode() As Integer
          Get
             Return mReturnCode
          End Get
       End Property

       Public ReadOnly Property ReturnMessage() As String
          Get
             Return mReturnMessage
          End Get
       End Property

       ' Constructor
       Private Sub New(ByVal workflowType As WorkflowTypes)
          Me.mWorkflowType = workflowType
       End Sub

       Public Shared Function GetBalances() As Boolean
          Dim cmd As ImportBalanceCmd = New ImportBalanceCmd(WorkflowTypes.GetBalances)
          cmd = DataPortal.Execute(Of ImportBalanceCmd)(cmd)
          Return cmd.mReturnCode = 0
       End Function

    The Data_Portal call to the stored procedure has this code: 

    cmd.Parameters.Add("@ReturnCode", SqlDbType.Int).Direction = ParameterDirection.Output
    cmd.Parameters.Add("@ReturnMessage", SqlDbType.VarChar, 255).Direction = ParameterDirection.Output
     cmd.ExecuteNonQuery()
     mReturnCode = DirectCast(cmd.Parameters("@ReturnCode").Value, Integer)
     mReturnMessage = DirectCast(cmd.Parameters("@ReturnMessage").Value, String) 

    How can I retrieve the value in mReturnMessage if my call to the command object looks like this:

    If ImportBalanceCmd.GetBalances Then

    Else

     '.Need the return message....

    Thanks in advance.

    Top 500 Contributor
    Posts 29
    Dawn replied on Thu, Jul 10 2008 12:37 AM
    You can't get return message like that.

    if the mReturn message is the only thing you want from the server, may be you can code you factory method like this:
    public static bool GetBalances(out string message)
    {
      ImportBalanceCmd cmd = new ImportBalanceCmd(WorkflowTypes.GetBalances)
      cmd = DataPortal.Exectue(cmd);
      message = cmd.ReturnMessage;
      return cmd.ReturnCode == 0;
    }
    then,
    string returnMessage;
    if (ImportBalanceCmd.GetBalances(out returnMessage)
    {
    }
    else
    {
       //now you get returnMessage
    }

    but, if you nees other stuff from server, my suggestion is encapsulate them in a class like the command object, then you get every thing.


    Dawn
    Top 10 Contributor
    Posts 9,475

    Warren:

    Should I be implementing 4 Command objects to do this?  Any help is appreciated.

    Yes, I think so. You have 4 different commands that occur at 4 different times, with user interaction in between them.

    I think I'd consider having a fifth object that orchestrates the workflow - because that's really what you are describing. Each time the user re-enters the workflow, this fifth object is the one that would be loaded, and it would invoke the 4 command objects as appropriate.

    An even better solution might be to use the Windows Workflow Foundation - hard to say.

    Rocky

    Top 200 Contributor
    Posts 48
    Warren replied on Tue, Jul 15 2008 4:20 PM

    Hi Rocky,

    Due to time constraints, I will not review WWF. I have managed to learn VB.NET, CSLA,  CSLAGen, OOP concepts, Microsoft Enterprise Application Blocks for
    Exception Handling and Logging in this one project which I feel good about. I found a VB.Net open source Wizard control which provides a UI centric approach for orchestrating the workflow and allows the user to choose a course of action after each step. For others who may be interested the Wizard can be found at http://www.codeproject.com/KB/vb/GNWizard.aspx.

    I think a "purer" OOP design would use 4 seperate command objects, each with a single behavior. On the otherhand, the behaviors are related and there is no use case for using anything but all four 'commands' together.

    My lack of expertise in .NET coding and concepts brings up another question:

    In order to retrieve both the return code and message from the sproc which is invoked by the command object factory method, I made the message field shared inside the Command Object like this:

    Private Shared mReturnMessage As String
      
    I get the return code like this:

    Public Shared Function GetBalances() As Boolean
          Dim cmd As ImportBalanceCmd = New ImportBalanceCmd(WorkflowTypes.GetBalances)
          cmd = DataPortal.Execute(Of ImportBalanceCmd)(cmd)
          Return cmd.mReturnCode = 0
    End Function


    Is it considered bad form to use a public shared field which persists the message returned from the sproc last time it was called? This works but I have a feeling there is a better way to do things.

    Thanks.

    Top 10 Contributor
    Posts 721
    sergeyb replied on Tue, Jul 15 2008 4:42 PM

    I must be missing something.  I am not sure why you need a shared member to get two values back from SP.  You should be able to declare two output parameters and retrieve both values in DP_Exec and expose both as public properties.

     

     

     

    Sergey Barskiy

    Senior Consultant

    office: 678.405.0687 | mobile: 404.388.1899

    Magenic ®

    Microsoft Worldwide Partner of the Year | Custom Development Solutions, Technical Innovation

     

    From: Warren [mailto:cslanet@lhotka.net]
    Sent: Tuesday, July 15, 2008 5:21 PM
    To: Sergey Barskiy
    Subject: Re: [CSLA .NET] Command Object Implementation

     

    Hi Rocky,

    Due to time constraints, I will not review WWF. I have managed to learn VB.NET, CSLA,  CSLAGen, OOP concepts, Microsoft Enterprise Application Blocks for
    Exception Handling and Logging in this one project which I feel good about. I found a VB.Net open source Wizard control which provides a UI centric approach for orchestrating the workflow and allows the user to choose a course of action after each step. For others who may be interested the Wizard can be found at http://www.codeproject.com/KB/vb/GNWizard.aspx.

    I think a "purer" OOP design would use 4 seperate command objects, each with a single behavior. On the otherhand, the behaviors are related and there is no use case for using anything but all four 'commands' together.

    My lack of expertise in .NET coding and concepts brings up another question:

    In order to retrieve both the return code and message from the sproc which is invoked by the command object factory method, I made the message field shared inside the Command Object like this:

    Private Shared mReturnMessage As String
      
    I get the return code like this:

    Public Shared Function GetBalances() As Boolean
          Dim cmd As ImportBalanceCmd = New ImportBalanceCmd(WorkflowTypes.GetBalances)
          cmd = DataPortal.Execute(Of ImportBalanceCmd)(cmd)
          Return cmd.mReturnCode = 0
    End Function


    Is it considered bad form to use a public shared field which persists the message returned from the sproc last time it was called? This works but I have a feeling there is a better way to do things.

    Thanks.



    Top 10 Contributor
    Posts 9,475

    Using a Shared field is problematic on a couple levels.

     

    First, Shared fields aren’t serialized, so it won’t flow to/from an app server in a 3-tier scenario.

     

    Second, Shared fields are not only shared across all object instances, but across the entire appdomain. So again, in a 3-tier scenario where you have a multi-threaded server you’ll get all sorts of odd results because different threads will all be overwriting the same field.

     

    Rocky

     

    Rocky

    Top 200 Contributor
    Posts 48
    Warren replied on Wed, Jul 16 2008 10:51 AM

    Thanks for the advice regarding the use of shared fields and serialization.  I had a feeling it was a bad idea.  I changed the factory method to accept a parameter passed by reference.

    Public Shared Function GetBalances(ByRef RetMsg As String) As Boolean
       Dim cmd As ImportBalanceCmd = New ImportBalanceCmd(WorkflowTypes.GetBalances)
       cmd = DataPortal.Execute(Of ImportBalanceCmd)(cmd)
       RetMsg = cmd.mReturnMessage
       Return cmd.mReturnCode = 0
    End Function
     

     

     

    Top 10 Contributor
    Posts 721
    sergeyb replied on Wed, Jul 16 2008 10:56 AM

    Looks good to me. If you want to get rid of byref parameter, you can always wrap return value in a structure:

     

    Public Struct BalanceValue

    Message as String

    Value a Boolean

    End

     

    Public Shared Function GetBalances() As BalanceValue

    ……

     

     

     

    Sergey Barskiy

    Senior Consultant

    office: 678.405.0687 | mobile: 404.388.1899

    cid:_2_0648EA840648E85C001BBCB886257279
    Microsoft Worldwide Partner of the Year | Custom Development Solutions, Technical Innovation

     

    From: Warren [mailto:cslanet@lhotka.net]
    Sent: Wednesday, July 16, 2008 11:52 AM
    To: Sergey Barskiy
    Subject: Re: [CSLA .NET] RE: Command Object Implementation

     

    Thanks for the advice regarding the use of shared fields and serialization.  I had a feeling it was a bad idea.  I changed the factory method to accept a parameter passed by reference.

    Public Shared Function GetBalances(ByRef RetMsg As String) As Boolean
       Dim cmd As ImportBalanceCmd = New ImportBalanceCmd(WorkflowTypes.GetBalances)
       cmd = DataPortal.Execute(Of ImportBalanceCmd)(cmd)
       RetMsg = cmd.mReturnMessage
       Return cmd.mReturnCode = 0
    End Function
     

     

     



    Top 200 Contributor
    Posts 48
    Warren replied on Fri, Jul 18 2008 1:30 PM

    Thanks for the idea re use of a structure Sergey,

    I would appreciate any advice regarding the use of transaction processing in sprocs vs using  <Transactional(TransactionalTypes.TransactionScope)>  in my command object. So far I have chosen to use TP in the SQL Server sproc. It works nicely in my TRY/CATCH Block where I can issue a rollback if need be and pass back the error code back to the command object. The UI can then decide what to do with the error.

    Are there drawbacks having to having TP in the stored procedure rather than the Command Object I should be aware of?

     

    Top 10 Contributor
    Posts 1,244

    Are there drawbacks having to having TP in the stored procedure rather than the Command Object I should be aware of?

    Not really. As long as the command object is the only thing you are trying to do at that time.

    When you have multiple BOs and you want them all to save to the DB as part of a transaction then the DB transaction model is a poor choice. In this case it is far better to start the tr in the BO layer and pass it around for all BOs to use.

    So for consistency sake alone, I would remove the DB tr code - so you do not get confused about when to use which model in the future.

    Joe

    Top 200 Contributor
    Posts 48
    Warren replied on Mon, Jul 21 2008 1:33 PM

    Thanks Joe.

    This wizard function required command objects only. For consistency and since I need another wizard function that will use both command objects and regular business objects I should do TP in code instead of the sproc. 

    The code method is much more "black boxed" however.  Can I assume that any SQL Errors that occur in the sproc will rollback the transaction allowing me to continue to return the cause of the error via my sproc so I can handle it in my UI?

     

    Page 1 of 2 (16 items) 1 2 Next > | RSS

    Copyright (c) 2006-2014 Marimer LLC. All rights reserved.
    Email admin@lhotka.net for support.
    Powered by Community Server (Non-Commercial Edition), by Telligent Systems