Async Design Question

S

Shawn Meyer

Hello -

I am trying to write a class that has an async BeginX and EndX, plus the
regular X syncronous method. Delegates seemed like the way to go,
however, I still am having problems getting exactly what I want. Here are
my goals

1. I would like the IAsyncResult that is returned by the Begin function to
be able to be waited on or polled to check for completion.
2. The Begin function would take a callback, and the async process would
call it when the task is complete.
3. I would like to have events such as ProgressChanged that get fired as the
async process progresses.
4. The async process should be able to be cancled with a function CancelX.
5. Multiple async tasks should be able to be performed at the same time.

Are delegates the way to go? Can anyone provide me with a quick example on
how to implement this.

Thanks,
Shawn
 
N

Nicholas Paldino [.NET/C# MVP]

Shawn,

I believe that all but the last request will be able to be handled with
delegates.

Basically, create a private delegate that has the same signature as your
synchronous method. When compiled, the delegate will create BeginInvoke and
EndInvoke methods which can be used to make the call asynchronously. Then,
all you have to do is expose the methods (BeginXXX and EndXXX) with the same
signatures as the corresponding methods on the delegate.

When those methods are called, you just channel the call to a delegate
instance and return the appropriate IAsyncResult. The delegate, of course,
points to your synchronous method implementation.

The thing is, you have to store the delegate in your class instance, and
can only have one async operation at a time being performed. The reason for
this is that the delegate instance is linked to the IAsyncResult returned
from the call to BeginInvoke, and if you create a new one, it will not work.
This is the reason #5 can not be implemented. You can get around this by
creating another instance of your class, and having that perform the async
operation.

For 3 and 4, that is up to you to create the progress events and the
cancel method. Basically, you would have a flag somewhere which indicates
whether or not the operation should be cancelled. As your synchronous
method is run, it checks the flag (access to the flag should be synchronized
through the lock statement) and if the flag is set, cancel the operation,
and exit the method.

Of course, if the method has a main loop that runs, it becomes very
obvious where to check the flag to see if it is a cancelled operation.

Hope this helps.
 
S

Shawn B.

(I'm not the Original Poster)

:: The reason for this is that the delegate instance is linked
:: to the IAsyncResult returned from the call to BeginInvoke,
:: and if you create a new one, it will not work. This is the
:: reason #5 can not be implemented. You can get around
:: this by creating another instance of your class, and having
:: that perform the async operation.

This is a curious remark. I don't recall reading this in the MSDN
documentation.

I created a data access component about 2 1/2 years ago (long before ADO.NET
2.0 was ever announced and even before the data access application block
ever existed) that has ability to execute readers, scalars, datasets,
datatables, and nonqueries asyncronously (I am more than happy to provide
the source code if anyone wants it, C#, and very neat and tidy). This same
component has been in 3 production environments over the years and in each
invenvironment there are close to about 3 million page requests per day. It
uses delegates internally to execute the async results, in much the same way
you described below. The only difference between what you describe and the
behaviors we receive, are that I can indeed execute as many commands as I
want simultaneously to return a datatable and all the results come back with
their proper results and the requests don't get queued. For example, if I
execute 10 commands at the same time async (on the same object instance,
delegate instance) and each command requires 3 seconds to complete, then I
only need to wait roughly 3 seconds before I have the results for all 10
commands properly.

I'm curious what you mean by your above statement?

Here's my source to demonstrate what I'm saying:

private void button1_Click(object sender, System.EventArgs e)
{
SqlDataHelper data = null;
DataTable x = null;
DataTable y = null;
DataTable z = null;
DataTable t = null;

try
{
data = new SqlDataHelper(connectionString);

IAsyncResult ar1 = data.BeginExecuteTable("SELECT * FROM Orders",
false);
IAsyncResult ar2 = data.BeginExecuteTable("SELECT TOP 5000 * FROM
Invoices", false);
IAsyncResult ar3 = data.BeginExecuteTable("SELECT TOP 10000 * FROM
AuditLog", false);

// Do something while the other 3 are still fetching
//
t = data.ExecuteTable("SELECT * FROM SalesGroups", false);

foreach (DataRow row in t.Rows)
{
row[0] = 0;
}

data.BeginExecuteReader("SELECT TOP 5 * FROM InventoryPieces", false,
new AsyncCallback(CallbackMethod));

// Now we synchronize
//
x = data.EndExecuteTable(ar1);
y = data.EndExecuteTable(ar2);
z = data.EndExecuteTable(ar3);

MessageBox.Show(t.Rows.Count.ToString());
MessageBox.Show(x.Rows.Count.ToString());
MessageBox.Show(y.Rows.Count.ToString());
MessageBox.Show(z.Rows.Count.ToString());
}
finally
{
if (data != null)
{
data.Dispose();
}
}
}

public void CallbackMethod(IAsyncResult ar)
{
SqlDataHelper data = null;
IDataReader result = null;
int count = 0;

try
{
data = new SqlDataHelper(connectionString);

result = data.EndExecuteReader(ar);

while (result.Read()) count++;
result.Close();

MessageBox.Show("Callback: " + count.ToString());
}
catch (Exception ex)
{
throw;
}
finally
{
if (data != null)
{
data.Dispose();
}
}
}


If you would like to see my data access component I'll provide you a link to
download the source code.


Thanks,
Shawn




Nicholas Paldino said:
Shawn,

I believe that all but the last request will be able to be handled with
delegates.

Basically, create a private delegate that has the same signature as your
synchronous method. When compiled, the delegate will create BeginInvoke and
EndInvoke methods which can be used to make the call asynchronously. Then,
all you have to do is expose the methods (BeginXXX and EndXXX) with the same
signatures as the corresponding methods on the delegate.

When those methods are called, you just channel the call to a delegate
instance and return the appropriate IAsyncResult. The delegate, of course,
points to your synchronous method implementation.

The thing is, you have to store the delegate in your class instance, and
can only have one async operation at a time being performed. The reason for
this is that the delegate instance is linked to the IAsyncResult returned
from the call to BeginInvoke, and if you create a new one, it will not work.
This is the reason #5 can not be implemented. You can get around this by
creating another instance of your class, and having that perform the async
operation.

For 3 and 4, that is up to you to create the progress events and the
cancel method. Basically, you would have a flag somewhere which indicates
whether or not the operation should be cancelled. As your synchronous
method is run, it checks the flag (access to the flag should be synchronized
through the lock statement) and if the flag is set, cancel the operation,
and exit the method.

Of course, if the method has a main loop that runs, it becomes very
obvious where to check the flag to see if it is a cancelled operation.

Hope this helps.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Shawn Meyer said:
Hello -

I am trying to write a class that has an async BeginX and EndX, plus the
regular X syncronous method. Delegates seemed like the way to go,
however, I still am having problems getting exactly what I want. Here are
my goals

1. I would like the IAsyncResult that is returned by the Begin function to
be able to be waited on or polled to check for completion.
2. The Begin function would take a callback, and the async process would
call it when the task is complete.
3. I would like to have events such as ProgressChanged that get fired as
the
async process progresses.
4. The async process should be able to be cancled with a function CancelX.
5. Multiple async tasks should be able to be performed at the same time.

Are delegates the way to go? Can anyone provide me with a quick example
on
how to implement this.

Thanks,
Shawn
 
S

Shawn B.

Okay, I correct myself, I am creating a different delegate instance each
time the command is created, so it isn't reusing the same instance.


#region ExecuteTable Asynchronous

public virtual IAsyncResult BeginExecuteTable(
string sqlQuery,
bool storedProcedure
)
{
ExecuteTableDelegate result = new
ExecuteTableDelegate(this.ExecuteTableAsync);
return result.BeginInvoke(sqlQuery, storedProcedure, null, null);
}

public virtual IAsyncResult BeginExecuteTable(
string sqlQuery,
bool storedProcedure,
AsyncCallback callback
)
{
ExecuteTableDelegate result = new
ExecuteTableDelegate(this.ExecuteTableAsync);
return result.BeginInvoke(sqlQuery, storedProcedure, callback, result);
}


public virtual DataTable EndExecuteTable(IAsyncResult ar)
{
ExecuteTableDelegate result = null;

if (ar.AsyncState != null)
{
result = (ExecuteTableDelegate)ar.AsyncState;
}
else
{
result = (ExecuteTableDelegate)((AsyncResult)ar).AsyncDelegate;
}

ar.AsyncWaitHandle.WaitOne();
return result.EndInvoke(ar);
}

protected virtual DataTable ExecuteTableAsync(
string sqlQuery,
bool storedProcedure
)
{
DbAsyncBase data = (DbAsyncBase)GetNewDalInstance();
DataTable result = null;

try
{
AsyncCounter++;

foreach(IDbDataParameter param in Parameters)
{
data.Parameters.Add(param);
}

result = data.ExecuteTable(sqlQuery, storedProcedure);
}
finally
{
if (data != null)
{
data.Dispose();
}

AsyncCounter--;
}

return result;
}


#endregion




Shawn B. said:
(I'm not the Original Poster)

:: The reason for this is that the delegate instance is linked
:: to the IAsyncResult returned from the call to BeginInvoke,
:: and if you create a new one, it will not work. This is the
:: reason #5 can not be implemented. You can get around
:: this by creating another instance of your class, and having
:: that perform the async operation.

This is a curious remark. I don't recall reading this in the MSDN
documentation.

I created a data access component about 2 1/2 years ago (long before ADO.NET
2.0 was ever announced and even before the data access application block
ever existed) that has ability to execute readers, scalars, datasets,
datatables, and nonqueries asyncronously (I am more than happy to provide
the source code if anyone wants it, C#, and very neat and tidy). This same
component has been in 3 production environments over the years and in each
invenvironment there are close to about 3 million page requests per day. It
uses delegates internally to execute the async results, in much the same way
you described below. The only difference between what you describe and the
behaviors we receive, are that I can indeed execute as many commands as I
want simultaneously to return a datatable and all the results come back with
their proper results and the requests don't get queued. For example, if I
execute 10 commands at the same time async (on the same object instance,
delegate instance) and each command requires 3 seconds to complete, then I
only need to wait roughly 3 seconds before I have the results for all 10
commands properly.

I'm curious what you mean by your above statement?

Here's my source to demonstrate what I'm saying:

private void button1_Click(object sender, System.EventArgs e)
{
SqlDataHelper data = null;
DataTable x = null;
DataTable y = null;
DataTable z = null;
DataTable t = null;

try
{
data = new SqlDataHelper(connectionString);

IAsyncResult ar1 = data.BeginExecuteTable("SELECT * FROM Orders",
false);
IAsyncResult ar2 = data.BeginExecuteTable("SELECT TOP 5000 * FROM
Invoices", false);
IAsyncResult ar3 = data.BeginExecuteTable("SELECT TOP 10000 * FROM
AuditLog", false);

// Do something while the other 3 are still fetching
//
t = data.ExecuteTable("SELECT * FROM SalesGroups", false);

foreach (DataRow row in t.Rows)
{
row[0] = 0;
}

data.BeginExecuteReader("SELECT TOP 5 * FROM InventoryPieces", false,
new AsyncCallback(CallbackMethod));

// Now we synchronize
//
x = data.EndExecuteTable(ar1);
y = data.EndExecuteTable(ar2);
z = data.EndExecuteTable(ar3);

MessageBox.Show(t.Rows.Count.ToString());
MessageBox.Show(x.Rows.Count.ToString());
MessageBox.Show(y.Rows.Count.ToString());
MessageBox.Show(z.Rows.Count.ToString());
}
finally
{
if (data != null)
{
data.Dispose();
}
}
}

public void CallbackMethod(IAsyncResult ar)
{
SqlDataHelper data = null;
IDataReader result = null;
int count = 0;

try
{
data = new SqlDataHelper(connectionString);

result = data.EndExecuteReader(ar);

while (result.Read()) count++;
result.Close();

MessageBox.Show("Callback: " + count.ToString());
}
catch (Exception ex)
{
throw;
}
finally
{
if (data != null)
{
data.Dispose();
}
}
}


If you would like to see my data access component I'll provide you a link to
download the source code.


Thanks,
Shawn




message news:[email protected]...
Shawn,

I believe that all but the last request will be able to be handled with
delegates.

Basically, create a private delegate that has the same signature as your
synchronous method. When compiled, the delegate will create BeginInvoke and
EndInvoke methods which can be used to make the call asynchronously. Then,
all you have to do is expose the methods (BeginXXX and EndXXX) with the same
signatures as the corresponding methods on the delegate.

When those methods are called, you just channel the call to a delegate
instance and return the appropriate IAsyncResult. The delegate, of course,
points to your synchronous method implementation.

The thing is, you have to store the delegate in your class instance, and
can only have one async operation at a time being performed. The reason for
this is that the delegate instance is linked to the IAsyncResult returned
from the call to BeginInvoke, and if you create a new one, it will not work.
This is the reason #5 can not be implemented. You can get around this by
creating another instance of your class, and having that perform the async
operation.

For 3 and 4, that is up to you to create the progress events and the
cancel method. Basically, you would have a flag somewhere which indicates
whether or not the operation should be cancelled. As your synchronous
method is run, it checks the flag (access to the flag should be synchronized
through the lock statement) and if the flag is set, cancel the operation,
and exit the method.

Of course, if the method has a main loop that runs, it becomes very
obvious where to check the flag to see if it is a cancelled operation.

Hope this helps.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Shawn Meyer said:
Hello -

I am trying to write a class that has an async BeginX and EndX, plus the
regular X syncronous method. Delegates seemed like the way to go,
however, I still am having problems getting exactly what I want. Here are
my goals

1. I would like the IAsyncResult that is returned by the Begin
function
 
N

Nicholas Paldino [.NET/C# MVP]

Shawn,

I was a bit off my rocker when I responded to this. You could do this.
My thought was that a different delegate would be created for each
invocation of the asynchronous method, which would require the instances to
be stored on the class level, and then when you called the EndXXX method, it
would throw an exception, because the IAsyncResult implementation would not
match with the delegate that created it.

However, a simple workaround would be to just create one delegate, and
have that handle all of the asynchronous calls.

The only problem that exists for #5 is that if this is allowed, then the
progress and the cancel methods would have to have some way of indicating
which asynchronous call is being updated or cancelled.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Shawn B. said:
(I'm not the Original Poster)

:: The reason for this is that the delegate instance is linked
:: to the IAsyncResult returned from the call to BeginInvoke,
:: and if you create a new one, it will not work. This is the
:: reason #5 can not be implemented. You can get around
:: this by creating another instance of your class, and having
:: that perform the async operation.

This is a curious remark. I don't recall reading this in the MSDN
documentation.

I created a data access component about 2 1/2 years ago (long before
ADO.NET
2.0 was ever announced and even before the data access application block
ever existed) that has ability to execute readers, scalars, datasets,
datatables, and nonqueries asyncronously (I am more than happy to provide
the source code if anyone wants it, C#, and very neat and tidy). This
same
component has been in 3 production environments over the years and in each
invenvironment there are close to about 3 million page requests per day.
It
uses delegates internally to execute the async results, in much the same
way
you described below. The only difference between what you describe and
the
behaviors we receive, are that I can indeed execute as many commands as I
want simultaneously to return a datatable and all the results come back
with
their proper results and the requests don't get queued. For example, if I
execute 10 commands at the same time async (on the same object instance,
delegate instance) and each command requires 3 seconds to complete, then I
only need to wait roughly 3 seconds before I have the results for all 10
commands properly.

I'm curious what you mean by your above statement?

Here's my source to demonstrate what I'm saying:

private void button1_Click(object sender, System.EventArgs e)
{
SqlDataHelper data = null;
DataTable x = null;
DataTable y = null;
DataTable z = null;
DataTable t = null;

try
{
data = new SqlDataHelper(connectionString);

IAsyncResult ar1 = data.BeginExecuteTable("SELECT * FROM Orders",
false);
IAsyncResult ar2 = data.BeginExecuteTable("SELECT TOP 5000 * FROM
Invoices", false);
IAsyncResult ar3 = data.BeginExecuteTable("SELECT TOP 10000 * FROM
AuditLog", false);

// Do something while the other 3 are still fetching
//
t = data.ExecuteTable("SELECT * FROM SalesGroups", false);

foreach (DataRow row in t.Rows)
{
row[0] = 0;
}

data.BeginExecuteReader("SELECT TOP 5 * FROM InventoryPieces", false,
new AsyncCallback(CallbackMethod));

// Now we synchronize
//
x = data.EndExecuteTable(ar1);
y = data.EndExecuteTable(ar2);
z = data.EndExecuteTable(ar3);

MessageBox.Show(t.Rows.Count.ToString());
MessageBox.Show(x.Rows.Count.ToString());
MessageBox.Show(y.Rows.Count.ToString());
MessageBox.Show(z.Rows.Count.ToString());
}
finally
{
if (data != null)
{
data.Dispose();
}
}
}

public void CallbackMethod(IAsyncResult ar)
{
SqlDataHelper data = null;
IDataReader result = null;
int count = 0;

try
{
data = new SqlDataHelper(connectionString);

result = data.EndExecuteReader(ar);

while (result.Read()) count++;
result.Close();

MessageBox.Show("Callback: " + count.ToString());
}
catch (Exception ex)
{
throw;
}
finally
{
if (data != null)
{
data.Dispose();
}
}
}


If you would like to see my data access component I'll provide you a link
to
download the source code.


Thanks,
Shawn




in
message news:[email protected]...
Shawn,

I believe that all but the last request will be able to be handled with
delegates.

Basically, create a private delegate that has the same signature as your
synchronous method. When compiled, the delegate will create BeginInvoke and
EndInvoke methods which can be used to make the call asynchronously. Then,
all you have to do is expose the methods (BeginXXX and EndXXX) with the same
signatures as the corresponding methods on the delegate.

When those methods are called, you just channel the call to a
delegate
instance and return the appropriate IAsyncResult. The delegate, of course,
points to your synchronous method implementation.

The thing is, you have to store the delegate in your class instance, and
can only have one async operation at a time being performed. The reason for
this is that the delegate instance is linked to the IAsyncResult returned
from the call to BeginInvoke, and if you create a new one, it will not work.
This is the reason #5 can not be implemented. You can get around this by
creating another instance of your class, and having that perform the
async
operation.

For 3 and 4, that is up to you to create the progress events and the
cancel method. Basically, you would have a flag somewhere which
indicates
whether or not the operation should be cancelled. As your synchronous
method is run, it checks the flag (access to the flag should be synchronized
through the lock statement) and if the flag is set, cancel the operation,
and exit the method.

Of course, if the method has a main loop that runs, it becomes very
obvious where to check the flag to see if it is a cancelled operation.

Hope this helps.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Shawn Meyer said:
Hello -

I am trying to write a class that has an async BeginX and EndX, plus
the
regular X syncronous method. Delegates seemed like the way to go,
however, I still am having problems getting exactly what I want. Here are
my goals

1. I would like the IAsyncResult that is returned by the Begin function to
be able to be waited on or polled to check for completion.
2. The Begin function would take a callback, and the async process
would
call it when the task is complete.
3. I would like to have events such as ProgressChanged that get fired
as
the
async process progresses.
4. The async process should be able to be cancled with a function CancelX.
5. Multiple async tasks should be able to be performed at the same
time.

Are delegates the way to go? Can anyone provide me with a quick
example
on
how to implement this.

Thanks,
Shawn
 
S

Shawn Meyer

I would love to see the code! It sounds very similar to what I am trying to
do.

Thanks much.
Shawn
 
N

Nicholas Paldino [.NET/C# MVP]

Shawn,

The reason this works for you is that you are passing the IAsyncResult
implementation, which happens to have a reference back to the delegate that
created it. Personally, I don't think that this is a good idea, since it
wastes the slot for information, should you need to pass that around (and I
wouldn't recommend this as standard practice).

However, as you show in your code, you can always cast the IAsyncResult
implementation to the AsyncResult instance which has a reference to the
delegate that created it. It is better to use this, and free up the slot
that has the data in it.

Also, your call to WaitOne on the AsyncWaitHandle property on the async
result is not needed. When you call EndInvoke, if the call hasn't
completed, it will block until it is.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Shawn B. said:
Okay, I correct myself, I am creating a different delegate instance each
time the command is created, so it isn't reusing the same instance.


#region ExecuteTable Asynchronous

public virtual IAsyncResult BeginExecuteTable(
string sqlQuery,
bool storedProcedure
)
{
ExecuteTableDelegate result = new
ExecuteTableDelegate(this.ExecuteTableAsync);
return result.BeginInvoke(sqlQuery, storedProcedure, null, null);
}

public virtual IAsyncResult BeginExecuteTable(
string sqlQuery,
bool storedProcedure,
AsyncCallback callback
)
{
ExecuteTableDelegate result = new
ExecuteTableDelegate(this.ExecuteTableAsync);
return result.BeginInvoke(sqlQuery, storedProcedure, callback, result);
}


public virtual DataTable EndExecuteTable(IAsyncResult ar)
{
ExecuteTableDelegate result = null;

if (ar.AsyncState != null)
{
result = (ExecuteTableDelegate)ar.AsyncState;
}
else
{
result = (ExecuteTableDelegate)((AsyncResult)ar).AsyncDelegate;
}

ar.AsyncWaitHandle.WaitOne();
return result.EndInvoke(ar);
}

protected virtual DataTable ExecuteTableAsync(
string sqlQuery,
bool storedProcedure
)
{
DbAsyncBase data = (DbAsyncBase)GetNewDalInstance();
DataTable result = null;

try
{
AsyncCounter++;

foreach(IDbDataParameter param in Parameters)
{
data.Parameters.Add(param);
}

result = data.ExecuteTable(sqlQuery, storedProcedure);
}
finally
{
if (data != null)
{
data.Dispose();
}

AsyncCounter--;
}

return result;
}


#endregion




Shawn B. said:
(I'm not the Original Poster)

:: The reason for this is that the delegate instance is linked
:: to the IAsyncResult returned from the call to BeginInvoke,
:: and if you create a new one, it will not work. This is the
:: reason #5 can not be implemented. You can get around
:: this by creating another instance of your class, and having
:: that perform the async operation.

This is a curious remark. I don't recall reading this in the MSDN
documentation.

I created a data access component about 2 1/2 years ago (long before ADO.NET
2.0 was ever announced and even before the data access application block
ever existed) that has ability to execute readers, scalars, datasets,
datatables, and nonqueries asyncronously (I am more than happy to provide
the source code if anyone wants it, C#, and very neat and tidy). This same
component has been in 3 production environments over the years and in
each
invenvironment there are close to about 3 million page requests per day. It
uses delegates internally to execute the async results, in much the same way
you described below. The only difference between what you describe and the
behaviors we receive, are that I can indeed execute as many commands as I
want simultaneously to return a datatable and all the results come back with
their proper results and the requests don't get queued. For example, if
I
execute 10 commands at the same time async (on the same object instance,
delegate instance) and each command requires 3 seconds to complete, then
I
only need to wait roughly 3 seconds before I have the results for all 10
commands properly.

I'm curious what you mean by your above statement?

Here's my source to demonstrate what I'm saying:

private void button1_Click(object sender, System.EventArgs e)
{
SqlDataHelper data = null;
DataTable x = null;
DataTable y = null;
DataTable z = null;
DataTable t = null;

try
{
data = new SqlDataHelper(connectionString);

IAsyncResult ar1 = data.BeginExecuteTable("SELECT * FROM Orders",
false);
IAsyncResult ar2 = data.BeginExecuteTable("SELECT TOP 5000 * FROM
Invoices", false);
IAsyncResult ar3 = data.BeginExecuteTable("SELECT TOP 10000 * FROM
AuditLog", false);

// Do something while the other 3 are still fetching
//
t = data.ExecuteTable("SELECT * FROM SalesGroups", false);

foreach (DataRow row in t.Rows)
{
row[0] = 0;
}

data.BeginExecuteReader("SELECT TOP 5 * FROM InventoryPieces", false,
new AsyncCallback(CallbackMethod));

// Now we synchronize
//
x = data.EndExecuteTable(ar1);
y = data.EndExecuteTable(ar2);
z = data.EndExecuteTable(ar3);

MessageBox.Show(t.Rows.Count.ToString());
MessageBox.Show(x.Rows.Count.ToString());
MessageBox.Show(y.Rows.Count.ToString());
MessageBox.Show(z.Rows.Count.ToString());
}
finally
{
if (data != null)
{
data.Dispose();
}
}
}

public void CallbackMethod(IAsyncResult ar)
{
SqlDataHelper data = null;
IDataReader result = null;
int count = 0;

try
{
data = new SqlDataHelper(connectionString);

result = data.EndExecuteReader(ar);

while (result.Read()) count++;
result.Close();

MessageBox.Show("Callback: " + count.ToString());
}
catch (Exception ex)
{
throw;
}
finally
{
if (data != null)
{
data.Dispose();
}
}
}


If you would like to see my data access component I'll provide you a link to
download the source code.


Thanks,
Shawn




message news:[email protected]...
Shawn,

I believe that all but the last request will be able to be handled with
delegates.

Basically, create a private delegate that has the same signature as your
synchronous method. When compiled, the delegate will create
BeginInvoke and
EndInvoke methods which can be used to make the call asynchronously. Then,
all you have to do is expose the methods (BeginXXX and EndXXX) with the same
signatures as the corresponding methods on the delegate.

When those methods are called, you just channel the call to a delegate
instance and return the appropriate IAsyncResult. The delegate, of course,
points to your synchronous method implementation.

The thing is, you have to store the delegate in your class
instance, and
can only have one async operation at a time being performed. The
reason for
this is that the delegate instance is linked to the IAsyncResult returned
from the call to BeginInvoke, and if you create a new one, it will not work.
This is the reason #5 can not be implemented. You can get around this by
creating another instance of your class, and having that perform the async
operation.

For 3 and 4, that is up to you to create the progress events and
the
cancel method. Basically, you would have a flag somewhere which indicates
whether or not the operation should be cancelled. As your synchronous
method is run, it checks the flag (access to the flag should be synchronized
through the lock statement) and if the flag is set, cancel the operation,
and exit the method.

Of course, if the method has a main loop that runs, it becomes very
obvious where to check the flag to see if it is a cancelled operation.

Hope this helps.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Hello -

I am trying to write a class that has an async BeginX and EndX, plus the
regular X syncronous method. Delegates seemed like the way to go,
however, I still am having problems getting exactly what I want.
Here are
my goals

1. I would like the IAsyncResult that is returned by the Begin
function
to
be able to be waited on or polled to check for completion.
2. The Begin function would take a callback, and the async process would
call it when the task is complete.
3. I would like to have events such as ProgressChanged that get fired as
the
async process progresses.
4. The async process should be able to be cancled with a function CancelX.
5. Multiple async tasks should be able to be performed at the same time.

Are delegates the way to go? Can anyone provide me with a quick example
on
how to implement this.

Thanks,
Shawn
 
S

Shawn B.

The reason this works for you is that you are passing the IAsyncResult
implementation, which happens to have a reference back to the delegate that
created it. Personally, I don't think that this is a good idea, since it
wastes the slot for information, should you need to pass that around (and I
wouldn't recommend this as standard practice).

However, as you show in your code, you can always cast the IAsyncResult
implementation to the AsyncResult instance which has a reference to the
delegate that created it. It is better to use this, and free up the slot
that has the data in it.
I will attempt to make changes as necessary to see if this can work on one
delegate instance. My memory doesn't serve anymore, but there was a reason
I did it this way after a bit of trial and error. If there's a better way,
I'll gladly make the necessary changes. This begs the question, if one
delegate instance can hold one result, how am I going to achieve the effect
of multiple executing commands?
Also, your call to WaitOne on the AsyncWaitHandle property on the async
result is not needed. When you call EndInvoke, if the call hasn't
completed, it will block until it is.
I dissagree (regarding my situation but not in general). When the WaitOne
call isn't there, the same sample code I posted earlier won't work properly.
There will be unpredictable results, for example, it seems that each of the
ar1, ar2, ar3 references gets confused regarding what result it should
actually have. Sometimes all ar1, ar2, ar3 have the same results as ar1,
other times ar1 will contain ar2 and ar3 will contain ar1 results and so on.
The only way to get reliable results is to either call WaitOne yourself and
then EndExecute*, or, to have it done automatically for you (which I'm doing
here since I'm a fan of less work for the developer using the component). I
don't dissagree that when you call EndInvoke it is done (thats what I read
all the time), but for some reason, in this case, it causes unreliable
results so I had to put it there.


Thanks,
Shawn
 
S

Shawn B.

However, as you show in your code, you can always cast the
IAsyncResult
implementation to the AsyncResult instance which has a reference to the
delegate that created it. It is better to use this, and free up the slot
that has the data in it.

Could you elaborate on this more, specifically, when you say "it is better
to use this, and free upt he slot that has the data in it".


Thanks,
Shawn
 
N

Nicholas Paldino [.NET/C# MVP]

Shawn,

Basically, the last parameter when you call BeginXXX is a state that
gets passed around during the asynchronous call. You use it to place the
IAsyncResult implementation in there. However, this isn't needed, because
the implementation holds a reference to the original delegate that created
it, which is what you are using it for in the first place.
 
S

Shawn B.

Oh, I knew that. I just for some reason wasn't connecting your statement
with that in my brain. However, you did say that you would recommend doing
things the way I am, such as creating a new delegate each call. What other
ways could I achieve the same?

Thanks,
Shawn



Nicholas Paldino said:
Shawn,

Basically, the last parameter when you call BeginXXX is a state that
gets passed around during the asynchronous call. You use it to place the
IAsyncResult implementation in there. However, this isn't needed, because
the implementation holds a reference to the original delegate that created
it, which is what you are using it for in the first place.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Shawn B. said:
Could you elaborate on this more, specifically, when you say "it is better
to use this, and free upt he slot that has the data in it".


Thanks,
Shawn
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top