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.
Related
I'm having an issue getting my transaction scope to rollback while using async/await. Everything works as it should without the transaction scope, but whenever I intentionally cause an exception (duplicate primary key on the insert for 2nd iteration), no rollback (for the update) or any sort of transaction related error occurs.
I should also note that unless "OLE DB Services=-4" is in the connection string, I receive the error:
"The ITransactionLocal interface is not supported by the 'Microsoft.ACE.OLEDB.12.0' provider. Local transactions are unavailable with the current provider."
The code in the button event handler below is just an example for testing the transaction scope. The main goal is to be able to update multiple tables in a loop that's contained in a transaction asynchronously, so I can avoid UI deadlocks and perform rollbacks for any exceptions that may occur during the loop. Any alternatives or suggestions to my problem are appreciated, thanks :)
private async void button1_Click(object sender, EventArgs e)
{
try
{
int customerCount = 150; // First 150 rows of customer table
TransactionScope transaction = null;
using (OleDbConnection dbConn = new OleDbConnection(Provider = Microsoft.ACE.OLEDB.12.0; OLE DB Services=-4; Data Source = " + filePath))
{
dbConn.Open();
using (transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
for (int i = 0; i < customerCount; i++)
{
// Update field indicating customer made an invoice
var taskName = sql.executeAsync("UPDATE Customer SET lastInvoiceDate = #date WHERE customerID = #custID", dbConn,
new OleDbParameter("#date", DateTime.Today),
new OleDbParameter("#custID", i));
// Insert new invoice - Breaks here
var taskInsert = sql.executeAsync("INSERT INTO Invoice VALUES (1, 'thisisatestinvoice', '$100.50')", dbConn);
await Task.WhenAll(taskName, taskInsert);
}
}
// All updates executed properly
transaction.Complete();
}
}
catch (AggregateException exception)
{
foreach (Exception ex in exception.InnerExceptions)
{
MessageBox.Show(ex.Message);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
public async Task executeAsync(string dbQuery, OleDbConnection dbConn, params OleDbParameter[] parameters)
{
var dbComm = new OleDbCommand(dbQuery, dbConn);
if (parameters != null)
dbComm.Parameters.AddRange(parameters);
await dbComm.ExecuteNonQueryAsync().ConfigureAwait(false);
}
I wasn't able to get the transaction scope to work, and I'm not entirely sure what the issue is, I think it's due to me being on ACE.OLEDB.12.0, but I found another alternative with OleDbTransaction that will rollback if any failures occur.
private async void button1_Click(object sender, EventArgs e)
{
try
{
using (OleDbConnection dbConn = new OleDbConnection(SQLWrapper.CONNECT_STRING))
{
dbConn.Open();
OleDbTransaction dbTrans = dbConn.BeginTransaction();
var taskName = sql.executeAsync("UPDATE Config SET Busname = #name", dbConn, dbTrans,
new OleDbParameter("#name", "name"));
var taskInsert = sql.executeAsync("INSERT INTO Callout VALUES (16, 'ryanistesting')", dbConn, dbTrans);
await Task.WhenAll(taskName, taskInsert);
dbTrans.Commit();
}
}
}
public async Task executeAsync(string dbQuery, OleDbConnection dbConn, OleDbTransaction dbTrans, params OleDbParameter[] parameters)
{
using (var dbComm = new OleDbCommand(dbQuery, dbConn))
{
if (parameters != null)
dbComm.Parameters.AddRange(parameters);
if (dbTrans != null)
dbComm.Transaction = dbTrans;
await dbComm.ExecuteNonQueryAsync().ConfigureAwait(false);
}
}
Maybe you know the answer by now as is' been a while. Same issue happened to me. Thing is that when using ConfigureAwait(false) you are telling the runtime to pick any free thread from the thread pool instead of waiting for the previous used when calling the async method. A different thread is used with a different context, creating another connection under the same transaction scope which was meant for only one connection. Then the transaction gets promoted to a distributed transaction. That was my experience, I had to abandon the idea of using async as it best and wait to for the previos thread to finish by not using configureAwait(false) or Task.WhenAll. Hope that help!
I hosted a web application in IIS7.With in the server can i track the number of request and request details from client programmatically.
Please advice
Within your app you can implement Application_BeginRequest() (in global.asax)
Inside this function you can use the Request object and you are probably interested in Request.Path, Request.RawUrl, Request.UserHostAddress, Request.InputStream, Request.Form.AllKeys.
It would be best if you provide more details on what you'll be doing with the data, for example if the goal is to get analytics, then I would strongly suggest to instead use Google Analytics or other similar products (AppInsights, WebTrends, or others).
Having said that, you can certainly do that without modifying the application, you can write a managed code module that you register in the server through configuration that implements an IHttpModule, something like:
public class TrackingModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.BeginRequest += OnBeginRequest;
}
void OnBeginRequest(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
HttpRequest request = app.Context.Request;
if (request == null)
{
return;
}
string url = request.RawUrl;
string userAddress = request.UserHostAddress;
DateTime time = DateTime.UtcNow;
string userAgent = request.UserAgent;
// Store somewhere the data...
}
}
Long time ago, I gave a few talks on how to Extend IIS and wrote a SQL Logging Provider that could give you some context. This one uses the LogRequest event that is exactly suited for that. You just need to set it up in ApplicationHost.config or the root web.config and you will not need to modify anything in the application and you'll get all of that:
#define TRACE
using System;
using System.Web;
using System.Data.SqlClient;
using System.Web.Hosting;
public class SqlLoggingModule : IHttpModule {
public void Dispose() {
}
public void Init(HttpApplication context) {
context.LogRequest += new EventHandler(OnLogRequest);
}
void OnLogRequest(object sender, EventArgs e) {
HttpApplication app = (HttpApplication)sender;
try {
Log(app.Context);
}
catch (Exception ex) {
System.Diagnostics.Trace.TraceError(ex.ToString());
app.Context.Trace.Warn(ex.ToString());
}
}
private void Log(HttpContext ctx) {
string connectionString = #"server=(local);database=TechEd;uid=youruser;password=yourpassword;";
// Disable Kernel Cache
ctx.Response.DisableKernelCache();
using (SqlConnection connection = new SqlConnection(connectionString)) {
SqlCommand cmd = connection.CreateCommand();
cmd.CommandText =
"insert into Log (Date, Method, IPAddress, Url, UserName, UserAgent, ResponseCode, SiteName, ApplicationName) values" +
"(#Date, #Method, #IPAddress, #Url, #UserName, #UserAgent, #ResponseCode, #SiteName, #ApplicationName)";
cmd.Parameters.AddWithValue("#Date", DateTime.Now);
cmd.Parameters.AddWithValue("#Method", ctx.Request.HttpMethod);
cmd.Parameters.AddWithValue("#IPAddress", ctx.Request.UserHostAddress);
cmd.Parameters.AddWithValue("#Url", ctx.Request.Url.ToString());
cmd.Parameters.AddWithValue("#UserName", ctx.Request.ServerVariables["LOGON_USER"]);
cmd.Parameters.AddWithValue("#UserAgent", ctx.Request.UserAgent);
cmd.Parameters.AddWithValue("#ResponseCode", ctx.Response.StatusCode + "." + ctx.Response.SubStatusCode);
cmd.Parameters.AddWithValue("#SiteName", HostingEnvironment.SiteName);
cmd.Parameters.AddWithValue("#ApplicationName", ctx.Request.ApplicationPath);
connection.Open();
cmd.ExecuteNonQuery();
}
}
}
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).
I've made a small utility program to test if a PC has a possibility to connect to a certain Oracle database.
To keep the UI responsive, and see the progress steps, I put the DB code in a background thread. To my amazement, the UI still hangs (but not as much).
It's really no big deal with this app, but I thought that the case is interesting in general, that the DB code in the thread hangs the UI thread!
private void bgwDataAccess_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bgw = sender as BackgroundWorker;
try
{
bgw.ReportProgress(0, "Starting...\r\n");
bgw.ReportProgress(0, "Active ConnectionString:\r\n");
bgw.ReportProgress(0, Settings.Default.ConnctionString + "\r\n\r\n");
OracleConnection con = new OracleConnection(Settings.Default.ConnctionString);
OracleCommand cmd = new OracleCommand("SELECT Count(*) FROM MYTABLE", con);
bgw.ReportProgress(0, "Opening db...\r\n");
con.Open();
bgw.ReportProgress(0, "Opened.\r\n\r\n");
bgw.ReportProgress(0, "Executing SQL-query...\r\n");
Object result = cmd.ExecuteScalar();
bgw.ReportProgress(0, String.Format("Result: {0}\r\n\r\n", result.ToString()));
con.Close();
}
catch (Exception)
{
throw;
}
}
private void bgwDataAccess_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
txtResult.Text += e.UserState;
}
Do you access the Oracle namespace anywhere in your code before this? This is just a guess, but maybe that pause is your application loading the required .dlls
You could try pre-loading the module. I use something like the code below in my applications. First I display a splash screen to show the app is loading and then call the snippet below to load all of the required dlls. That way, once the app is loaded, there's no pauses further down the line.
void PreloadDLLs()
{
Assembly^ assembly = Assembly::GetEntryAssembly();
array<System::Reflection::AssemblyName^>^ referencedAssemblies = assembly->GetReferencedAssemblies();
for each(System::Reflection::AssemblyName^ referencedAssemblyName in referencedAssemblies)
{
try
{
Assembly^ a = assembly->Load(referencedAssemblyName);
}
catch(System::Exception^ /*e*/)
{
}
}
}
Apologies for the C++/CLI syntax, but hopefully you can see how to convert that to C# - mine's a little rusty :-)
[Edit] I think this is pretty much C#:
using System;
using System.Reflection;
private void PreloadDLLs()
{
Assembly assembly = Assembly.GetEntryAssembly();
System.Reflection.AssemblyName[] referencedAssemblies = assembly.GetReferencedAssemblies();
foreach(System.Reflection.AssemblyName referencedAssemblyName in referencedAssemblies)
{
try
{
Assembly a = assembly.Load(referencedAssemblyName);
}
catch
{
}
}
}
You can change query to "SELECT top 1 id FROM MYTABLE" and impact will be the same.
In case impact is not caused by those operations, you can use profiler to figure out which .net code causes impact.
*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.