I am trying to understand the domain events. Let's say I have a customer object and it raises the OnCustomerInserted event. I want my email service class to subscribe to that event so that whenever the event is raised an email is sent.
public class Customer
{
public delegate void CustomerInsertedHandler();
public event CustomerInsertedHandler CustomerInserted;
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public void OnCustomerInserted()
{
if(CustomerInserted != null)
{
CustomerInserted();
}
}
}
And here is the CustomerRepository:
public class CustomerRepository
{
public void Add(Customer customer)
{
// add the customer
customer.Id = 2;
if(customer.Id > 0)
{
// raise the event!
}
}
}
Finally, here is the simple EmailService class:
public class EmailService
{
public EmailService()
{
}
public void Send()
{
Console.WriteLine("Email has been sent!");
}
}
UPDATE:
Still the code below is bit ugly!
public class CustomerRepository
{
public delegate void CustomerInsertedHandler(Customer newCustomer);
public event CustomerInsertedHandler CustomerInserted;
public void OnCustomerInserted(Customer newCustomer)
{
if (CustomerInserted != null)
CustomerInserted(newCustomer);
}
public void Add(Customer customer)
{
// add the customer
customer.Id = 2;
if(customer.Id > 0)
{
var emailService = new EmailService();
CustomerInserted += emailService.Send;
// raise the event!
OnCustomerInserted(customer);
}
}
}
I updated my repository and is using StructorMap to create instance of the Repository
public class CustomerRepository : ICustomerRepository
{
public delegate void CustomerInsertedHandler(Customer newCustomer);
public event CustomerInsertedHandler CustomerInserted;
private IEmailService _emailService;
public CustomerRepository(IEmailService emailService)
{
_emailService = emailService;
CustomerInserted += _emailService.Send;
}
The code is still not good since now the CustomerRepository constructor is getting crowded. The EmailService is clean as follows:
public class EmailService : IEmailService
{
public EmailService()
{
}
public void Send(Customer customer)
{
Console.WriteLine("Email Sent!");
}
}
Udi Dahan has a good example on his blog: http://www.udidahan.com/2008/08/25/domain-events-take-2/.
The basic idea is to create an Event as a first class object in your domain model. Then whoever needs to can subscribe to the event, and also publish it.
Move CustomerInserted from Customer to CustomerRepository and you'll be just fine. And tweak CustomerInsertedHandler so that it will contain a reference to the inserted Customer object:
public delegate void CustomerInsertedHandler(Customer insertedCustomer);
Or do it more in line with the rest of .NET:
public delegate void CustomerInsertedHandler(object sender, CustomerInsertedEventArgs e);
So the overall structure will be as follows:
public delegate void CustomerInsertedHandler(Customer newCustomer);
public class CustomerRepository
{
public event CustomerInsertedHandler CustomerInserted;
private void RaiseCustomerInserted(Customer newCustomer)
{
if(CustomerInserted != null)
CustomerInserted(newCustomer);
}
public void Add(Customer customer)
{
// add the customer
customer.Id = 2;
if(customer.Id > 0)
RaiseCustomerInserted(customer);
}
}
public class EmailService
{
// Using autoproperty syntax from C# 3.0
public CustomerRepository { get; set; }
public EmailService()
{
}
public void Initialize()
{
//
// You need to get hold of a reference to CustomerRepository somehow.
// Google for either "Dependency Injection" or "Service Locator".
CustomerRepository.CustomerInserted += delegate(Customer c)
{ Send(c); };
}
public void Send(Customer customer)
{
Console.WriteLine("Email has been sent to " + customer.Name);
}
}
Related
How to invoke function Run() from Controller class in class I2C?
class Program
{
public class Controller
{
public void Run()
{
}
}
public class ChildController : Controller
{
}
public class LowLevelController : ChildController
{
private class I2C
{
public I2C()
{
}
// Want to call Controller.Run() from this level
}
public void Computate()
{
base.Run();
}
}
}
Option 1
One way to achieve this would be exposing method in I2C which accepts an Action. This would allow the instance of I2C, which is a private class defined within LowLevelController to invoke Controller.Run. For example,
private class I2C
{
public I2C()
{
}
public void RunBase(Action execute)
{
execute.Invoke();
}
}
Now you could execute the RunBase as
public void Computate()
{
var i2c = new I2C();
i2c.RunBase(()=>base.Run());
}
Option 2
The other option would be to pass an instance of LowLevelController to I2C and invoke the Controller.Run method
Example,
public class LowLevelController : ChildController
{
private class I2C
{
private LowLevelController _parent;
public I2C(LowLevelController parent)
{
_parent = parent;
}
public void RunBase()
{
_parent.Run();
}
}
public void Computate()
{
var i2c = new I2C(this);
i2c.RunBase();
}
}
I think what you want is simply:
public class LowLevelController : ChildController {
private class I2C {
public I2C(LowLevelController outerInstance) {
OuterInstance = outerInstance;
}
private LowLevelController OuterInstance { get; }
private void DoSomething() {
OuterInstance.Run();
}
}
}
I have a view for which I'd like to mock the Show behaviour.
Once the credentials have been entered, the [Connecter] button enables itself, and then the user can click. I wish I could reproduce this behaviour without having to show the view and actually really enter my credentials.
The application is a WinForms MDI presented by the IApplicationPresenter. The IApplicationPresenter raises the ShowView to which the IApplicationView subscribed.
Then, when the IApplicationView.Shown, the IApplicationPresenter forces the user to authenticate like this:
IApplicationPresenter.OnViewShown
public void OnViewShown() { forceAuthentication(); }
private void forceAuthentication() {
IAuthenticationView authView = new AuthenticationView();
IAuthenticationPrenseter authPresenter = new AuthenticationPresenter();
authPresenter.ShowView();
}
It's like I can smell one thing.
It's just like I could inject the IAuthenticationView to the IApplicationPresenter. Then, this would allow me to inject my mocked view to it, and avoid the view being actually shown, which is in fact what I want to come up with. Is it the best way to make it?
Now, I want to test that when the IApplicationView is shown, the IApplicationPresenter is notified and forces authentication.
Any thoughts of a better approach in terms of mocking here?
UPDATE
IView
public interface IView {
void CloseView();
void SetTitle(string title);
void ShowView();
void RaiseVoidEvent(VoidEventHandler #event);
event VoidEventHandler OnViewInitialize;
event VoidEventHandler OnViewShown;
}
IApplicationView
public interface IApplicationView : IView {
void OnUserAuthenticated();
event VoidEventHandler ManageRequestsClicked;
}
IPresenter
public interface IPresenter<V> where V : IView {
V View { get; }
IDatabaseUser CurrentUser { get; }
void CloseView();
void OnViewInitialize();
void RaiseVoidEvent(VoidEventHandler #event);
void ShowView();
event VoidEventHandler OnCloseView;
event VoidEventHandler OnShowView;
}
Presenter
public abstract class Presenter<V> : IPresenter<V> where V : IView {
public Presenter(V view) {
if (view == null) throw new ArgumentNullException("view");
View = view;
View.OnViewInitialize += OnViewInitialize;
OnCloseView += View.CloseView;
OnShowView += View.ShowView;
}
public virtual IDatabaseUser CurrentUser { get; protected set; }
public virtual V View { get; private set; }
public virtual void CloseView() { RaiseVoidEvent(OnCloseView); }
public virtual void OnViewInitialize() { }
public void RaiseVoidEvent(VoidEventHandler #event) { if (#event != null) #event(); }
public virtual void ShowView() { RaiseVoidEvent(OnShowView); }
public virtual event VoidEventHandler OnCloseView;
public virtual event VoidEventHandler OnShowView;
}
IApplicationPresenter
public interface IApplicationPresenter : IPresenter<IApplicationView> {
IAuthenticationPresenter AuthenticationPresenter { get; set; }
void OnManageRequestsClicked();
void OnUserAuthenticated(UserAuthenticatedEventArgs e);
void OnViewShown();
}
ApplicationPresenter
public class ApplicationPresenter : Presenter<IApplicationView>, IApplicationPresenter {
public ApplicationPresenter(IApplicationView view) : this(view, null) { }
public ApplicationPresenter(IApplicationView view, IAuthenticationPresenter authPresenter) : base(view) {
AuthenticationPresenter = authPresenter;
View.OnViewShown += OnViewShown;
View.ManageRequestsClicked += OnManageRequestsClicked;
}
public IAuthenticationPresenter AuthenticationPresenter { get { return authenticationPresenter; } set { setAuthenticationPresenter(value); } }
public void OnManageRequestsClicked() {
var requests = new GestionDemandeAccesInformationForm();
requests.Database = database;
requests.MdiParent = (System.Windows.Forms.Form)View;
requests.Show();
}
public void OnUserAuthenticated(UserAuthenticatedEventArgs e) {
CurrentUser = new DatabaseUser(e.Login, e.Password, e.DatabaseInstance);
database = new DatabaseSessionFactory(CurrentUser);
setAppTitle();
showRequestsManagementView();
}
public void OnViewShown() { forceAuthentication(); }
}
IAuthenticationView
public interface IAuthenticationView : IView {
string ErrorMessage { get; set; }
string Instance { get; set; }
IEnumerable<string> Instances { get; set; }
string Login { get; set; }
string Password { get; set; }
void EnableConnectButton(bool enabled);
void SetDefaultInstance(string defaultInstance);
void RaiseSelectionChangedEvent(SelectionChangedEventHandler #event, SelectionChangedEventArgs e);
event VoidEventHandler OnConnect;
event SelectionChangedEventHandler OnDatabaseInstanceChanged;
event VoidEventHandler OnLoginChanged;
event VoidEventHandler OnPasswordChanged;
}
IAuthenticationPresenter
public interface IAuthenticationPresenter : IValidatablePresenter, IPresenter<IAuthenticationView> {
void OnConnect();
void OnViewDatabaseInstanceChanged(SelectionChangedEventArgs e);
void OnViewLoginChanged();
void OnViewPasswordChanged();
void RaiseUserAuthenticatedEvent(UserAuthenticatedEventArgs e);
event UserAuthenticatedEventHandler UserAuthenticated;
}
AuthenticationPresenter
public class AuthenticationPresenter : Presenter<IAuthenticationView>, IAuthenticationPresenter {
public AuthenticationPresenter(IAuthenticationView view, IMembershipService service) : base(view) {
MembershipService = service;
View.ErrorMessage = null;
View.SetTitle(ViewTitle);
subscribeToEvents();
}
public bool IsValid { get { return credentialsEntered(); } }
public IMembershipService MembershipService { get; set; }
public virtual void OnConnect() {
if (noDatabaseInstanceSelected()) display(MissingInstanceErrorMessage);
else if (noLoginEntered()) display(MissingLoginErrorMessage);
else if (noPasswordEntered()) display(MissingPasswordErrorMessage);
else {
display(EverythingIsFine);
if (isAuthenticUser()) notifyTheApplicationThatTheUserIsAuthentic();
else { display(InvalidLoginOrPasswordErrorMessage); }
}
}
public override void OnViewInitialize() {
base.OnViewInitialize();
View.ErrorMessage = null;
View.Instances = Configuration.DatabaseInstances;
View.SetDefaultInstance(Configuration.DefaultInstance);
}
public void OnViewDatabaseInstanceChanged(SelectionChangedEventArgs e) { View.Instance = (string)e.Selected; }
public void OnViewLoginChanged() { View.EnableConnectButton(IsValid); }
public void OnViewPasswordChanged() { View.EnableConnectButton(IsValid); }
public void RaiseUserAuthenticatedEvent(UserAuthenticatedEventArgs e) { if (UserAuthenticated != null) UserAuthenticated(e); }
public event UserAuthenticatedEventHandler UserAuthenticated;
}
If I were you, I'd inject a factory for creating AuthenticationPresenter and in your test I would call OnViewShown() and verify on your mock (of the presenter returned by the factory) that ShowView is called.
EDIT
Note that I haven't compiled this, I don't have a C# compiler right now.
Here is my version of the test. Based on my interpretation of what you really want to test :
[TestClass]
public class ApplicationPresenterTests
{
[TestClass]
public class OnViewShown : ApplicationPresenterTests
{
[TestMethod]
public void ForceAuthentication()
{
// given
var authenticationPresenterFactory = new Mock<IAuthenticationPresenterFactory>();
var authenticationPresenter = new Mock<IAuthenticationPresenter>();
authenticationPresenterFactory.Setup(f => f.create()).Returns(authenticationPresenter.Object);
var presenter = new ApplicationPresenter(authenticationPresenterFactory);
// when
presenter.OnViewShown();
// then
authenticationPresenter.Verify(p => p.ShowView());
}
}
So far, I have come up with this solution which works flawlessly.
It's all about setting up the mock object to work as expected.
[TestClass]
public abstract class ApplicationPresenterTests {
[TestClass]
public class OnViewShown : ApplicationPresenterTests {
[TestMethod]
public void ForceAuthentication() {
// arrange
// act
Presenter.OnViewShown();
var actual = Presenter.CurrentUser;
// assert
Assert.IsNotNull(actual);
Assert.IsInstanceOfType(actual, typeof(IDatabaseUser));
}
}
[TestInitialize]
public void ApplicationMainPresenterSetUp() {
Mock<IAuthenticationView> authView = new Mock<IAuthenticationView>(MockBehavior.Strict);
authView.SetupProperty(v => v.ErrorMessage);
authView.SetupGet(v => v.Instance).Returns(RandomValues.RandomString());
authView.SetupGet(v => v.Login).Returns(RandomValues.RandomString());
authView.SetupGet(v => v.Password).Returns(RandomValues.RandomString());
authView.Setup(v => v.CloseView());
authView.Setup(v => v.SetTitle(It.IsAny<string>()));
authView.Setup(v => v.ShowView()).Raises(v => v.OnConnect += null).Verifiable();
Mock<IMembershipService> authService = new Mock<IMembershipService>(MockBehavior.Loose);
authService.Setup(s => s.AuthenticateUser(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Returns(true);
IAuthenticationPresenter authPresenter = new AuthenticationPresenter(authView.Object, authService.Object);
ApplicationView = new ApplicationForm();
Presenter = new ApplicationPresenter(ApplicationView, authPresenter);
}
protected IApplicationView ApplicationView { get; private set; }
protected IApplicationPresenter Presenter { get; private set; }
}
Therefore, the key change was to inject the dependancy of an IAuthenticationPresenter into the IApplicationPresenter, hence the ApplicationPresenter constructor overload.
Though this has solved my problem, I better understand the need for a PresenterFactory being injected into the ApplicationPresenter, since this is the presenter which handles everything in the application, that is, the calls to other views for each which has its own presenter.
Before me lies an even more complex challenge to take on. Stay tuned!
I just did something kind of wacky using a partial class and I'm wondering if there's an already established pattern that might have accomplished the same thing using a less confusing approach.
The problem:
I had a base class with protected members and virtual methods designed for the derived class to do work when they are called.
I wanted to delegate this work out to a list of workers.
However I needed the workers to have access to the protected members.
My probably overly complicated solution:
Note: I realize this depends on the class being partial - I'm OK with that but it would be cool if there was a solution that didn't need it...
void Main()
{
ABase aBase = new ADerived();
aBase.DoWork();
}
public partial class ABase
{
protected int state1 = 1;
protected int state2 = 2;
List<ABase> workers;
public ABase()
{
workers = new List<ABase>();
CreateWorkers(workers);
}
protected virtual void CreateWorkers(List<ABase> workers)
{
}
public ABase(ABase aBase)
{
this.Target = aBase;
}
public virtual void DoWork()
{
foreach (var worker in this.workers)
{
worker.DoWork();
}
}
protected ABase Target { get; private set; }
}
public partial class ABase
{
public class Worker1 : ABase
{
public Worker1(ABase aBase) : base(aBase) { }
public override void DoWork()
{
Console.WriteLine (Target.state1);
}
}
public class Worker2 : ABase
{
public Worker2(ABase aBase) : base(aBase) { }
public override void DoWork()
{
Console.WriteLine (Target.state2);
}
}
}
public class ADerived : ABase
{
protected override void CreateWorkers(List<ABase> workers)
{
workers.Add(new Worker1(this));
workers.Add(new Worker2(this));
}
}
Output:
1
2
I would change the design so that the workers weren't accessing instance fields at all. I would have DoWork take a parameter with the information that they need and have the base class pass the state into the DoWork method.
public class MyState //TODO give better name
{
public int State1 { get; set; }
public int State2 { get; set; }
}
public class ABase
{
public MyState state = new MyState()
{
State1 = 1,
State2 = 2
};
List<Action<MyState>> workers = new List<Action<MyState>>();
public ABase()
{
CreateWorkers();
}
public void DoWork()
{
foreach (var action in workers)
{
action(state);
}
}
private void CreateWorkers()
{
workers.Add(new Worker1().DoWork);
workers.Add(Worker2.Process);
}
}
public class Worker1
{
public void DoWork(MyState state)
{
Console.WriteLine(state.State1);
}
}
public class Worker2
{
public static void Process(MyState state)
{
Console.WriteLine(state.State2);
}
}
Let's assume that I have this scenario: I have got 2 repositories of information, and I want to access both, but it would be nice to leave the task of deciding which repo to use to common class.
The goal is to accomplish this with something similar to the code I've wrote below, but this sounds pretty bad:
where TOnline : class
where TOffline : class
where TContract : class
Sure I can ommit that, but bassically what I'm asking is what to do in order to stop using reflection and go typed. Maybe any design-pattern recomendation?
Code (if you copy/paste this on a console app replacing the Program class you should be able to run the example)
using CustomerDispatcher = DispatcherProxy<CustomerOnline, CustomerOffline, ICustomer>;
public interface ICustomer
{
string Get(int id);
}
public class CustomerOnline : ICustomer
{
public string Get(int id)
{
// Get From intranet DB
return "From DB";
}
}
public class CustomerOffline : ICustomer
{
public string Get(int id)
{
// Get From local storage
return "From local storage";
}
}
public class DispatcherProxy<TOnline, TOffline, TContract>
where TOnline : class
where TOffline : class
where TContract : class
{
public TContract Instance { get; set; }
public bool IsConnected { get; set; }
public DispatcherProxy()
{
// Asume that I check if it's connected or not
if (this.IsConnected)
this.Instance = (TContract)Activator.CreateInstance(typeof(TOnline));
else
this.Instance = (TContract)Activator.CreateInstance(typeof(TOffline));
}
}
class Program
{
static void Main(string[] args)
{
var customerDispatcher = new CustomerDispatcher();
Console.WriteLine("Result: " + customerDispatcher.Instance.Get(1));
Console.Read();
}
}
Thanks in advance!
You can add the new() constraint:
public class DispatcherProxy<TOnline, TOffline, TContract>
where TOnline : class, new()
where TOffline : class, new()
where TContract : class //isn't TContract an interface?
{
public TContract Instance { get; set; }
public bool IsConnected { get; set; }
public DispatcherProxy()
{
// Asume that I check if it's connected or not
if (this.IsConnected)
this.Instance = new TOnline() as TContract;
else
this.Instance = new TOffline() as TContract;
}
}
In case any of you are interested, I had to change the way I did this because it was checking connection at Constructor Level, and I needed that check at Operation Level.
using System;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;
namespace ConsoleApplication1
{
public enum ConnectionStatus
{
Online,
Offline,
System // System checks connectivity
}
public static class Connectivity
{
private static ConnectionStatus ConnectionStatus = ConnectionStatus.Offline;
public static void ForceConnectionStatus(ConnectionStatus connectionStatus)
{
ConnectionStatus = connectionStatus;
}
public static bool IsConnected()
{
switch (ConnectionStatus)
{
case ConnectionStatus.Online:
return true;
case ConnectionStatus.Offline:
return false;
case ConnectionStatus.System:
return CheckConnection();
}
return false;
}
private static bool CheckConnection()
{
return true;
}
}
public class Unity
{
public static IUnityContainer Container;
public static void Initialize()
{
Container = new UnityContainer();
Container.AddNewExtension<Interception>();
Container.RegisterType<ILogger, OnlineLogger>();
Container.Configure<Interception>().SetInterceptorFor<ILogger>(new InterfaceInterceptor());
}
}
class Program
{
static void Main(string[] args)
{
Unity.Initialize();
var r = new Router<ILogger, OnlineLogger, OnlineLogger>();
Connectivity.ForceConnectionStatus(ConnectionStatus.Offline);
Console.WriteLine("Calling Online, will attend offline: ");
r.Logger.Write("Used offline.");
Connectivity.ForceConnectionStatus(ConnectionStatus.Online);
Console.WriteLine("Calling Online, will attend online: ");
r.Logger.Write("Used Online. Clap Clap Clap.");
Console.ReadKey();
}
}
public class Router<TContract, TOnline, TOffline>
where TOnline : TContract
where TOffline : TContract
{
public TContract Logger;
public Router()
{
Logger = Unity.Container.Resolve<TContract>();
}
}
public interface IOnline
{
IOffline Offline { get; set; }
}
public interface IOffline
{
}
public interface ILogger
{
[Test()]
void Write(string message);
}
public class OnlineLogger : ILogger, IOnline
{
public IOffline Offline { get; set; }
public OnlineLogger()
{
this.Offline = new OfflineLogger();
}
public void Write(string message)
{
Console.WriteLine("Online Logger: " + message);
}
}
public class OfflineLogger : ILogger, IOffline
{
public IOnline Online { get; set; }
public void Write(string message)
{
Console.WriteLine("Offline Logger: " + message);
}
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
public class TestAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(IUnityContainer container)
{
return new TestHandler();
}
}
public class TestHandler : ICallHandler
{
public int Order { get; set; }
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
Console.WriteLine("It's been intercepted.");
if (!Connectivity.IsConnected() && input.Target is IOnline)
{
Console.WriteLine("It's been canceled.");
var offline = ((input.Target as IOnline).Offline);
if (offline == null)
throw new Exception("Online class did not initialized Offline Dispatcher.");
var offlineResult = input.MethodBase.Invoke(offline, this.GetObjects(input.Inputs));
return input.CreateMethodReturn(offlineResult, this.GetObjects(input.Inputs));
}
return getNext()(input, getNext);
}
private object[] GetObjects(IParameterCollection parameterCollection)
{
var parameters = new object[parameterCollection.Count];
int i = 0;
foreach (var parameter in parameterCollection)
{
parameters[i] = parameter;
i++;
}
return parameters;
}
}
}
Below is my implementation of the state pattern. In order to persist the State object to my database with NHibernate, I am assigning each state class an enum value. This is stored as a private field on the entity, and mapped to a integer field in my database table.
I want to know whether this is a good implementation as I will be using the state pattern throughout my application and want to get it right the first time. Thanks
public class Order
{
private OrderStatusEnum _statusId;
public virtual Guid Id { get; set; }
private OrderState _status;
public virtual OrderState Status {
get
{
if (_status == null)
_status = GetState(_statusId);
return _status;
}
set
{
_status = value;
_statusId = _status.Id;
}
}
private OrderState GetState(OrderStatusEnum status)
{
switch (_statusId) {
case OrderStatusEnum.Pending:
return new Submitted(this);
case OrderStatusEnum.Completed:
return new Completed(this);
default:
return new NewOrder(this);
}
}
}
public abstract class OrderState
{
private readonly Order _order;
public OrderState(Order order) {
_order = order;
}
internal Order Order { get { return _order; } }
public abstract OrderStatusEnum Id { get; }
public virtual void Submit() {
throw new InvalidOperationException(
string.Format("Can't Submit a {0} Order", this.GetType().Name)
);
}
public virtual void Complete() {
throw new InvalidOperationException(
string.Format(string.Format("Can't Cancel a {0} Order", this.GetType().Name))
);
}
protected internal void _Submit() {
Order.Status = new Submitted(Order);
}
protected internal void _Complete() {
Order.Status = new Completed(Order);
}
}
public class NewOrder : OrderState
{
public NewOrder(Order order) : base(order) { }
public override OrderStatusEnum Id {
get { return OrderStatusEnum.New; }
}
public override void Submit() {
_Submit();
}
}
public class Submitted : OrderState
{
public Submitted(Order order) : base(order) { }
public override OrderStatusEnum Id {
get { return OrderStatusEnum.Pending; }
}
public override void Complete() {
_Complete();
}
}
public class Completed : OrderState
{
public Completed(Order order) : base(order) { }
public override OrderStatusEnum Id {
get { return OrderStatusEnum.Completed; }
}
}
public enum OrderStatusEnum {
New = 1,
Pending = 2,
Completed = 3
}
Not sure whether to answer or add a comment, but your approach worked very well for me in a similar situation.
I also experimented with the approach described here using the Tarantino framework, but I found it easier to extend from your code.