Vibrant discussion about CSLA .NET and using the framework to build great business applications.
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.
Joe
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
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.Outputcmd.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.
Warren: Should I be implementing 4 Command objects to do this? Any help is appreciated.
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
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 forException 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 = 0End 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.
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
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.
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.
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 = 0End Function
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
……
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
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
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?
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.
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?