Hi Dimitry,
You don't need them because you are not going to expose synchronous
version
of your API. I mean you have something like this:
void UpdateLookupTablesAsync()
{
_jobScheduler.TaskAdd(new
AgencyInfo.Helpers.JobSchedulerTask("LookupTableUpdate",
DateTime.Now + new TimeSpan(0, interval, 0),
new TimeSpan(0, interval, 0),
Helpers.LookupTableSingleton.Instance.dsLookupTbls.UpdateLookupTables));
}
but you don't expose synchronous version:
void UpdateLookupTables()
which blocks execution till the job completion.
Usually we need asynchronous versions to be used in WinForms, while
synchronous versions are suitable for batch files and WebForms. There are
some design patterns about:
How about this? - Part of the the data is fetched immediately and the rest
is fetched in background. Note the use of the ticket. If the tickets
don't match, the background fill is aborted.
------------------------------------------------------------------------------------
using System;
using System.Data;
using System.Threading;
using System.Runtime.Remoting.Messaging;
using log4net;
namespace JimRand.AgencyInfo.Datasets
{
partial class DSAgencyForm
{
private static readonly log4net.ILog _log =
log4net.LogManager.GetLogger(typeof(DSAgencyForm));
private
System.Collections.Generic.List<System.Data.SqlClient.SqlDataAdapter>
_adapterList = new
System.Collections.Generic.List<System.Data.SqlClient.SqlDataAdapter>();
private
System.Collections.Generic.List<System.Data.SqlClient.SqlDataAdapter>
_adapterListBkGrnd = new
System.Collections.Generic.List<System.Data.SqlClient.SqlDataAdapter>();
private
System.Collections.Generic.List<System.Data.SqlClient.SqlDataAdapter>
_adapterListForeGrnd = new
System.Collections.Generic.List<System.Data.SqlClient.SqlDataAdapter>();
private string _lastUpdatedBy;
private DataAdapters.DAAgencyForm _adapterSet;
private System.Data.DataSet _dsClone;
private int _ticket = 0;
private object _lockObj = new object();
/* Dataset must be updated on the GUI thread - see UpdateLookupTables()
*/
private System.ComponentModel.ISynchronizeInvoke _GUISync;
internal void Fill(int officeID)
{
lock (_lockObj)
{
_ticket++;
/* Set parameters for selected officeID */
_adapterSet.daNotes.SelectCommand.Parameters["@ParentTblID"].Value =
officeID;
_adapterSet.daOffice.SelectCommand.Parameters["@OfficeID"].Value =
officeID;
_adapterSet.daOfficeCnsltnt.SelectCommand.Parameters["@OfficeID"].Value
= officeID;
_adapterSet.daOfficeEnduserMM.SelectCommand.Parameters["@OfficeID"].Value
= officeID;
_adapterSet.daOfficeIdxUrl.SelectCommand.Parameters["@OfficeID"].Value
= officeID;
_adapterSet.daRefUrls.SelectCommand.Parameters["@OfficeID"].Value =
officeID;
_adapterSet.daUrlCnsltSolution.SelectCommand.Parameters["@OfficeID"].Value
= officeID;
_adapterSet.daUrlFraming.SelectCommand.Parameters["@OfficeID"].Value =
officeID;
_adapterSet.daUrlOfficeConsultant.SelectCommand.Parameters["@OfficeID"].Value
= officeID;
_adapterSet.daUrlOfficeEnduser.SelectCommand.Parameters["@OfficeID"].Value
= officeID;
/* Fill tables Office, Notes and OifficeIdxUrl on GUI thread */
Helpers.DataAccessLayer.Fill((System.Data.DataSet)this,
_adapterListForeGrnd);
/* Fill remaining tables on background thread */
BackGrndFillDelegate d = new BackGrndFillDelegate(BackGrndFill);
System.AsyncCallback ac = new
System.AsyncCallback(BackGrndFillCallback);
System.IAsyncResult ar = d.BeginInvoke(_ticket, ac, null);
}
} /* internal void Fill */
private delegate DatasetPackage BackGrndFillDelegate(int ticket);
private DatasetPackage BackGrndFill(int ticket)
{
/* Dataset to fill on the background thread */
System.Data.DataSet ds;
lock (_dsClone)
{
ds = _dsClone.Clone();
}
try
{
foreach (System.Data.SqlClient.SqlDataAdapter da in _adapterListBkGrnd)
{
lock (_lockObj)
{
if (ticket == _ticket)
{
string tableName = da.TableMappings[0].DataSetTable;
da.Fill(ds, tableName);
}
else
{
break;
}
}
}
}
catch (Exception ex)
{
_log.Error("BackGrndFill", ex);
}
return new DatasetPackage(ticket, ds);
}
private void BackGrndFillCallback(System.IAsyncResult ar)
{
BackGrndFillDelegate d =
(BackGrndFillDelegate)((AsyncResult)ar).AsyncDelegate;
DatasetPackage dsp = d.EndInvoke(ar);
MergeData(dsp);
}
private delegate void MergeDataDelegate(DatasetPackage dsp);
private void MergeData(DatasetPackage dsp)
{
if ( _GUISync.InvokeRequired )
{
_GUISync.Invoke(new MergeDataDelegate(MergeData), new object[] {
dsp });
}
else
{
lock (_lockObj)
{
if (dsp.Ticket == _ticket)
{
/* Only merge if an update has not been started */
if (!this.HasChanges()) this.Merge(dsp.DS);
}
}
}
}
internal void
PrepareDatasetForUse(System.ComponentModel.ISynchronizeInvoke guiSync)
{
_GUISync = guiSync;
_adapterSet = new JimRand.AgencyInfo.DataAdapters.DAAgencyForm();
_lastUpdatedBy = Thread.CurrentPrincipal.Identity.Name;
/* All adapters - use during update */
_adapterList.Add(_adapterSet.daOffice);
_adapterList.Add(_adapterSet.daNotes);
_adapterList.Add(_adapterSet.daOfficeIdxUrl);
_adapterList.Add(_adapterSet.daUrlOfficeConsultant);
_adapterList.Add(_adapterSet.daUrlCnsltSolution);
_adapterList.Add(_adapterSet.daUrlFraming);
_adapterList.Add(_adapterSet.daUrlOfficeEnduser);
_adapterList.Add(_adapterSet.daRefUrls);
_adapterList.Add(_adapterSet.daOfficeCnsltnt);
_adapterList.Add(_adapterSet.daOfficeEnduserMM);
/* Foreground - fill immediate */
_adapterListForeGrnd.Add(_adapterSet.daOffice);
_adapterListForeGrnd.Add(_adapterSet.daNotes);
_adapterListForeGrnd.Add(_adapterSet.daOfficeIdxUrl);
/* Background - fill async */
_adapterListBkGrnd.Add(_adapterSet.daUrlOfficeConsultant);
_adapterListBkGrnd.Add(_adapterSet.daUrlCnsltSolution);
_adapterListBkGrnd.Add(_adapterSet.daUrlFraming);
_adapterListBkGrnd.Add(_adapterSet.daUrlOfficeEnduser);
_adapterListBkGrnd.Add(_adapterSet.daRefUrls);
_adapterListBkGrnd.Add(_adapterSet.daOfficeCnsltnt);
_adapterListBkGrnd.Add(_adapterSet.daOfficeEnduserMM);
Helpers.DataAccessLayer.FlipToProduction(_adapterList);
/* Model dataset for background filling */
_dsClone = this.Clone();
_dsClone.Relations.Clear();
_dsClone.Tables["Notes"].Constraints.Remove("FK_Office_Notes");
_dsClone.Tables["OfficeCnsltnt"].Constraints.Remove("FK_Office_OfficeCnsltnt");
_dsClone.Tables["OfficeEnduserMM"].Constraints.Remove("FK_Office_OfficeEnduserMM");
_dsClone.Tables["OfficeIdxUrl"].Constraints.Remove("FK_Office_OfficeIdxUrl");
_dsClone.Tables["UrlOfficeEnduser"].Constraints.Remove("FK_OfficeIdxUrl_UrlOfficeEnduser");
_dsClone.Tables["UrlFraming"].Constraints.Remove("FK_OfficeIdxUrl_UrlFraming");
_dsClone.Tables["UrlOfficeConsultant"].Constraints.Remove("FK_OfficeIdxUrl_UrlOfficeConsultant");
_dsClone.Tables["RefUrls"].Constraints.Remove("FK_OfficeIdxUrl_RefUrls");
_dsClone.Tables.Remove("Office");
_dsClone.Tables.Remove("Notes");
_dsClone.Tables.Remove("OfficeIdxUrl");
} /* internal void PrepareDatasetForUse */
internal void TearDown()
{
_adapterSet.Dispose();
_adapterList.Clear();
_adapterList = null;
_adapterListForeGrnd.Clear();
_adapterListForeGrnd = null;
_adapterListBkGrnd.Clear();
_adapterListBkGrnd = null;
} /* internal void TearDown */
/* Update all tables in the backend */
internal void Update()
{
/* Copy of changes used by DSLookupTbls.RefreshImmediate to determine
which tables to refresh immediatedly */
DataSet dsChanges = this.GetChanges();
Helpers.DataAccessLayer.Update((System.Data.DataSet)this, _adapterList,
true, _lastUpdatedBy);
/* Immediate refill of lookup tables impacted */
Helpers.LookupTableSingleton.Instance.dsLookupTbls.RefreshImmediate(dsChanges);
dsChanges.Dispose();
} /* internal void Update */
}
internal class DatasetPackage
{
public readonly System.Data.DataSet DS;
public readonly int Ticket;
internal DatasetPackage(int ticket, System.Data.DataSet ds)
{
this.Ticket = ticket;
this.DS = ds;
}
}
}