Net 4.0 with Dapper and Ms Sql 2000 - c#

As we know Ms Sql 2000 does not support MultipleActiveResultSets.
Can i use Dapper with async Task without exceptions :
"There is already an open DataReader associated with this Command which must be closed first."
My code example
private async void Form_Load(object sender, EventArgs e){
var sql1 = "select * from Tab1";
var sql2 = "select * from Tab2";
var sql3 = "select * from Tab3";
await Task.Factory.StartNew(() => FillComboBoxWithData(this.cbo1, sql1));
await Task.Factory.StartNew(() => FillComboBoxWithData(this.cbo2, sql2));
await Task.Factory.StartNew(() => FillComboBoxWithData(this.cbo3, sql3));
}
public static async Task FillComboBoxWithData(ComboBox comboBox, string sql{
try
{
var data = await Task.Factory.StartNew(() => SqlConn.Query<IdName>(sql));
var d1 = data.ToNonNullList();
comboBox.DataSource = d1;
comboBox.DisplayMember = "Name";
comboBox.ValueMember = "Id";
comboBox.SelectedItem = null;
}
catch (Exception ex)
{
Console.Write(ex.ToString());
}
}
Thank you.

It looks like your code should work, although I'm very confused as to why you're using Task.Factory.StartNew everywhere; you shouldn't have to do that, and indeed: it is not ideal to do so.
I also can't see how / where SqlConn is defined, so for all I know it is indeed being accessed concurrently. However, to rewrite your code more idiomatically:
private async void Form_Load(object sender, EventArgs e)
{
await FillComboBoxWithData(this.cbo1, "select from Tab1");
await FillComboBoxWithData(this.cbo2, "select from Tab2");
await FillComboBoxWithData(this.cbo3, "select from Tab3");
}
public static async Task FillComboBoxWithData(ComboBox comboBox, string sql)
{
try
{
var data = (await SqlConn.QueryAsync<IdName>(sql)).AsList();
comboBox.DataSource = data;
comboBox.DisplayMember = "Name";
comboBox.ValueMember = "Id";
comboBox.SelectedItem = null;
}
catch (Exception ex)
{
Console.Write(ex.ToString());
}
}
Notice: no Task.Factory.StartNew; it uses the async implementation of the backing service.

Related

Is there a way for reading data asynchronously for datagridview in c#?

i am trying to fill datagridview datasourece asynchronously in entity framework, but it shows me cross-thread error and it does not work, does anybody know how?
thank you very much
private async Task FillData()
{
await Task.Run(() =>
{
Model.AsyncTestDBEntities db = new Model.AsyncTestDBEntities();
dataGridView1.AutoGenerateColumns = false;
dataGridView1.DataSource = db.Table_1.ToList();
});
}
when i call the above method it does not work
private async void button1_Click(object sender, EventArgs e)
{
await FillData();
}
Use ToListAsync instead of Task.Run().
private async Task FillData()
{
var db = new Model.AsyncTestDBEntities();
var list = await db.Table_1.ToListAsync();
dataGridView1.AutoGenerateColumns = false;
dataGridView1.DataSource = list;
}

How to stop a method (triggered by button click) in WPF

I have a private async void Button_Click method in my WPF which runs a very complicated SQL query which can run for several minutes.
I wish the user can stop this method by clicking another button.
My code is like this:
public partial class MainWindow : Window
{
private async void Button_Click(object sender, RoutedEventArgs e)
{
string SQL_Query= " a very long and complicated SQL query ... "
SqlCommand SQL_Query_cmd = new SqlCommand(SQL_Query, conn);
DataTable dt = new DataTable();
await Task.Run(() => {
using (SqlDataAdapter a = new SqlDataAdapter(SQL_Query_cmd))
{ a.Fill(dt);}
});
}
}
I read about BackgroundWorker in this link How to use WPF Background Worker.
But didn't understand how to integrate it into my code. I think, my "filling datatable" code is already asynchronous but I don't know how to stop it. Assume that the button which is going to end this method is called stop_btn and its Click method is called cancelButton_Click.
Please please please write your answer in a post, rather than comments. I will be greatly thankful.
Here is how you could use the IDbCommand.Cancel method and a CancellationTokenSource, to perform cancellation both on the server side and on the client side.
private IDbCommand _activeSqlCommand;
private CancellationTokenSource _cts;
private async void btnExecute_Click(object sender, RoutedEventArgs e)
{
// The _activeSqlCommand and _cts should be null here.
// Otherwise, you may end up with multiple concurrent executions.
Debug.Assert(_activeSqlCommand == null);
Debug.Assert(_cts == null);
var sqlQuery = "A very long and complicated SQL query...";
var localSqlCommand = new SqlCommand(sqlQuery, _connection);
var localCts = new CancellationTokenSource();
_activeSqlCommand = localSqlCommand;
_cts = localCts;
btnExecute.IsEnabled = false;
btnCancel.IsEnabled = true;
try
{
DataTable dataTable = await AsCancelable(Task.Run(() =>
{
var dt = new DataTable();
using (SqlDataAdapter a = new SqlDataAdapter(localSqlCommand))
a.Fill(dt);
return dt;
}, localCts.Token), localCts.Token);
// Here use the dataTable to update the UI
}
catch (OperationCanceledException) { } // Ignore
catch (SqlException ex) when (ex.ErrorCode == CANCEL_ERROR_CODE) { } // Ignore
finally
{
btnCancel.IsEnabled = false;
btnExecute.IsEnabled = true;
// The _activeSqlCommand and _cts should still have the local values here.
Debug.Assert(_activeSqlCommand == localSqlCommand);
Debug.Assert(_cts == localCts);
_activeSqlCommand = null;
_cts = null;
localCts.Dispose();
}
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
_activeSqlCommand?.Cancel();
_cts?.Cancel();
}
private static Task<T> AsCancelable<T>(Task<T> task,
CancellationToken cancellationToken)
{
var cancelable = new Task<T>(() => default, cancellationToken);
return Task.WhenAny(task, cancelable).Unwrap();
}
You'll have to figure out what kind of exception is thrown by the database server when the execution is canceled, and ignore this exception based on its ErrorCode or some other property.

Display real progress of loading WPF DataGrid instead of .IsIndeterminate

I have managed to get ProgressBar into my WPF form. However my current skill is stuck on using:
ProgressBar.IsIndeterminate = true;
DataGrid1.ItemsSource = await GetDataAsync();
ProgressBar.IsIndeterminate = false;
Any professional ready to describe how to get ProgressBar to display actual loading progress?
Here is my full code:
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
try
{
ProgressBar.IsIndeterminate = true;
DataGrid1.ItemsSource = await GetDataAsync();
ProgressBar.IsIndeterminate = false;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private Task<DataView> GetDataAsync()
{
return Task.Run(() =>
{
string connectionStringDE = "Driver={Pervasive ODBC Client Interface};ServerName=DE875;dbq=#DEDBFS;Uid=DEUsername;Pwd=DEPassword;";
string queryStringDE = "select NRO,NAME,NAMEA,NAMEB,ADDRESS,POSTA,POSTN,POSTADR,COMPANYN,COUNTRY,ID,ACTIVE from COMPANY";
string connectionStringFR = "Driver={Pervasive ODBC Client Interface};ServerName=FR875;dbq=#FRDBFS;Uid=FRUsername;Pwd=FRPassword;";
string queryStringFR = "select NRO,NAME,NAMEA,NAMEB,ADDRESS,POSTA,POSTN,POSTADR,COMPANYN,COUNTRY,ID,ACTIVE from COMPANY";
DataTable dataTable = new DataTable("COMPANY");
// using-statement will cleanly close and dispose unmanaged resources i.e. IDisposable instances
using (OdbcConnection dbConnectionDE = new OdbcConnection(connectionStringDE))
{
dbConnectionDE.Open();
OdbcDataAdapter dadapterDE = new OdbcDataAdapter();
dadapterDE.SelectCommand = new OdbcCommand(queryStringDE, dbConnectionDE);
dadapterDE.Fill(dataTable);
}
using (OdbcConnection dbConnectionFR = new OdbcConnection(connectionStringFR))
{
dbConnectionFR.Open();
OdbcDataAdapter dadapterFR = new OdbcDataAdapter();
dadapterFR.SelectCommand = new OdbcCommand(queryStringFR, dbConnectionFR);
var newTable = new DataTable("COMPANY");
dadapterFR.Fill(newTable);
dataTable.Merge(newTable);
}
return dataTable.DefaultView;
});
}
You can't display the actual process unless you know it and you don't unless the API that you are using actually reports the current progress to you.
The OdbcCommand API doesn't so you should stick to using indeterminate progress bars.

Update/populate DataGrid with data asynchronously

I have checked some of topics and google but can't find proper solution.
I want to make WPF application to download Items information to DataGrid (items columns) with TextBox (item name) using RestApi.
The code is correct and work but there's a problem with async updating DataGrid.
DataTable dt;
public DataTable Dt { get => dt; set { dt = value; dataGridItems.DataContext = Dt.DefaultView; } }
private async void ButtonSearch_Click(object sender, RoutedEventArgs e)
{
//buttonSearch.IsEnabled = false;
rest = new RestClass(ClientId, ClientSecret);
Task T = Task.Run(() => SearchItem(rest, textBoxProductName.Text));
T.ContinueWith((t) =>
{
dataGridItems.DataContext = Dt.DefaultView;
//buttonSearch.IsEnabled = true;
}, TaskScheduler.FromCurrentSynchronizationContext());
Above code with small changes (dataGridItems.DataBinding) worked in WinForms without any problems but I can't make it work in WPF application.
private void SearchItem(RestClass Rest, string ItemName)
{
try
{
var x = Rest.GetTokenJ().Result;
ItemsOffersWPF.Rootobject searchResponse = Rest.requestSearchItem(ItemName);
GetItemsCollection(searchResponse);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
//dataGridItems.DataContext = Dt.DefaultView;
}
I have tried Invoke, InvokeAsync but it makes UI irresponsible which is what I want to avoid.
You should use await instead of T.ContinueWith
await Task.Run(() => SearchItem(rest, textBoxProductName.Text));
dataGridItems.DataContext = Dt.DefaultView;
anything after the "await" will be executed as soon as the Task has finished running.
Ok thanks to you, I finally found solution. It's not perfect but works well.
The problem was propably updating DataTable (Dt property) inside GetItemsCollection method and using textBoxProductName.Text inside await SearchItem function.
// its useless now
//DataTable Dt { get => dt; set { dt = value; dataGridAllegro.DataContext = Dt.DefaultView; } }
private async void ButtonSearch_Click(object sender, RoutedEventArgs e)
{
buttonSearch.IsEnabled = false;
var productName = textBoxProductName.Text; // get Text value before using Task!
await Task.Run(() => SearchItem(productName));
dataGridItems.ItemsSource = dt.DefaultView;
buttonSearch.IsEnabled = true;
}
private async void SearchItem(string ProductName)
{
try
{
var x = rest.GetTokenJ().Result;
ItemsOffersWPF.Rootobject searchResponse = rest.requestSearchItem(ProductName);
GetItemsCollection(searchResponse); // inside update dt not property DataTable Dt { get => dt; set { dt = value; dataGridAllegro.DataContext = Dt.DefaultView; } }
// = exception using another thread UI
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}

How to run two or more sql query at the same time?

I want to run more query at the really same time in different thread. I am using backgroundworkers to solve this problem! My question is: Is there better way to delegate UI element or is it correct as i did it?
private void mainform_Load(object sender, EventArgs e)
{
if (bscan_backgroundworker.IsBusy == false)
{
bscan_backgroundworker.RunWorkerAsync();
}
if (bscan2_backgroundworker.IsBusy == false)
{
bscan2_backgroundworker.RunWorkerAsync();
}
}
private void bscan_backgroundworker_DoWork(object sender, DoWorkEventArgs e)
{
bscan();
}
private void bscan2_backgroundworker_DoWork(object sender, DoWorkEventArgs e)
{
bscan2();
}
private void bscan()
{
string query = "Select * from table_name";
MySqlConnection mysqlconn_to_db = new MySqlConnection(connectionstring);
try
{
mysqlconn_to_db.Open();
using (MySqlCommand command = new MySqlCommand(query, mysqlconn_to_db))
{
command.ExecuteNonQuery();
using (MySqlDataAdapter adapter = new MySqlDataAdapter(command))
{
DataTable datatable = new DataTable();
adapter.Fill(datatable);
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(delegate ()
{
dataGridView1.DataSource = datatable;
dataGridView1.Refresh();
label2.Text = dataGridView1.Rows[Convert.ToInt16(label1.Text)].Cells[0].Value.ToString();
}));
}
else
{
dataGridView1.DataSource = datatable;
dataGridView1.Refresh();
label2.Text = dataGridView1.Rows[Convert.ToInt16(label1.Text)].Cells[0].Value.ToString();
}
adapter.Dispose();
}
}
}
catch (Exception ex)
{
messagebox(ex.Message);
}
}
bscan2() method is nearly the same as bscan() with different query and different datagridview. Is there more efficient way to do it, or its ok?
Your BackgroundWorker solution isn't necessarily a bad one, but there are new and improved ways to handle asynchronous programming in C#. It's highly recommended that you look into async and await. That may not apply quite so directly to what you're trying to accomplish in this case since you don't seem to want to wait for one method to complete, so it's also recommended that you look into the Task Parallel Library (TPL), and particularly Task.Run(). There's even something called Parallel LINQ that is specifically meant for handling asynchronous queries.

Categories

Resources