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; } } } }