I want to cancel querying data from my Oracle database, when the user clicks on the "Cancel" button.
Below the simplified code of existing application:
Database connector
public interface IDatabaseConnector
{
string Query { get; set; }
void Fetch(Action<DataTable> action);
}
class DatabaseConnector : IDatabaseConnector
{
private string connectionString = "";
public string Query { get; set; }
private OracleConnection connection { get; set; }
private OracleCommand command { get; set; }
public DatabaseConnector(string connectionString)
{
this.connectionString = connectionString;
}
public bool ConnectionOpened
{
get => connection.State == ConnectionState.Open;
}
public OracleConnection Connection { get => connection; }
public void Fetch(Action<DataTable> action)
{
try
{
connection = new OracleConnection(connectionString);
command = new OracleCommand(Query, connection);
connection.Open();
using (var reader = command.ExecuteReader())
{
if (!reader.HasRows)
{
throw new Exception("Empty result!");
}
// maybe I should stop querying here?
var dataTable = new DataTable();
dataTable.Load(reader);
// run callback processor
action(dataTable);
}
}
finally
{
if (connection != null)
{
connection.Dispose();
}
if (command != null)
{
command.Dispose();
}
}
}
public void Close()
{
command.Cancel();
command.Dispose();
connection.Close();
connection.Dispose();
}
}
Form:
public class ProductsForm : Form
{
public EntryForm EntryForm { get; set; }
public ApplicationSettings Settings { get; set; }
private BackgroundWorker backgroundWorker;
private DatabaseConnector DbConnector;
public ProductsForm()
{
InitializeComponent();
}
public ProductsForm(EntryForm parentForm, ApplicationSettings appSettings) : this()
{
EntryForm = parentForm;
Settings = appSettings;
backgroundWorker = new BackgroundWorker()
{
WorkerSupportsCancellation = true
};
backgroundWorker.DoWork += DoWork;
backgroundWorker.RunWorkerCompleted += WorkCompleted;
}
public void DoWork(object sender, DoWorkEventArgs e)
{
Invoke(new Action(() => {
ExecuteButton.Text = "Querying...";
ExecuteButton.Enabled = false;
StopButton.Enabled = true;
}));
DbConnector = new DatabaseConnector(Settings.DwhConnection.ToString());
var repo = new ProductsRepository(DbConnector)
{
startDate = StartDateTimePicker.Value.ToShortDateString(),
endDate = EndDateTimePicker.Value.ToShortDateString(),
byPattern = ByPattrenCheckBox.Checked
};
try
{
// POINT 1
repo.Query((datatable) =>
{
if (backgroundWorker.CancellationPending)
{
MessageBox.Show("BackgroundWorker Canceled");
if (DbConnector.ConnectionOpened)
{
DbConnector.Close();
}
e.Cancel = true;
return;
}
Invoke(new Action(() =>
{
QueryResultDataGridView.DataSource = datatable;
QueryResultDataGridView.SetColumntDateTimeFormat("Start time");
ExecuteButton.Enabled = true;
StopButton.Enabled = false;
}));
});
}
catch (Exception exception)
{
MessageBox.Show(exception.Message, Text);
Invoke(new Action(() => {
ExecuteButton.Enabled = true;
StopButton.Enabled = false;
}));
}
}
public void WorkCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
if(DbConnector.ConnectionOpened)
{
DbConnector.Close();
}
}
else if (e.Error != null)
{
MessageBox.Show(e.Error.StackTrace, "Error!");
}
}
private void ExecuteButton_Click(object sender, EventArgs e)
{
backgroundWorker.RunWorkerAsync();
}
private void StopButton_Click(object sender, EventArgs e)
{
backgroundWorker.CancelAsync();
}
}
Repo
public abstract class AbstractRepository
{
public abstract void Query(Action<DataTable> callback);
}
public class ProductsRepository : AbstractRepository
{
private IDatabaseConnector connector;
public string startDate;
public string endDate;
public bool byPattern = false;
public ProductsRepository(IDatabaseConnector connector)
{
this.connector = connector;
}
public override void Query(Action<DataTable> callback)
{
var query = QueriesSQL.GetAllProdutcs(startDate, endDate, byPattern);
connector.Query = query;
connector.Fetch(data => callback(data));
}
}
When I cancel operation program continues querying for data and only when data is completely retrieved, it stops.
I found out that this happens because program call callback function that passed to DatabaseConnector.Fetch
after data is retrieved.
So, how I can reorganazie code to stop querying for the data when cancel button has been clicked?