Hi,
Yes that would be great thanks, I would be very interested to see how you have structured this...
Many thanks,
Nick
Yes, if you're just fetching an object, no matter how complex internally, I think this is best done via a BusinessBase derived class (or one of xxxxListBase classes if you are fetching a collection). Then you can implement static factory methods that execute the appropriate DataPortal_Fetch.
On the other hand, complicated server side updates can be done either as CommandBase or BusinessBase derived classes. If input from the user is expected, a BusinessBase class is better because it is bindable and has validation rule capability. CommandBase classes have neither. You can do whatever you want server side in DataPortal_Execute method of a CommandBase or DataPortal_Update of a BusinessBase.
Here is a sample command implementation. We do internal transaction management, so think of TransactionContext as just something that can Begin/Commit/Rollback and knows if a transaction in progress or not.
Base Class (the "harder" one)
using System;
namespace Csla.WORMapper
{
/// <summary>
/// Execute a command with a generic argument in a database transaction, automatically enlisting in
/// current one if active
///
/// Usually, derived classes need only override the Execute(TransactionContext context) method
/// </summary>
/// <typeparam name="T">A class that inherits from <see cref="TransactionalCommand{T,V}"/>.</typeparam>
/// <typeparam name="V">An argument of any type.</typeparam>
[Serializable()]
public class TransactionalCommand<T, V> : CommandBase where T : TransactionalCommand<T, V>
{
protected V _arg;
protected string _databaseKey;
protected TransactionalCommand()
: this("Base")
{
}
protected TransactionalCommand(string databaseKey)
{
_databaseKey = databaseKey;
}
public static V Execute(V arg )
{
return Execute(arg, "Base");
}
bool _autoBeginTrans = true;
public virtual bool AutoBeginTrans
{
get { return _autoBeginTrans; }
set { _autoBeginTrans = value; }
}
public V Argument
{
get { return _arg; }
}
public static V Execute(V arg, string databaseKey)
{
// Must retrieve via data portal
TransactionalCommand<T, V> command = Activator.CreateInstance<T>();
command._arg = arg;
command._databaseKey = databaseKey;
return DataPortal.Execute<TransactionalCommand<T,V>>(command)._arg;
}
protected override void DataPortal_Execute()
{
TransactionContext.ExecuteInTransaction(_databaseKey, Execute, AutoBeginTrans);
}
protected virtual void Execute(TransactionContext context)
{
throw new System.NotImplementedException("Method Execute() must be implemented in derived class");
}
}
}
-----------------
Derived classes can be very simple and just implement and override of Execute(). Here is a command nested in a class I have called ReceiveOneItem that sends the item to inspection:
The command argument for the base class can be anything, but in this case it's an actual BO that implements a normal save operation (the receive item transaction). In this case I don't want to save it normally but instead do a different transformation on it on the server (send it to inspection where someone can verify it's condition and either receive it or reject it).
As you can see, the derived command implementation is trivial. It just calls a method on the argument. You can compose these yourself almost abitrarily.
[Serializable]
private class SendToInspectionCommand : Csla.WORMapper.TransactionalCommand<SendToInspectionCommand, ReceiveOneItem>
{
protected override void Execute(Csla.WORMapper.TransactionContext context)
{
_arg.ToInspectOneItem(context);
}
}
internal void ToInspectOneItem(Csla.WORMapper.TransactionContext transaction)
{
..
.. DO WORK HERE
}
Yes, if you're just fetching an object, no matter how complex internally, I think this is best done via a BusinessBase derived class (or one of xxxxListBase classes if you are fetching a collection). Then you can implement static factory methods that execute the appropriate DataPortal_Fetch.
On the other hand, complicated server side updates can be done either as CommandBase or BusinessBase derived classes. If input from the user is expected, a BusinessBase class is better because it is bindable and has validation rule capability. CommandBase classes have neither. You can do whatever you want server side in DataPortal_Execute method of a CommandBase or DataPortal_Update of a BusinessBase.
Here is a sample command implementation. We do internal transaction management, so think of TransactionContext as just something that can Begin/Commit/Rollback and knows if a transaction in progress or not.
Base Class (the "harder" one)
using System;
namespace Csla.WORMapper
{
/// <summary>
/// Execute a command with a generic argument in a database transaction, automatically enlisting in
/// current one if active
///
/// Usually, derived classes need only override the Execute(TransactionContext context) method
/// </summary>
/// <typeparam name="T">A class that inherits from <see cref="TransactionalCommand{T,V}"/>.</typeparam>
/// <typeparam name="V">An argument of any type.</typeparam>
[Serializable()]
public class TransactionalCommand<T, V> : CommandBase where T : TransactionalCommand<T, V>
{
protected V _arg;
protected string _databaseKey;
protected TransactionalCommand()
: this("Base")
{
}
protected TransactionalCommand(string databaseKey)
{
_databaseKey = databaseKey;
}
public static V Execute(V arg )
{
return Execute(arg, "Base");
}
bool _autoBeginTrans = true;
public virtual bool AutoBeginTrans
{
get { return _autoBeginTrans; }
set { _autoBeginTrans = value; }
}
public V Argument
{
get { return _arg; }
}
public static V Execute(V arg, string databaseKey)
{
// Must retrieve via data portal
TransactionalCommand<T, V> command = Activator.CreateInstance<T>();
command._arg = arg;
command._databaseKey = databaseKey;
return DataPortal.Execute<TransactionalCommand<T,V>>(command)._arg;
}
protected override void DataPortal_Execute()
{
TransactionContext.ExecuteInTransaction(_databaseKey, Execute, AutoBeginTrans);
}
protected virtual void Execute(TransactionContext context)
{
throw new System.NotImplementedException("Method Execute() must be implemented in derived class");
}
}
}
-----------------
Derived classes can be very simple and just implement an override of Execute(). Here is a command nested in a class I have called ReceiveOneItem that sends the item to inspection:
The command argument for the base class can be anything, but in this case it's an actual BO that implements a normal save operation (the receive item transaction). In this case I don't want to save it normally but instead do a different transformation on it on the server (send it to inspection where someone can verify it's condition and either receive it or reject it).
Invocation is simple: (ReceiveOneItem implements a SendToInspection method that calls the command on itself and returns the transformed object)
public ReceiveOneItem SendToInspection()
{
return SendToInspectionCommand.Execute(this);
}
Below, the derived command class implementation is trivial. It just calls a method on the argument. You can compose these yourself almost abitrarily, and your objects can support either simple server side operations or different flavors as needed.
[Serializable]
private class SendToInspectionCommand : Csla.WORMapper.TransactionalCommand<SendToInspectionCommand, ReceiveOneItem>
{
protected override void Execute(Csla.WORMapper.TransactionContext context)
{
_arg.ToInspectOneItem(context);
}
}
internal void ToInspectOneItem(Csla.WORMapper.TransactionContext transaction)
{
..
.. DO WORK HERE
}
Copyright (c) Marimer LLC