| 1 | using System; | 
|---|
| 2 | using System.Collections.Generic; | 
|---|
| 3 | using System.Text; | 
|---|
| 4 | using System.Data; | 
|---|
| 5 | using IndianHealthService.BMXNet.Services; | 
|---|
| 6 | using System.Windows.Forms; | 
|---|
| 7 |  | 
|---|
| 8 | namespace IndianHealthService.BMXNet.Ado | 
|---|
| 9 | { | 
|---|
| 10 | /// <summary> | 
|---|
| 11 | /// DataTableFutures are created synchrounously when Async calls are made on a RemoteSession. | 
|---|
| 12 | /// A DataTabelFuture is used to manage each async call and has a variety of methods to support | 
|---|
| 13 | /// the relativelty long and dynamic lifecycle and an Async call. | 
|---|
| 14 | /// </summary> | 
|---|
| 15 | /// <remarks> | 
|---|
| 16 | /// Beware that all return communications are not on the UI thread unless an InvokedControl | 
|---|
| 17 | /// is specify. | 
|---|
| 18 | /// </remarks> | 
|---|
| 19 | public class DataTableFuture | 
|---|
| 20 | { | 
|---|
| 21 |  | 
|---|
| 22 | /// <summary> | 
|---|
| 23 | /// Triggered with the async returns, see DataTableFutureEventArgs to know if there | 
|---|
| 24 | /// is a result waiting.  It's very common to hook the Returned event after each Async call and | 
|---|
| 25 | /// then unhook it when the Returned or Aborted calls return. | 
|---|
| 26 | /// </summary> | 
|---|
| 27 | public event EventHandler<DataTableFutureEventArgs> Returned; | 
|---|
| 28 |  | 
|---|
| 29 | /// <summary> | 
|---|
| 30 | /// 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 | 
|---|
| 31 | /// then unhook it when the Returned or Aborted calls return. | 
|---|
| 32 | /// </summary> | 
|---|
| 33 | /// </summary> | 
|---|
| 34 | public event EventHandler<DataTableFutureEventArgs> Aborted; | 
|---|
| 35 |  | 
|---|
| 36 | private Control _invokedControl = null; | 
|---|
| 37 |  | 
|---|
| 38 |  | 
|---|
| 39 | /// If the InvokedControl is set to a valid Form or Control object (non-displosed), the | 
|---|
| 40 | /// DataTabletFuture will trigger events on the UI thread using Invoke() | 
|---|
| 41 | public Control InvokedControl | 
|---|
| 42 | { | 
|---|
| 43 | get { return _invokedControl; } | 
|---|
| 44 | set { _invokedControl = value; } | 
|---|
| 45 | } | 
|---|
| 46 |  | 
|---|
| 47 | private bool _isAutoFetchEnabled = true; | 
|---|
| 48 |  | 
|---|
| 49 | /// <summary> | 
|---|
| 50 | /// If set to true the result will be fetched before the Returned event is called, otherwise | 
|---|
| 51 | /// the Returned event will be triggered and the application code needs to call PostFetch() to retrieve | 
|---|
| 52 | /// the result.  True by default. | 
|---|
| 53 | /// </summary> | 
|---|
| 54 | public bool IsAutoFetchEnabled | 
|---|
| 55 | { | 
|---|
| 56 | get { return _isAutoFetchEnabled; } | 
|---|
| 57 | set { _isAutoFetchEnabled = value; } | 
|---|
| 58 | } | 
|---|
| 59 |  | 
|---|
| 60 | private Exception _errorException = null; | 
|---|
| 61 |  | 
|---|
| 62 |  | 
|---|
| 63 | /// <summary> | 
|---|
| 64 | /// Access to any exceptions that occur during Async call. | 
|---|
| 65 | /// </summary> | 
|---|
| 66 | public Exception ErrorException | 
|---|
| 67 | { | 
|---|
| 68 | get { return _errorException; } | 
|---|
| 69 | set { _errorException = value; } | 
|---|
| 70 | } | 
|---|
| 71 |  | 
|---|
| 72 | private int _resultId = 0; | 
|---|
| 73 |  | 
|---|
| 74 | internal int ResultId | 
|---|
| 75 | { | 
|---|
| 76 | get { return _resultId; } | 
|---|
| 77 | set { _resultId = value; } | 
|---|
| 78 | } | 
|---|
| 79 |  | 
|---|
| 80 | private String _resultKey = null; | 
|---|
| 81 |  | 
|---|
| 82 | protected String ResultKey | 
|---|
| 83 | { | 
|---|
| 84 | get { return _resultKey; } | 
|---|
| 85 | set { _resultKey = value; } | 
|---|
| 86 | } | 
|---|
| 87 |  | 
|---|
| 88 |  | 
|---|
| 89 | private DataTable _result = null; | 
|---|
| 90 |  | 
|---|
| 91 | /// <summary> | 
|---|
| 92 | /// The result table if any, or null. | 
|---|
| 93 | /// </summary> | 
|---|
| 94 | public DataTable Result | 
|---|
| 95 | { | 
|---|
| 96 | get { return _result; } | 
|---|
| 97 | set { _result = value; } | 
|---|
| 98 | } | 
|---|
| 99 |  | 
|---|
| 100 | private bool _wasCancelled = false; | 
|---|
| 101 |  | 
|---|
| 102 | /// <summary> | 
|---|
| 103 | /// Answer true if canceled.  It's common that a dialog would allow the user to cancel the | 
|---|
| 104 | /// Async call and the Dialog that is holding on the the Future will cancel the Future.  Cancelling | 
|---|
| 105 | /// the Future does not stop the server-side processing but when the server returns nothing will happen. | 
|---|
| 106 | /// </summary> | 
|---|
| 107 | public bool WasCancelled | 
|---|
| 108 | { | 
|---|
| 109 | get { return _wasCancelled; } | 
|---|
| 110 | set { _wasCancelled = value; } | 
|---|
| 111 | } | 
|---|
| 112 |  | 
|---|
| 113 | private bool _hasTimedOut = false; | 
|---|
| 114 |  | 
|---|
| 115 | /// <summary> | 
|---|
| 116 | /// Answer true if the Async call has timeed-out (a time-based version of Cancel) | 
|---|
| 117 | /// </summary> | 
|---|
| 118 | public bool HasTimedOut | 
|---|
| 119 | { | 
|---|
| 120 | get { return _hasTimedOut; } | 
|---|
| 121 | set { _hasTimedOut = value; } | 
|---|
| 122 | } | 
|---|
| 123 |  | 
|---|
| 124 | private bool _hasReturned = false; | 
|---|
| 125 |  | 
|---|
| 126 |  | 
|---|
| 127 | /// <summary> | 
|---|
| 128 | /// Answer true if the Async call has completed on the server and that a result is ready. | 
|---|
| 129 | /// </summary> | 
|---|
| 130 | public bool HasReturned | 
|---|
| 131 | { | 
|---|
| 132 | get { return _hasReturned; } | 
|---|
| 133 | set | 
|---|
| 134 | { | 
|---|
| 135 | _hasReturned = value; | 
|---|
| 136 | this.FutureTime = DateTime.Now; | 
|---|
| 137 | } | 
|---|
| 138 | } | 
|---|
| 139 |  | 
|---|
| 140 |  | 
|---|
| 141 | private bool _hasData = false; | 
|---|
| 142 |  | 
|---|
| 143 | /// <summary> | 
|---|
| 144 | /// Answer true if the data has been fetched from the server and is ready to be used by the application. | 
|---|
| 145 | /// </summary> | 
|---|
| 146 | public bool HasData | 
|---|
| 147 | { | 
|---|
| 148 | get { return _hasData; } | 
|---|
| 149 | set { _hasData = value; } | 
|---|
| 150 | } | 
|---|
| 151 |  | 
|---|
| 152 | private DateTime _presentTime = DateTime.Now; | 
|---|
| 153 |  | 
|---|
| 154 | /// <summary> | 
|---|
| 155 | /// The time the Future was created. | 
|---|
| 156 | /// </summary> | 
|---|
| 157 | public DateTime PresentTime | 
|---|
| 158 | { | 
|---|
| 159 | get { return _presentTime; } | 
|---|
| 160 | set { _presentTime = value; } | 
|---|
| 161 | } | 
|---|
| 162 |  | 
|---|
| 163 | private DateTime? _futureTime = null; | 
|---|
| 164 |  | 
|---|
| 165 | /// <summary> | 
|---|
| 166 | /// The time the async call returned (the future) | 
|---|
| 167 | /// </summary> | 
|---|
| 168 | public DateTime? FutureTime | 
|---|
| 169 | { | 
|---|
| 170 | get { return _futureTime; } | 
|---|
| 171 | set { _futureTime = value; } | 
|---|
| 172 | } | 
|---|
| 173 |  | 
|---|
| 174 | /// <summary> | 
|---|
| 175 | /// How long in milliseconds the Async call took to complete. | 
|---|
| 176 | /// </summary> | 
|---|
| 177 | public double WaitTime | 
|---|
| 178 | { | 
|---|
| 179 | get | 
|---|
| 180 | { | 
|---|
| 181 | return ((this.FutureTime.HasValue ? this.FutureTime.Value : DateTime.Now) - this.PresentTime).TotalMilliseconds; | 
|---|
| 182 | } | 
|---|
| 183 | } | 
|---|
| 184 |  | 
|---|
| 185 | private int _maxWaitTime = 60 * 60 * 24; | 
|---|
| 186 |  | 
|---|
| 187 | /// <summary> | 
|---|
| 188 | /// Number of seconds to wait for an async response before returning without a response. | 
|---|
| 189 | /// </summary> | 
|---|
| 190 | public int MaxWaitTime | 
|---|
| 191 | { | 
|---|
| 192 | get { return _maxWaitTime; } | 
|---|
| 193 | set { _maxWaitTime = value; } | 
|---|
| 194 | } | 
|---|
| 195 |  | 
|---|
| 196 | private BMXNetRemoteSession _session = null; | 
|---|
| 197 |  | 
|---|
| 198 | internal BMXNetRemoteSession Session | 
|---|
| 199 | { | 
|---|
| 200 | get { return _session; } | 
|---|
| 201 | set { _session = value; } | 
|---|
| 202 | } | 
|---|
| 203 |  | 
|---|
| 204 | /// <summary> | 
|---|
| 205 | /// Method to cancel the Async call.  This does not stop the server-side processing.  The Aborted event | 
|---|
| 206 | /// will be called immediately. | 
|---|
| 207 | /// </summary> | 
|---|
| 208 | public void Cancel() | 
|---|
| 209 | { | 
|---|
| 210 | this.WasCancelled = true; | 
|---|
| 211 | this.Session.RemoveFromFutures(this); | 
|---|
| 212 | this.TriggerAbortedEvent(); | 
|---|
| 213 | } | 
|---|
| 214 |  | 
|---|
| 215 | delegate void UiCall(); | 
|---|
| 216 |  | 
|---|
| 217 | protected void TriggerAbortedEvent() | 
|---|
| 218 | { | 
|---|
| 219 | if (this.Aborted != null) | 
|---|
| 220 | { | 
|---|
| 221 | if (this.InvokedControl != null && this.InvokedControl.InvokeRequired) | 
|---|
| 222 | { | 
|---|
| 223 | this.InvokedControl.Invoke(new UiCall(UiTriggerAbortedEvent)); | 
|---|
| 224 | } | 
|---|
| 225 | else | 
|---|
| 226 | { | 
|---|
| 227 | this.UiTriggerAbortedEvent(); | 
|---|
| 228 | } | 
|---|
| 229 | } | 
|---|
| 230 | } | 
|---|
| 231 |  | 
|---|
| 232 | private void UiTriggerAbortedEvent() | 
|---|
| 233 | { | 
|---|
| 234 | DataTableFutureEventArgs args = new DataTableFutureEventArgs(); | 
|---|
| 235 | args.Future = this; | 
|---|
| 236 | this.Aborted(this, args); | 
|---|
| 237 | } | 
|---|
| 238 |  | 
|---|
| 239 | protected void TriggerReturnedEvent() | 
|---|
| 240 | { | 
|---|
| 241 | if (this.Returned != null) | 
|---|
| 242 | { | 
|---|
| 243 | if (this.InvokedControl != null && this.InvokedControl.InvokeRequired) | 
|---|
| 244 | { | 
|---|
| 245 | this.InvokedControl.Invoke(new UiCall(UiTriggerReturnedEvent)); | 
|---|
| 246 | } | 
|---|
| 247 | else | 
|---|
| 248 | { | 
|---|
| 249 | this.UiTriggerReturnedEvent(); | 
|---|
| 250 | } | 
|---|
| 251 | } | 
|---|
| 252 | } | 
|---|
| 253 |  | 
|---|
| 254 | private void UiTriggerReturnedEvent() | 
|---|
| 255 | { | 
|---|
| 256 | DataTableFutureEventArgs args = new DataTableFutureEventArgs(); | 
|---|
| 257 | args.Future = this; | 
|---|
| 258 | this.Returned(this, args); | 
|---|
| 259 | } | 
|---|
| 260 |  | 
|---|
| 261 |  | 
|---|
| 262 | internal void FetchResults(BMXNetRemoteSession aSession) | 
|---|
| 263 | { | 
|---|
| 264 | if (!this.HasReturned || this.HasTimedOut || this.WasCancelled) | 
|---|
| 265 | return; | 
|---|
| 266 |  | 
|---|
| 267 | try | 
|---|
| 268 | { | 
|---|
| 269 | this.Result = aSession.TableFromCommand("BMX ASYNC GET^" + this.ResultKey, this.ResultDataSet, this.ResultTableName, this.ResultAppContext); | 
|---|
| 270 | this.HasData = true; | 
|---|
| 271 | } | 
|---|
| 272 | catch (Exception problem) | 
|---|
| 273 | { | 
|---|
| 274 | this.ErrorException = problem; | 
|---|
| 275 | this.Cancel(); | 
|---|
| 276 | } | 
|---|
| 277 | } | 
|---|
| 278 |  | 
|---|
| 279 | private BMXNetRemoteSession _fetchingSession = null; | 
|---|
| 280 |  | 
|---|
| 281 | internal BMXNetRemoteSession FetchingSession | 
|---|
| 282 | { | 
|---|
| 283 | get { return _fetchingSession; } | 
|---|
| 284 | set { _fetchingSession = value; } | 
|---|
| 285 | } | 
|---|
| 286 |  | 
|---|
| 287 | /// <summary> | 
|---|
| 288 | /// If IsAutoFetchEnabled is false, then the results must be fetched synchrounously useding PostFetch(). | 
|---|
| 289 | /// Call PostFetch() once and check if HasData is true.  If not true, there is was error related to the | 
|---|
| 290 | /// async call. | 
|---|
| 291 | /// </summary> | 
|---|
| 292 | public void PostFetch() | 
|---|
| 293 | { | 
|---|
| 294 |  | 
|---|
| 295 | this.FetchResults(this.FetchingSession); | 
|---|
| 296 | } | 
|---|
| 297 |  | 
|---|
| 298 | internal void FutureHasReturned(BMXNetRemoteSession aSession, String resultsKey) | 
|---|
| 299 | { | 
|---|
| 300 | //TODO:If cancled, BMX ASYNC GET to clean cancelle ditem | 
|---|
| 301 | if (this.HasReturned) | 
|---|
| 302 | return; | 
|---|
| 303 |  | 
|---|
| 304 | this.ResultKey = resultsKey; | 
|---|
| 305 | this.HasReturned = true; | 
|---|
| 306 |  | 
|---|
| 307 | if (!this.HasTimedOut && !this.WasCancelled) | 
|---|
| 308 | { | 
|---|
| 309 | if (this.IsAutoFetchEnabled) | 
|---|
| 310 | { | 
|---|
| 311 | this.FetchResults(aSession); | 
|---|
| 312 | } | 
|---|
| 313 | else | 
|---|
| 314 | { | 
|---|
| 315 | this.FetchingSession = aSession; | 
|---|
| 316 | } | 
|---|
| 317 | } | 
|---|
| 318 |  | 
|---|
| 319 | this.TriggerReturnedEvent(); | 
|---|
| 320 | } | 
|---|
| 321 |  | 
|---|
| 322 | private String _resultTableName = null; | 
|---|
| 323 |  | 
|---|
| 324 | public String ResultTableName | 
|---|
| 325 | { | 
|---|
| 326 | get { return _resultTableName; } | 
|---|
| 327 | set { _resultTableName = value; } | 
|---|
| 328 | } | 
|---|
| 329 |  | 
|---|
| 330 | private DataSet _resultDataSet = null; | 
|---|
| 331 |  | 
|---|
| 332 | /// <summary> | 
|---|
| 333 | /// The data set the result data table is in. | 
|---|
| 334 | /// </summary> | 
|---|
| 335 | public DataSet ResultDataSet | 
|---|
| 336 | { | 
|---|
| 337 | get { return _resultDataSet; } | 
|---|
| 338 | set { _resultDataSet = value; } | 
|---|
| 339 | } | 
|---|
| 340 |  | 
|---|
| 341 | private String _resultAppContext = null; | 
|---|
| 342 |  | 
|---|
| 343 | /// <summary> | 
|---|
| 344 | /// The AppContext that the async call was made with | 
|---|
| 345 | /// </summary> | 
|---|
| 346 | public String ResultAppContext | 
|---|
| 347 | { | 
|---|
| 348 | get { return _resultAppContext; } | 
|---|
| 349 | set { _resultAppContext = value; } | 
|---|
| 350 | } | 
|---|
| 351 |  | 
|---|
| 352 |  | 
|---|
| 353 | } | 
|---|
| 354 | } | 
|---|