I try to implement the 'AsyncPattern' within a WCF data service. I define the 2 methods BeginGetExperiments(...) and EndGetExperiments(...) in the interface and implement the methods as can be seen below.
public class GmdProfileService : IGmdProfileService
{
IAsyncResult IGmdProfileService.BeginGetExperiments(AsyncCallback callback, object state)
{
//IAsyncResult res = Experiment.GetExperimentsAsync(callback, state, Properties.Settings.Default.gmdConnectionString);
//return res;
System.Data.SqlClient.SqlConnectionStringBuilder csb = new System.Data.SqlClient.SqlConnectionStringBuilder(Properties.Settings.Default.gmdConnectionString);
csb.AsynchronousProcessing = true;
System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection(csb.ConnectionString);
conn.Open();
System.Data.SqlClient.SqlCommand cmd = conn.CreateCommand();
cmd = conn.CreateCommand();
cmd.CommandText = "SELECT id, name, comment, date, doi FROM tf.TagList WITH(NOLOCK) WHERE proprietary=0;";
cmd.CommandType = System.Data.CommandType.Text;
return new SqlCommandAsyncResult(cmd, callback, state);
}
public List<Experiment> EndGetExperiments(IAsyncResult result)
{
List<Experiment> res = new List<Experiment>();
SqlCommandAsyncResult myresult = result as SqlCommandAsyncResult;
using (System.Data.SqlClient.SqlDataReader reader = myresult.cmd.EndExecuteReader(myresult.originalState as IAsyncResult))
{
try
{
while (reader.Read())
{
res.Add(new Experiment(reader));
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
// Closing the reader also closes the connection, because this reader was created using the CommandBehavior.CloseConnection value.
if (reader != null)
{
reader.Close();
}
}
}
return res;
}
BeginGetExperiments returns a class SqlCommandAsyncResult implementing the IAsyncResult interface in addition to holding a reference to my SqlCommand for later access.
public class SqlCommandAsyncResult : IAsyncResult
{
public SqlCommand cmd { get; private set; }
public IAsyncResult originalState { get; private set; }
public SqlCommandAsyncResult(SqlCommand cmd, AsyncCallback callback, object state)
{
this.cmd = cmd;
this.originalState = cmd.BeginExecuteReader(callback,
state,
System.Data.CommandBehavior.SequentialAccess | // doesn't load whole column into memory
System.Data.CommandBehavior.CloseConnection // close connection immediately after read
);
}
public object AsyncState
{
get { return originalState.AsyncState; }
}
public WaitHandle AsyncWaitHandle
{
get { return originalState.AsyncWaitHandle; }
}
public bool CompletedSynchronously
{
get { return false; }
}
public bool IsCompleted
{
get { return AsyncWaitHandle.WaitOne(0); }
}
}
The difficulties I face are in the EndGetExperiments method. I dont know how to access the SqlCommand to call EndExecuteReader(...).
Normally I would use the state object in the BeginExecutereader to pass on the command. But if I do so, I get the exception:
"IAsyncResult's State must be the state argument passed to your Begin call."
So I try to use the IAsyncResult to pass the SqlCommand forward to EndGetExperiments. Here, the point I don’t understand is that in EndGetExperiments the variable result is either of type IAsyncResult or of type SqlCommandAsyncResult depending on the value of CompletedSynchronously in the SqlCommandAsyncResult class.
Setting CompletedSynchronously = false makes my code fail because I cant't access the SqlCommand whereas setting CompletedSynchronously = true the code works like a charm but I have an odd feeling that something might go wrong under the hood.
I appreciate any help, guidance and example code how to make this code working and even more important in helping me to understand the problem at hand.
Thank you very much.
Jan
Today WCF Data Services doesn't support asynchronous processing on the server. Please vote/add a feature request for it here: http://data.uservoice.com/forums/72027-wcf-data-services-feature-suggestions/topics/72603-wcf-data-services-feature-suggestions
If you are using C# 4.0. This might be easier using Task<T> to achieve. Task<T> is to execute Func<T> where T is the return value. You can define a continuation task which fetch Parent.Result. I know this answer might not be what you are looking for. But please consider this as an alternative. The code will be cleaner, easy to maintain, and easy to debug (using Task Parallel Window, Parallel Stacks etc).
Related
I've read various questions/suggestions about this exception. However what I am supposed to do in order to avoid it when I use retry policy? Connection might not end up closed and so parameters could not be reused?
public class ReliableSqlCommand
{
public List<ResultType> ExecuteReader<ResultType>() where ResultType : new()
{
var list = new List<ResultType>();
var retryPolicy = new DWSqlAzureExecutionStrategy(SqlMaxRetryCount, SqlMaxDelay);
retryPolicy.Execute(() =>
{
list = new List<ResultType>();
using (var sqlConnection = new SqlConnection(ConnectionString))
{
using (var sqlCommand = new SqlCommand(CommandText, sqlConnection))
{
sqlCommand.CommandTimeout = CommandTimeout;
sqlCommand.CommandType = CommandType;
sqlCommand.Parameters.AddRange(Parameters.ToArray());
sqlCommand.Connection = sqlConnection;
sqlConnection.Open();
using (SqlDataReader dataReader = sqlCommand.ExecuteReader())
{
while (dataReader.Read())
{
if (typeof(ResultType).BaseType == typeof(System.ValueType))
{
var sqlValue = dataReader.GetValue(0);
if (sqlValue == DBNull.Value)
list.Add(default);
else
list.Add((ResultType)ChangeType(sqlValue, typeof(ResultType)));
}
else
{
//handle complex types (objects)
ResultType item = new ResultType();
Type itemType = item.GetType();
for (int columnNr = 0; columnNr < dataReader.FieldCount; columnNr++)
{
PropertyInfo prop = itemType.GetProperty(dataReader.GetName(columnNr));
if (prop == null) continue;
var value = dataReader.GetValue(columnNr);
if (value == null || value == DBNull.Value)
{
prop.SetValue(item, null);
}
else
{
prop.SetValue(item, value);
}
}
list.Add(item);
}
}
sqlConnection.Close();
}
sqlCommand.Parameters.Clear();
}
}
});
return list;
}
}
ReliableSqlCommand contains this as a property:
public List<SqlParameter> Parameters { get; } = new List<SqlParameter>();
After reviewing your code, I could imagine the following. (Note that I haven't tested it.)
You pass a function to retryPolicy.Execute(), which seems to correctly handle your database actions, disposing all connections, commands, datareaders, etc.
However, I assume that the retryPolicy can already start executing a new run of that function while a previous run is still active/running (or at least not yet fully completed). In that case, the parameters in ReliableSqlCommand.Parameters will be added to a new instance of SqlCommand, which is clearly not allowed when those parameters are still "alive" in a previous running function call in the background (which is still waiting for a database timeout exception, perhaps).
I do not see a straightforward stable/reliable fix for this.
Within the function, you could try to make new copies/instances of the Parameter objects and assign those copies to the SqlCommand instance. But in case you have output parameters, you will have to update the ReliableSqlCommand.Parameters collection afterwards. When having multiple running/overlapping function calls, that might be tricky as well.
I think what you need to do is either to ensure the parameters are removed from the old command, or cache the command
If I understand correctly, the Execute function retries the lambda, and swallows any exceptions along the way. It does not execute multiple times concurrently.
Unfortunately, SqlCommand.Dispose does not remove the parameters from the command.
So option 1 is:
using (var sqlCommand = new SqlCommand(CommandText, sqlConnection))
{
try
{
.......
}
finally
{
sqlCommand.Parameters.Clear();
}
}
A better option in my opinion, given that a parameter is supposed to be used with only one command, is to cache the command also.
There is nothing wrong with this, as long as the connection is changed each time.
public ReliableSqlCommand
{
public SqlCommand Command { get; set; }
Then instead of using (var sqlCommand = new SqlCommand..., just use the existing _command:
_command.Connection = sqlConnection;
If you don't want to expose your command object directly, you could make a wrapper that adds and deletes the parameters.
It's not strictly necessary to dispose SqlCommand, because it's Dispose does nothing. But for consistency's sake, you may want to have ReliableSqlCommand be disposable as well.
I tried this code on an array of 3 connection strings without any complaints.My question is, is it okay to invoke multiple dispose calls on the same object?
foreach (var s in strings)
{
connection.ConnectionString = s;
connection.Open();
connection.Close();
connection.Dispose();
}
Here is one way to do it:
bool TestConnection<T>(string connectionString) where T : IDbConnection, new
{
using(T con = new T())
{
con.ConnectionString = connectionString;
connection.Open();
return true;
}
}
Another way to implement connection testing code is with an extension method (note this does not dispose the connection object):
public static Tuple<bool, Exception> TestConnection(this IDbConnection connection)
{
try
{
connection.Open();
connection.Close();
return new Tuple<bool, Exception>(true, null);
}
catch(Exception e)
{
return new Tuple<bool, Exception>(false, e);
}
}
Please note in this version I'm returning a Tuple of bool and Exception so whoever use this code can get the information on why the connection failed, but not have to wrap the call in a try...catch block. Of course, you can choose to simply return a bool just like in the first example, this is just for demonstration purposes.
You should fix your code this way:
foreach (var s in strings)
{
connection.ConnectionString = s;
connection.Open();
connection.Close();
}
Connection doesn't need to dispose, or atleast you shoudln't dispose an object that you want to use again.
Anyway this isn't a good approach.
You should have a
using(DbContext db = new DbContext()){
//SQL Actions
}
for every db relative code, to avoid problems ^^
public bool TestConnection(IDbConnection con)
{
using (con)
{
try
{
con.Open();
con.Close();
return true;
}
catch
{
return false;
}
}
}
It's "ok" with what you are doing (completely different connect everytime with no ran queries) but as Amy said in the comments, it really doesn't get you anything special. Should probably abide by the wisdom of not reusing disposed objects.
Also for SqlConnection, calling Close then dispose is repetitive since it will call its close upon dispose.
Going to throw my code into the mix as well, comments in code:
private bool DBValidCheck(string connection)
{
//Using statement releases the object that implement iDisposable once it exits the block. Takes care of the dispose
using (var connection = new SqlConnection(connection))
{
try
{
connection.Open();
return true;
}
catch
{
return false;
}
}
}
I have to support an old project which uses PageAsyncTask with webforms vs2010 fw4.
However this simple code does compile + execute( no errors in debug mode / trace mode) but the response never ends.
Looking in debug mode + breakpoints - it reaches all stages of code.
public partial class _Default2 : System.Web.UI.Page
{
IAsyncResult BeginGetData(object sender, EventArgs e, AsyncCallback callback, object state)
{
SqlConnection con = new SqlConnection(#"Data Source=... Asynchronous Processing=True;");
var sql = #" SELECT [NAME] FROM [WebERP].[dbo].[t]";
{
SqlCommand _cmd = null;
try
{
_cmd = new SqlCommand(sql, con);
_cmd.CommandTimeout = 100000;
con.Open();
return _cmd.BeginExecuteReader(EndGetData, _cmd);
}
catch (Exception ex)
{
if (_cmd != null) _cmd.Dispose();
con.Close();
throw;
}
}
}
void EndGetData(IAsyncResult ar)
{
(ar.AsyncState as SqlCommand).EndExecuteReader(ar);
Response.Write(1111); // HttpContext.Current.Response also doesnt help
}
void TimeoutData(IAsyncResult ar)
{
Response.Write("Could not retrieve data!");
}
protected void Page_Load(object sender, EventArgs e)
{
PageAsyncTask task = new PageAsyncTask(BeginGetData, EndGetData, TimeoutData, null, true);
Page.RegisterAsyncTask(task);
Page.ExecuteRegisteredAsyncTasks();
}
}
Question
The response never ends. All I see is :
What am I missing ?
(nb Async="true" is on the page directive , also - code was simplified just to describe the case )
I think the problem here is that you pass the same BeginGetData and EndGetData callbacks to both BeginExecuteReader and new PageAsyncTask(). They should be different: what you pass to PageAsyncTask is "outer" to what you pass to BeginExecuteReader.
You'd call BeginExecuteReader from the begin callback you'd pass to PageAsyncTask, and you'd be actually required to call the AsyncCallback callback provided to you there by ASP.NET (you'd call it when the async operation would have finished, i.e., from your EndGetData).
It would be so much easier if you could use the PageAsyncTask(Func<Task>) override, but I don't think you can target .NET 4.5 with VS2010.
I have a method which checks whether a SQL Server is available or not
public static bool IsServerAvailable(string connection)
{
bool isAvailable = false;
try
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
isAvailable = true;
}
}
catch
{
isAvailable = false;
}
return isAvailable;
}
Now i have to write a method which accepts a dictionary having keys as connection strings and update the value depending on the result returned from above method
But the problem is i need to make asynchronous calls and able to do, but facing issues with updating the value of a corresponding conn (key) in dictionary
public delegate bool CheckConnectionDelegate(string conn);
public static void CheckCubeAccessibility(Dictionary<ServerName, bool> serverDict)
{
string conn;
//Create the delegate
CheckConnectionDelegate serverConn = new CheckConnectionDelegate(IsServerAvailable);
foreach (var server in serverDict)
{
switch (server.Key)
{
case ServerName.A:
conn = "A";
break;
case ServerName.B:
conn = "B";
break;
case ServerName.C:
conn = "C";
break;
}
//Call the begin invoke method
IAsyncResult res = serverConn.BeginInvoke(conn, null, null);
}
}
Can any one help me?
solution for the above problem is
Writing a lambda expression mentioned at How to get the parameters passed to the asynchronous method in the callback
But i need a solution to delay the function CheckCubeAccessibility return untill all the Async calls are returned
How to do this?
The Dictionary needs to be outside of the function.
You need to do the Async call and have a callback method to another function that then updates the Dictionary.
Change this line to:
IAsyncResult res = serverConn.BeginInvoke(conn, new AsyncCallback(CallbackMethod), null);
Then create the function:
static void CallbackMethod(IAsyncResult ar)
{
}
Then do your updating there.
Read this for more information: Calling Synchronous Methods Asynchronously
*Edit: Please see my answer below for the solution.
Is there any danger in the following? I'm trying to track down what I think might be a race condition. I figured I'd start with this and go from there.
private BlockingCollection<MyTaskType>_MainQ = new BlockingCollection<MyTaskType>();
private void Start()
{
_CheckTask = new Timer(new TimerCallback(CheckTasks), null, 10, 5000);
}
private void CheckTasks(object state)
{
_CheckTask.Change(Timeout.Infinite, Timeout.Infinite);
GetTask();
_CheckTask.Change(5000,5000);
}
private void GetTask()
{
//get task from database to object
Task.Factory.StartNew( delegate {
AddToWorkQueue(); //this adds to _MainQ which is a BlockingCollection
});
}
private void AddToWorkQueue()
{
//do some stuff to get stuff to move
_MainQ.Add(dataobject);
}
edit: I am also using a static class to handle writing to the database. Each call should have it's own unique data called from many threads, so it is not sharing data. Do you think this could be a source of contention?
Code below:
public static void ExecuteNonQuery(string connectionString, string sql, CommandType commandType, List<FastSqlParam> paramCollection = null, int timeout = 60)
{
//Console.WriteLine("{0} [Thread {1}] called ExecuteNonQuery", DateTime.Now.ToString("HH:mm:ss:ffffff"), System.Threading.Thread.CurrentThread.ManagedThreadId);
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand command = new SqlCommand(sql, connection))
{
try
{
if (paramCollection != null)
{
foreach (FastSqlParam fsqlParam in paramCollection)
{
try
{
SqlParameter param = new SqlParameter();
param.Direction = fsqlParam.ParamDirection;
param.Value = fsqlParam.ParamValue;
param.ParameterName = fsqlParam.ParamName;
param.SqlDbType = fsqlParam.ParamType;
command.Parameters.Add(param);
}
catch (ArgumentNullException anx)
{
throw new Exception("Parameter value was null", anx);
}
catch (InvalidCastException icx)
{
throw new Exception("Could not cast parameter value", icx);
}
}
}
connection.Open();
command.CommandType = commandType;
command.CommandTimeout = timeout;
command.ExecuteNonQuery();
if (paramCollection != null)
{
foreach (FastSqlParam fsqlParam in paramCollection)
{
if (fsqlParam.ParamDirection == ParameterDirection.InputOutput || fsqlParam.ParamDirection == ParameterDirection.Output)
try
{
fsqlParam.ParamValue = command.Parameters[fsqlParam.ParamName].Value;
}
catch (ArgumentNullException anx)
{
throw new Exception("Output parameter value was null", anx);
}
catch (InvalidCastException icx)
{
throw new Exception("Could not cast parameter value", icx);
}
}
}
}
catch (SqlException ex)
{
throw ex;
}
catch (ArgumentException ex)
{
throw ex;
}
}
}
per request:
FastSql.ExecuteNonQuery(connectionString, "someProc", System.Data.CommandType.StoredProcedure, new List<FastSqlParam>() { new FastSqlParam(SqlDbType.Int, "#SomeParam", variable)});
Also, I wanted to note that this code seems to fail at random running it from VS2010 [Debug or Release]. When I do a release build, run setup on a dev server that will be hosting it, the application has failed to crash and has been running smoothly.
per request:
Current architecture of threads:
Thread A reading 1 record from a database scheduling table
Thread A, if a row is returned, launches a Task to login to resource to see if there are files to transfer. The task is referencing an object that contains data from the DataTable that was creating using a static call. Basically as below.
If there are files found, Task adds to _MainQ the files to move
//Called from Thread A
void ProcessTask()
{
var parameters = new List<FastSqlParam>() { new FastSqlParam(SqlDbType.Int, "#SomeParam", variable) };
using (DataTable someTable = FastSql.ExecuteDataTable(connectionString, "someProc", CommandType.StoredProcedure, parameters))
{
SomeTask task = new Task();
//assign task some data from dt.Rows[0]
if (task != null)
{
Task.Factory.StartNew(delegate { AddFilesToQueue(task); });
}
}
}
void AddFilesToQueue(Task task)
{
//connect to remote system and build collection of files to WorkItem
//e.g, WorkItem will have a collection of collections to transfer. We control this throttling mechanism to allow more threads to split up the work
_MainQ.Add(WorkItem);
}
Do you think there could be a problem returning a value from FastSql.ExecuteDataTable since it is a static class and then using it with a using block?
I'd personally be wary of introducing extra threads in quite so many places - "Here be Dragons" is a useful rule when it comes to working with threads! I can't see any problems with what you have, but if it were simpler it'd be easier to be more certain. I'm assuming you want the call to "AddToWorkQueue" to be done in a different thread (to test the race condition) so I've left that in.
Does this do what you need it to? (eye compiled so may be wrong)
while(true) {
Task.Factory.StartNew( delegate { AddToWorkQueue(); });
Thread.Sleep(5000);
}
random aside - prefer "throw;" to "throw ex;" - the former preserves the original call stack, the latter will only give you the line number of the "throw ex;" call. Even better, omit the try/catch in this case as all you do is re-throw the exceptions, so you may as well save yourself the overhead of the try.
It turns out the problem was a very, very strange one.
I converted the original solution from a .NET 3.5 solution to a .NET 4.0 solution. The locking up problem went away when I re-created the entire solution in a brand new .NET 4.0 solution. No other changes were introduced, so I am very confident the problem was the upgrade to 4.0.