using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using IndianHealthService.BMXNet.Services;
using System.Windows.Forms;
namespace IndianHealthService.BMXNet.Ado
{
///
/// DataTableFutures are created synchrounously when Async calls are made on a RemoteSession.
/// A DataTabelFuture is used to manage each async call and has a variety of methods to support
/// the relativelty long and dynamic lifecycle and an Async call.
///
///
/// Beware that all return communications are not on the UI thread unless an InvokedControl
/// is specify.
///
public class DataTableFuture
{
///
/// Triggered with the async returns, see DataTableFutureEventArgs to know if there
/// is a result waiting. It's very common to hook the Returned event after each Async call and
/// then unhook it when the Returned or Aborted calls return.
///
public event EventHandler Returned;
///
/// The async call was aborted by either the server or the client. It's very common to hook the Aborted event after each Async call and
/// then unhook it when the Returned or Aborted calls return.
///
///
public event EventHandler Aborted;
private Control _invokedControl = null;
/// If the InvokedControl is set to a valid Form or Control object (non-displosed), the
/// DataTabletFuture will trigger events on the UI thread using Invoke()
public Control InvokedControl
{
get { return _invokedControl; }
set { _invokedControl = value; }
}
private bool _isAutoFetchEnabled = true;
///
/// If set to true the result will be fetched before the Returned event is called, otherwise
/// the Returned event will be triggered and the application code needs to call PostFetch() to retrieve
/// the result. True by default.
///
public bool IsAutoFetchEnabled
{
get { return _isAutoFetchEnabled; }
set { _isAutoFetchEnabled = value; }
}
private Exception _errorException = null;
///
/// Access to any exceptions that occur during Async call.
///
public Exception ErrorException
{
get { return _errorException; }
set { _errorException = value; }
}
private int _resultId = 0;
internal int ResultId
{
get { return _resultId; }
set { _resultId = value; }
}
private String _resultKey = null;
protected String ResultKey
{
get { return _resultKey; }
set { _resultKey = value; }
}
private DataTable _result = null;
///
/// The result table if any, or null.
///
public DataTable Result
{
get { return _result; }
set { _result = value; }
}
private bool _wasCancelled = false;
///
/// Answer true if canceled. It's common that a dialog would allow the user to cancel the
/// Async call and the Dialog that is holding on the the Future will cancel the Future. Cancelling
/// the Future does not stop the server-side processing but when the server returns nothing will happen.
///
public bool WasCancelled
{
get { return _wasCancelled; }
set { _wasCancelled = value; }
}
private bool _hasTimedOut = false;
///
/// Answer true if the Async call has timeed-out (a time-based version of Cancel)
///
public bool HasTimedOut
{
get { return _hasTimedOut; }
set { _hasTimedOut = value; }
}
private bool _hasReturned = false;
///
/// Answer true if the Async call has completed on the server and that a result is ready.
///
public bool HasReturned
{
get { return _hasReturned; }
set
{
_hasReturned = value;
this.FutureTime = DateTime.Now;
}
}
private bool _hasData = false;
///
/// Answer true if the data has been fetched from the server and is ready to be used by the application.
///
public bool HasData
{
get { return _hasData; }
set { _hasData = value; }
}
private DateTime _presentTime = DateTime.Now;
///
/// The time the Future was created.
///
public DateTime PresentTime
{
get { return _presentTime; }
set { _presentTime = value; }
}
private DateTime? _futureTime = null;
///
/// The time the async call returned (the future)
///
public DateTime? FutureTime
{
get { return _futureTime; }
set { _futureTime = value; }
}
///
/// How long in milliseconds the Async call took to complete.
///
public double WaitTime
{
get
{
return ((this.FutureTime.HasValue ? this.FutureTime.Value : DateTime.Now) - this.PresentTime).TotalMilliseconds;
}
}
private int _maxWaitTime = 60 * 60 * 24;
///
/// Number of seconds to wait for an async response before returning without a response.
///
public int MaxWaitTime
{
get { return _maxWaitTime; }
set { _maxWaitTime = value; }
}
private BMXNetRemoteSession _session = null;
internal BMXNetRemoteSession Session
{
get { return _session; }
set { _session = value; }
}
///
/// Method to cancel the Async call. This does not stop the server-side processing. The Aborted event
/// will be called immediately.
///
public void Cancel()
{
this.WasCancelled = true;
this.Session.RemoveFromFutures(this);
this.TriggerAbortedEvent();
}
delegate void UiCall();
protected void TriggerAbortedEvent()
{
if (this.Aborted != null)
{
if (this.InvokedControl != null && this.InvokedControl.InvokeRequired)
{
this.InvokedControl.Invoke(new UiCall(UiTriggerAbortedEvent));
}
else
{
this.UiTriggerAbortedEvent();
}
}
}
private void UiTriggerAbortedEvent()
{
DataTableFutureEventArgs args = new DataTableFutureEventArgs();
args.Future = this;
this.Aborted(this, args);
}
protected void TriggerReturnedEvent()
{
if (this.Returned != null)
{
if (this.InvokedControl != null && this.InvokedControl.InvokeRequired)
{
this.InvokedControl.Invoke(new UiCall(UiTriggerReturnedEvent));
}
else
{
this.UiTriggerReturnedEvent();
}
}
}
private void UiTriggerReturnedEvent()
{
DataTableFutureEventArgs args = new DataTableFutureEventArgs();
args.Future = this;
this.Returned(this, args);
}
internal void FetchResults(BMXNetRemoteSession aSession)
{
if (!this.HasReturned || this.HasTimedOut || this.WasCancelled)
return;
try
{
this.Result = aSession.TableFromCommand("BMX ASYNC GET^" + this.ResultKey, this.ResultDataSet, this.ResultTableName, this.ResultAppContext);
this.HasData = true;
}
catch (Exception problem)
{
this.ErrorException = problem;
this.Cancel();
}
}
private BMXNetRemoteSession _fetchingSession = null;
internal BMXNetRemoteSession FetchingSession
{
get { return _fetchingSession; }
set { _fetchingSession = value; }
}
///
/// If IsAutoFetchEnabled is false, then the results must be fetched synchrounously useding PostFetch().
/// Call PostFetch() once and check if HasData is true. If not true, there is was error related to the
/// async call.
///
public void PostFetch()
{
this.FetchResults(this.FetchingSession);
}
internal void FutureHasReturned(BMXNetRemoteSession aSession, String resultsKey)
{
//TODO:If cancled, BMX ASYNC GET to clean cancelle ditem
if (this.HasReturned)
return;
this.ResultKey = resultsKey;
this.HasReturned = true;
if (!this.HasTimedOut && !this.WasCancelled)
{
if (this.IsAutoFetchEnabled)
{
this.FetchResults(aSession);
}
else
{
this.FetchingSession = aSession;
}
}
this.TriggerReturnedEvent();
}
private String _resultTableName = null;
public String ResultTableName
{
get { return _resultTableName; }
set { _resultTableName = value; }
}
private DataSet _resultDataSet = null;
///
/// The data set the result data table is in.
///
public DataSet ResultDataSet
{
get { return _resultDataSet; }
set { _resultDataSet = value; }
}
private String _resultAppContext = null;
///
/// The AppContext that the async call was made with
///
public String ResultAppContext
{
get { return _resultAppContext; }
set { _resultAppContext = value; }
}
}
}