using System;
using System.Collections.Generic;
using System.Text;
using Ubik.Engine.Client;
namespace StoresAndStockPricing.Model{
/// <summary>
/// Abstract base class for scheduled events such as price changes.
/// </summary>
public abstract class DataMaintenanceEvent : Individual
{
TrackedProperty<DateTime> _plannedDateTime;
TrackedProperty<DateTime?> _detectedDateTime;
TrackedProperty<DataMaintenanceEventStatus> _status;
TrackedString _failureMessage;
TrackedString _failureTrace;
const int MaxFailureTrace = StandardFieldLengths.NotesLength;
protected DataMaintenanceEvent(Session session)
: base(session)
{
_plannedDateTime = new TrackedProperty<DateTime>(this, "PlannedDateTime");
_detectedDateTime = new TrackedProperty<DateTime?>(this, "DetectedDateTime");
_status = new TrackedProperty<DataMaintenanceEventStatus>(this, "Status");
_failureMessage = new TrackedString(this, "FailureMessage");
_failureTrace = new TrackedString(this, "FailureTrace", MaxFailureTrace);
}
protected DataMaintenanceEvent(Session session, DateTime plannedDateTime)
: this (session)
{
if (plannedDateTime < EngineDateTime.UtcNow)
throw new ArgumentOutOfRangeException("plannedDateTime");
_plannedDateTime.Reset(plannedDateTime);
_status.Reset(DataMaintenanceEventStatus.Pending);
}
public DateTime PlannedDateTime
{
get
{
return _plannedDateTime.Value;
}
}
public DateTime? DetectedDateTime
{
get
{
return _detectedDateTime.Value;
}
}
public DataMaintenanceEventStatus Status
{
get
{
return _status.Value;
}
}
public string FailureMessage
{
get
{
return _failureMessage.Value;
}
}
public string FailureTrace
{
get
{
return _failureTrace.Value;
}
}
public abstract string Description
{
get;
}
/// <summary>
/// This should be called an the transaction committed when the event is detected by the
/// event daemon, i.e. before the transaction in which Apply is called.
/// </summary>
public void Detected()
{
if (Status != DataMaintenanceEventStatus.Pending)
throw new InvalidOperationException(DataMaintenanceEventResources.EventNotPending);
_status.Value = DataMaintenanceEventStatus.Detected;
_detectedDateTime.Value = EngineDateTime.UtcNow;
}
/// <summary>
/// Perform the action represented by the event. Apply can only be called if the event is in the Pending state.
/// </summary>
public void Apply()
{
if (Status != DataMaintenanceEventStatus.Detected)
throw new InvalidOperationException(DataMaintenanceEventResources.EventNotDetected);
ApplyImpl();
_status.Value = DataMaintenanceEventStatus.Complete;
}
protected abstract void ApplyImpl();
/// <summary>
/// Cancel the event. Cancel can only be called if the event is in the Pending state.
/// </summary>
public void Cancel()
{
if (Status != DataMaintenanceEventStatus.Pending)
throw new InvalidOperationException(DataMaintenanceEventResources.EventNotPending);
_status.Value = DataMaintenanceEventStatus.Cancelled;
}
/// <summary>
/// Record the failure of event application. This can only be done if the event is in the
/// Detected state, and should be done in a separate transaction to the Apply atempt.
/// </summary>
/// <param name="ex">The exception that previously caused the event to fail.</param>
public void RecordFailure(Exception ex)
{
if (Status != DataMaintenanceEventStatus.Detected)
throw new InvalidOperationException(DataMaintenanceEventResources.EventNotDetected);
_failureMessage.Value = ex.Message;
string trace = ex.StackTrace;
if (trace.Length > MaxFailureTrace)
trace = trace.Substring(0, MaxFailureTrace);
_failureTrace.Value = trace;
_status.Value = DataMaintenanceEventStatus.Failed;
}
}
}
|