
Introduction
If there is a need for a setup component that can read an XML setting file (SetupScripts.xml) and run methods or scripts defined in it asynchronously, then this Setup Grid component is what you are looking for. To use this component, the methods in an assembly, a batch file, or a path to a T-SQL file can be defined in SetupScripts.xml and we can call StartSetup() to execute them asynchronously. That means, when the setup is in progress, other operations are not suspended.
Solution
SetupGridUC is a UserControl that contains a DataGridView to show the scripts and the progress status when the setup is running. A typical scenario of using the SetupGridUC component is as follows:
- Initialize
SetupGridUC from the SetupScripts.xml file using the Load() method.
- Start setup using the
StartSetup() method of SetupGridUC and also StopSetup(false) when it is started.
- In order to do some actions (e.g., disable buttons) when the setup is being started or finished, use the following events:
void stpGrid_ProgressFinished(object sender, EventArgs e) is called when the setup finishes.
void stpGrid_ProgressStarted(object sender, EventArgs e) is called when the setup starts.
Implementation
The StartSetup() method starts the setup process asynchronously by calling the ExecuteScript() method of the SetupGridUC component using BeginInvoke().
private MethodDelegate methodDelegate;
...
asyncResult = methodDelegate.BeginInvoke(null, null);
Using visual components, like refreshing the DataGridView from another thread of execution, can cause a Cross-thread operation not valid exception. To bypass this exception, the Invoke() method of the component should be used. SafeInvoke() uses the Invoke() method of a component to call a parameter-less method (the void MethodName() delegate) in the component:
private delegate void VoidDelegate();
...
private void SafeInvoke(Control ctrl, VoidDelegate delgt)
{
if (ctrl.InvokeRequired)
{
VoidDelegate d = new VoidDelegate(delgt);
ctrl.Invoke(d, new object[] { });
}
}
To refresh asyncSetupGrid in another thread, use the SafeInvoke() method as follow:
private void command_Progress(object sender, ProgressEventArgs e)
{
SafeInvoke(asyncSetupGrid, Refresh);
}
Note: The void command_Progress event is called from another thread, so the visual components should use Invoke() to call a method.
The scripts or methods can be defined in SetupScripts.xml as follows:
<tblSetupScripts>
<ID>2</ID>
<Name>S3</Name>
<Title>Execute SQLQuery.sql against Database</Title>
<Script>SQLQuery.sql</Script>
<Kind>SQLScript</Kind>
</tblSetupScripts>
Tag meanings:
<Name>:
Name tag should be a unique name that user can assign to the script to be run
<Title>:
Title tag is an explanation for the script
<Script>:
Script tag meanings depend on <Kind> tag that can be set as follows:
<Kind>Method</Kind>
<Script>
{Assembly file name}, {Namespace. class name}, {Method name}
public void Mehtod(SetupGrid.ScriptCommands.ProgressDelegate doProgress,
SetupGrid.ScriptCommands.HaltDelegate doHalt)
{
...
}
</Script>
<Kind>SQLScript</Kind>
<Script> {Path of SQL script to be executed}
The T-SQL commands are separated using 'Go'
command from each other. For example:
...
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
...
</Script>
<Kind>BatchFile</Kind>
<Script> { The path and file name of the Batch file to be executed}
The batch file execution will open a Command Prompt
window and waits the command is finished.
</Script>
Deployment
This sample contains two assemblies:
- Main.exe as the executable to host the SetupGrid component.
- SetupGrid.dll as the library that contains the SetupGridUC component.
In order to test the SQL scripts, create a database in SQL Server and add proper connection settings to the database in Properties/Settings.settings file.