I'm using Serenity C# to develop a website.
When I click on a button it should run a SQL command, which starts a stored procedure.
My code
public ListResponse<MyRow> RunSQL(IDbConnection connection, ListRequest request)
{
string sql = "EXEC SP_A #Username='" + Authorization.UserDefinition.Username + "'";
SqlHelper.ExecuteNonQuery(connection, sql);
return new MyRepository().List(connection, request);
}
This code works fine, but it makes my web slow because my web needs to wait for the query to finish.
I want to kick off the SQL command and not wait for the result. Can I use the Task Parallel Library (TPL) for this?
Can I use TPL (Task Parallel Library)??
No, you can not. You execute ONE statement, I am not even sure where you get the idea that paralellism of one item will do anything. if that query takes a long time, analyze whether it is defective. if it is not defective...
...change the API to be async an return a come back later with a token. It is waiting for the return value because you degiend the API to be synchroneous . you this is not acceptable, then the API is a design error and the design at least of this method should change.
Nothing in async/await/paralellism will change the API design and it will not magically make the request finish faster.
Related
I have a website on Rackspace which does calculation, the calculation can take anywhere from 30 seconds to several minutes. Originally I implemented this with SignalR but had to yank it due to excessive CC usage. Hosted Rackspace sites are really not designed for that kind of use. The Bill went though the roof.
The basic code is as below which work perfectly on my test server but of course gets a timeout error on Rackspace if the calculation take more than 30 seconds due to their watcher killing it. (old code) I have been told that the operation must write to the stream to keep it alive. In the days of old I would have started a thread and polled the site until the thread was done. If there is a better way I would prefer to take it.
It seems that with .NET 4.5 I can use the HttpTaskAsyncHandler to accomplish this. But I'm not getting it. The (new code) below is as I understand the handler you would use by taking the old code in the using and placing it in the ProcessRequestAsync task. When I attempt to call the CalcHandler / Calc I get a 404 error which most likely has to do with routing. I was trying to follow this link but could not get it to work either. The add name is "myHandler" but the example link is "feed", how did we get from one to the other. They mentioned they created a class library but can the code be in the same project as the current code, how?
http://codewala.net/2012/04/30/asynchronous-httphandlers-with-asp-net-4-5/
As a side note, will the HttpTaskAsyncHandler allow me to keep the request alive until it is completed if it takes several minutes? Basically should I use something else for what I am trying to accomplish.
Old code
[Authorize]
[AsyncTimeout(5000)] // does not do anything on RackSpace
public async Task<JsonResult> Calculate(DataModel data)
{
try
{
using (var db = new ApplicationDbContext())
{
var result = await CalcualteResult(data);
return Json(result, JsonRequestBehavior.AllowGet);
}
}
catch (Exception ex)
{
LcDataLink.ProcessError(ex);
}
return Json(null, JsonRequestBehavior.AllowGet);
}
new code
public class CalcHandler : HttpTaskAsyncHandler
{
public override System.Threading.Tasks.Task ProcessRequestAsync(HttpContext context)
{
Console.WriteLine("test");
return new Task(() => System.Threading.Thread.Sleep(5000));
}
}
It's not a best approach. Usually you need to create a separate process ("worker role" in Azure).
This process will handle long-time operations and save result to the database. With SignalR (or by calling api method every 20 seconds) you will update the status of this operation on client side (your browser).
If this process takes too much time to calculate, your server will become potentially vulnerable to DDoS attacks.
Moreover, it depends on configuration, but long-running operations could be killed by the server itself. By default, if I'm not mistaken, after 30 minutes of execution.
I am attempting to call/push a semi-large tiff and a Gal file to a java webservice.
The platform is Visual Studio 2013, C# windows forms application.
I am pointing to the WSDL file and "The Platform" is generating a service reference class for me.
This is all very abstracted from me, which is a good thing as I am a relative newbie to this arena.
I left the "Generate Task based Code" checked and I get an addSample and addSampleAsync method.
I populate the class fields and push the code up.
The addSample code works fine but blocks the UI.
The async code, addSampleAsync, also works, bit is slower and is not completely asynchronous.
addSampleAsync locks the UI for about half of the processing time and the function call to fncTestUpload does not return for that same period of time.
//Dimensioned at class level
//private static addSamplePortClient Service = new addSamplePortClient();
//private static addSampleResponse Myresult = new addSampleResponse();
//ThisRequest is the WSDL modeled class object.
//This code works, but is slow, 30 seconds on wifi
ResponseType Myresult = Service.addSample(ThisRequest.Request);
MessageBox.Show(Myresult.Message + Myresult.Code);
//This code locks up the UI for about 15 - 20 seconds then takes another 15 to display the messagebox
fncTestUpload(ThisRequest);
async void fncTestUpload(addSampleRequest SentRequest)
{
Myresult = await Service.addSampleAsync(SentRequest.Request);
MessageBox.Show(Myresult.Response.Message + " - " + Myresult.Response.Code);
}
I made the response object a class level variable in hopes of doing something with it in the function that calls fncTestUpload, which it thought would return immediately when calling an Async function. It does not return until after 15 seconds.??
I have spent several hours googling this and have not found any answers as to why the addSampleAsync is not working as advertised.
Microsoft's tutorials may as well be written in Dilbert's Elbonian. I can't follow them and don't find them helpful, so please don't direct me to one.
When you use the 'await' keyword in your method you are saying "Ok, you go ahead and do work, I will return to my caller, let me know when you're done".
So the 15 seconds of waiting is the time it takes your service to process the request, then invoking the state machine generated by the async method to return to the method after the previously awaited method has finished. That is the normal behavior for await.
About the MessageBox that is taking 15 seconds, it could be that the Response property is lazyloading and actually trying to load the code / message for the first time wheb you access those properties.
I just tried to make a part of my ASP .NET MVC Application asynchronous but even after reading and trying out a lot I don't really understand the async-await pattern and hope someone could give me a hint.
Basically I have the following:
A javascript call to my controller which fetches a partial View for a chart (this happens several times after page load for a lot of charts)
// Load content of one chart
my.loadChartContent = function (data, callback) {
$.post("/Dashboard/GetChartContent/", data, function (datain) {
if (isFunction(callback))
callback(datain);
});
};
A controller action which calls a database method in another class
public ActionResult GetChartContent(int id, bool isDraft = false)
{
//do something
//...
var chartdata = _dataService.GetDataForChart(chart, isDraft, _user.Id); //long running query
//do something with chartdata
return View(chartdata);
}
The data class (_dataService) which fetches the data from the database with a SqlDataReader and loads a DataTable with that data.
The problem is that although the javascript is executed asynchronously the Controller-Actions seems to be blocked until a result from the DataService class returns. I would like to start all queries to the database and wait for the results asynchronously, so that long-running queries don't block shorter ones. (In SQL Server Profiler I see the queries as Begin-End, Begin-End, Begin-End => but it should be begin-begin-begin - end-end-end)
Where should I use async-await? Is it enough to use it (somehow) for my controller action or is it necessary to make the whole "call-chain" asynchronous?
Update:
When I use SQLConnection.OpenAsync and ExecuteReaderAsync the code never finishes...and I don't get why?
public async Task<Query> GetSqlServerData(Query query)
{
var dt = new DataTable();
var con = new SqlConnection(query.ConnectionString);
await con.OpenAsync();
var cmd = new SqlCommand(query.SelectStatement, con);
var datareader = await cmd.ExecuteReaderAsync();
dt.Load(datareader);
con.Close();
query.Result = dt;
return query;
}
Thank you all for your answers. But the real answer is way simpler than I thought. Stephen pointed me in the right direction with the sentence
All HTTP calls are naturally asynchronous.
I knew that and generally that's true but obviously not when you're using sessions because ASP.NET MVC waits for each request (from one user) to be completed to synchronize the session. You can find a better explanation here: http://www.stefanprodan.eu/2012/02/parallel-processing-of-concurrent-ajax-requests-in-asp-net-mvc/
So - just decorating my controller with ...
[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
...did it and now I have the result I wanted - simultaneous queries on my SQL Server and a lot faster response time.
#Stephen - it's more than 2 simultaneous requests => http://www.browserscope.org/?category=network
The problem is that although the javascript is executed asynchronously the Controller-Actions seems to be blocked until a result from the DataService class returns. I would like to start all queries to the database and wait for the results asynchronously, so that long-running queries don't block shorter ones.
The term "asynchronous" can be applied a few different ways. In JavaScript, everything is asynchronous. All HTTP calls are naturally asynchronous.
If you're not seeing any sql overlapping, then you should be sure that you're calling loadChartContent multiple times (not calling it once at a time chained through callbacks or anything like that). The browser will limit you to two simultaneous requests, but you should see two requests at a time hitting your sql server.
Making your server side async won't help you, because async doesn't change the HTTP protocol (as I describe on my blog). Even if you make your database access async, your controller action will still have to wait for them to complete, and the browser will still limit you to two outstanding requests. Server-side async is useful for scaling your web servers, but if your web server is talking to a single SQL Server backend, then there's no point in scaling your web server because your web server is not the determining factor in your scalability.
On a side note, I suspect the reason your async code never finishes is because you're using Result or Wait; I explain this on my blog.
So, back to your original problem: if you want to start all the queries to the database, then you'll need to change API to be "chunky" instead of "chatty". I.e., add a GetChartContents action which takes multiple ids and runs all of those queries in parallel. I'd recommend using async database methods with something like this:
public async Task<ActionResult> GetChartContents(int[] ids, bool isDraft = false)
{
var charts = ...;
var chartTasks = Enumerable.Range(0, ids.Length)
.Select(i => _dataService.GetDataForChartAsync(charts[i], isDraft, _user.Id))
.ToArray();
var results = await Task.WhenAll(chartTasks);
...
return View(results);
}
I think this tutorial gives a pretty good starting point for what you're trying to do.
http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4
If you have:
public async Task<Query> GetSqlServerData(Query query)
{
var dt = new DataTable();
var con = new SqlConnection(query.ConnectionString);
await con.OpenAsync();
var cmd = new SqlCommand(query.SelectStatement, con);
var datareader = await cmd.ExecuteReaderAsync();
dt.Load(datareader);
con.Close();
query.Result = dt;
return query;
}
Then use:
query1 = await GetSqlServerData(query1);
query2 = await GetSqlServerData(query2);
query3 = await GetSqlServerData(query3);
To make this easier to understand: We are using a database that does not have connection pooling built in. We are implementing our own connection pooler.
Ok so the title probably did not give the best description. Let me first Describe what I am trying to do. We have a WCF Service (hosted in a windows service) that needs to be able to take/process multiple requests at once. The WCF service will take the request and try to talk to (say) 10 available database connections. These database connections are all tracked by the WCF service and when processing are set to busy. If a request comes in and the WCF tries to talk to one of the 10 database connections and all of them are set to busy we would like the WCF service to wait for and return the response when it becomes available.
We have tried a few different things. For example we could have while loop (yuck)
[OperationContract(AsyncPattern=true)]
ExecuteProgram(string clientId, string program, string[] args)
{
string requestId = DbManager.RegisterRequest(clientId, program, args);
string response = null;
while(response == null)
{
response = DbManager.GetResponseForRequestId(requestId);
}
return response;
}
Basically the DbManager would track requests and responses. Each request would call the DbManager which would assign a request id. When a database connection is available it would assign (say) Responses[requestId] = [the database reponse]. The request would constantly ask the DbManager if it had a response and when it did the request could return it.
This has problems all over the place. We could possibly have multiple threads stuck in while loops for who knows how long. That would be terrible for performance and CPU usage. (To say the least)
We have also looked into trying this with events / listeners. I don't know how this would be accomplished so the code below is more of how we envisioned it working.
[OperationContract(AsyncPattern=true)]
ExecuteProgram(string clientId, string program, string[] args)
{
// register an event
// listen for that event
// when that event is called return its value
}
We have also looked into the DbManager having a queue or using things like Pulse/Monitor.Wait (which we are unfamiliar with).
So, the question is: How can we have an async WCF Operation that returns when it is able to?
WCF supports the async/await keywords in .net 4.5 http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx. You would need to do a bit of refactoring to make your ExecuteProgram async and make your DbManager request operation awaitable.
If you need your DbManager to manage the completion of these tasks as results become available for given clientIds, you can map each clientId to a TaskCompletionSource. The TaskCompletionSource can be used to create a Task and the DbManager can use the TaskCompletionSource to set the results.
This should work, with a properly-implemented async method to call:
[OperationContract]
string ExecuteProgram(string clientId, string program, string[] args)
{
Task<string> task = DbManager.DoRequestAsync(clientId, program, args);
return task.Result;
}
Are you manually managing the 10 DB connections? It sounds like you've re-implemented database connection pooling. Perhaps you should be using the connection pooling built-in to your DB server or driver.
If you only have a single database server (which I suspect is likely), then just use a BlockingCollection for your pool.
I would like to give a user the ability to cancel a running query. The query is really slow. (Query optimization is besides the point.) This is mainly out of my curiosity.
MSDN says:
If there is nothing to cancel, nothing occurs. However, if there is a
command in process, and the attempt to cancel fails, no exception is
generated.
Cmd - SqlCommand
DA - DataAdapter
Conn - SqlConnection
CurrentSearch - Thread
LongQuery - Singleton
Here's what I have:
var t = new Thread(AbortThread);
t.Start();
void AbortThread()
{
LongQuery.Current.Cmd.Cancel();
LongQuery.Current.Cmd.Dispose();
LongQuery.Current.DA.Dispose();
LongQuery.Current.Conn.Close();
LongQuery.Current.Conn.Dispose();
LongQuery.Current.Cmd = null;
LongQuery.Current.DA = null;
LongQuery.Current.Conn = null;
CurrentSearch.Abort();
CurrentSearch.Join();
CurrentSearch = null;
}
I noticed that CurrentSearch.Abort() was blocking, that's why I wrapped it in a thread, which probably means that the thread is still working.
Finally, is there anything else than this that I can do to cancel a query? Is it actually possible to cancel such a long query from .NET?
IF you really absolutely want to kill it for good use this approach:
store away the session ID right before starting the long-running query by calling SELECT ##SPID AS 'SESSIONID' on the same connection
When you want to kill it:
Open a new DB connection
issue a KILL command for that session ID
BEWARE as the MSDN documentation states you need the permission ALTER ANY CONNECTION to do this
Yes, you can kill a process from .NET. Here is an example. Please note you will need proper permissions and you have to figure out the process in question. I don't have a quick sample of determining which process your query is running under.
You example aborts the thread, but that does not mean the work on SQL Server was terminated. If you think about it this way: when you go through a bad cell zone and the call drops, if you mom/wife/friend was droning on, do they instantly stop talking? That is an analogy of aborting the thread, at least in the case of working with a database server.