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 MyCommandBOJoe
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.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.
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.
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.
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.
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
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
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
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
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?
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
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?
When the tr is in the BO layer and you make a call in ADO.Net to the database, if there is a problem with the query then you will get an exception. You can trap this exception and then evaluate it and re-throw it if necessary.
For example, I get back "ambiguous column name" errors if a query has a JOIN and the same column name is in both tables but I fail to prefix it with the table name. Similarly, if a trigger fires and fails, I get back the error message in the trigger code that I wrote. So I am pretty sure you would get back whatever error you raised in your SP (but not 100% positive.)
Let us know after you try it <g>.
Joe
Copyright (c) Marimer LLC