Hooking up async method to event handler with ASMX - asmx

I have a standard asmx webmethod, that when referenced creates the following method in the client proxy:
msr.SendAndReceiveAsync("Hello");
This is just an example, any method you generate with asmx automatically gets the Async method included.
I can call this method fine, what I can't seem to get right is hook up the event handler.
Best examples I can find recommend this:
msr.SendAndReceiveCompleted += new EventHandler<AsyncCompletedEventArgs>(msr_complete);
}
private void msr_complete(object sender, AsyncCompletedEventArgs e)
{
}
This does not compile. What am I doing wrong?

The web reference should have generated a completed event handler similar to this for you to use:
msr.SendAndReceiveCompleted += new SendAndReceiveCompletedEventHandler(msr_complete);

Related

Call async method from ASP.NET validator handler

I have red how to work with async method in ASP.NET application example Async-ASP.NET.
Now I need to call web serivce in CustomValidator on ServerValidate event and by the service result decide, if value is valid or not. Service method are asynchronous and I am not sure, how to handle this. If I use async void event handler, validation does not work properly, because validation message doesn't show up in case validation is not successful. It seems like await does not await for result of web service nor ASP.NET doesn't await the validator to send response.
What is the proper way to solve this? Or how to use RegisterAsyncTask in validator event handler? Page is set to Async="true".
Piece of my code:
protected async void cvWorkplace_ServerValidate(object sender, ServerValidateEventArgs e)
{
// do test call to sap
var testResult = await PerformLabelServiceTestCallAsync(materialId, ddPlant.SelectedValue, workplaceName, UsersLanguage);
if (testResult.HasError)
{
e.IsValid = false;
// other stuff
// ...
}
else
{
e.IsValid = true;
}
}
This will immediately return and the calling code will be evaluating e.IsValid before it is set by cvWorkplace_ServerValidate.
RegisterAsyncTask is a method of the Page class, which is the base class of your page and you can call it from the cvWorkplace_ServerValidate method, which is a method of your page.
But, even if you register an asynchronous task, you won't get the result in the cvWorkplace_ServerValidate method.
The best way would be to register an asynchronous task that would set a field in your page class and render accordingly.

Quartz.Net ASP.NET Status update

I'm using quartz in an asp.net site and I'm using it to fire off a longer background task. I want to be able to use a timer or jquery to call the job and get back status and progress info similar to how I was doing it before. Here is code that I was using:
protected void Timer1_Tick(object sender, EventArgs e)
{
if (MyTaskManager.Instance.IsTaskRunning)
{
if (MyTaskManager.Instance.TotalItems > 0) ProgressLabel.Text = string.Format(ProgressLabel.Text, MyTaskManager.Instance.TotalItems, MyTaskManager.Instance.ItemsProcessed);
else ProgressLabel.Text = string.Format("Records Processed: {0}", MyTaskManager.Instance.ItemsProcessed);
}
else
{
Timer1.Enabled = false;
}
}
Has anyone done this that could point me in the right direction?
It might not be what you're looking for but I've used SignalR with Quartz.Net and it works great.
I've published a simple application in my repository.
You have to create a custom Hub which you will use to interact with your ASP.NET page.
Your (quartz.net) job will interact your ASP.NET page through your Hub the same way.
Once you have installed ASP.NET SignalR:
Install-Package Microsoft.AspNet.SignalR
You can create a startup class to configure SignalR:
[assembly: OwinStartup(typeof(AspNetQuartSignalR.Startup))]
namespace AspNetQuartSignalR
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}
You have to reference a couple of scripts in your page:
jquery-2.1.1.js
jquery.signalR-2.1.1.js
and the automatically generated /signalr/hubs
Now you can create your own Hub:
public class QuartzHub : Hub
{
...
}
with methods which will allow you to interact with the scripts in your ASP.NET page.
Let's say your Hub has a method CheckQuartzStatus which gives you the status of all the quartz.net triggers configured:
public void CheckQuartzStatus()
{
string message = string.Empty;
var allTriggerKeys = Global.Scheduler.GetTriggerKeys(GroupMatcher<TriggerKey>.AnyGroup());
foreach (var triggerKey in allTriggerKeys)
{
ITrigger trigger = Global.Scheduler.GetTrigger(triggerKey);
message += string.Format("{0} = {1}", trigger.Key, Global.Scheduler.GetTriggerState(trigger.Key)) + Environment.NewLine;
}
Clients.All.onCheckQuartzStatus(message);
}
Your jQuery script can interact with this method in a very simple way:
quartz.server.checkQuartzStatus();
As you can see your Hub method at some point fires an action: onCheckQuartzStatus.
That is a call to an event defined in your javascript defined in the page :
quartz.client.onCheckQuartzStatus = function (message) {
alert(message);
};
You can see how the interaction works looking at the script in the Default.aspx page.
You can read a lot more here.
You're going to have to build all of this functionality yourself. Quartz.Net jobs run in the threadpool and you don't have a way of referencing them from the scheduler. You could do this by having the job write its progress somewhere and then have your timer check that spot for progress.

IHttpModule is not being called for my WebMethod

Ok, so I have an existing application to which I have added a custom HttpModule. I'm registering two events in the Init() method (PreRequestHandlerExecute and PostRequestHandlerExecute). The HttpModule gets called for every 'normal' request. But not I have created an .aspx containing a few WebMethods that are being called for ajaxifying some UI components. The WebMethod gets called nicely, but the trouble is that my HttpModule does NOT get called at all (no events, no init, even no constructor) when accessing the WebMethod. The module gets called nicely when accessing the .aspx in question as a 'normal' request. But it refuses to be called when calling the WebMethod.
My .aspx looks like this:
public partial class SelectionListService : System.Web.UI.Page
{
[WebMethod]
[ScriptMethod]
public static RadComboBoxData GetItemsAsRadComboBoxData(RadComboBoxContext context)
{
...
}
}
My HttpModule look like this:
public class MyModule : IHttpModule, IRequiresSessionState
{
public MyModule ()
{
}
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += new EventHandler(Application_PreRequestHandlerExecute);
context.PostRequestHandlerExecute += new EventHandler(Application_PostRequestHandlerExecute);
}
private void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
...
}
private void Application_PostRequestHandlerExecute(object sender, EventArgs e)
{
...
}
}
I have been digging into this for quite some time now, but I just can't get it to work. Any ideas?
PS1: the BeginRequest, etc in global.asax.cs do get called when accessing the WebMethod.
PS2: I'm running IIS7 on Windows7.
since PageMethods must be static, an instance of the Page class with all it's events and the ASP.NET pipeline never happens. You simply get the result of your PageMethod call, and that is all.
I have a project that had the same problem. We found that the first event in the pipeline that we could get to fire for the WebMethods was the AcquireRequestState event. We hooked into that with our HttpModule in order to do the authorization checking required for the application.
I don't know what your pre and post request handlers do, but maybe you could shift some of the logic into the AcquireRequestState event handler.

How can I utilize or mimic Application OnStart in an HttpModule?

We are trying to remove the global.asax from our many web applications in favor of HttpModules that are in a common code base. This works really well for many application events such as BeginRequest and PostAuthentication, but there is no Application Start event exposed in the HttpModule.
I can think of a couple of smelly ways to overcome this deficit. For example, I can probably do this:
protected virtual void BeginRequest(object sender, EventArgs e)
{
Log.Debug("Entered BeginRequest...");
var app = HttpContext.Current.Application;
var hasBeenSet app["HasBeenExecuted"] == null ? false : true;
if(!hasBeenSet)
{
app.Lock();
// ... do app level code
app.Add("HasBeenExecuted", true);
app.Unlock();
}
// do regular begin request stuff ...
}
But this just doesn't smell well to me.
What is the best way to invoke some application begin logic without having a global.asax?
Just keep a static bool in the HttpModule:
private static bool _hasApplicationStarted = false;
private static object _locker = new object();
private void EnsureStarted()
{
if (_hasApplicationStarted) return;
lock (_locker)
{
if (_hasApplicationStarted) return;
// perform application startup here
_hasApplicationStarted = true;
}
}
Then have any method that needs the application to have started just call EnsureStarted.
HttpModules and HttpHandlers will execute on every single request, while the Global.asax App Start event is when the application starts, thus only once.
You could make a general global.asax which will load all assemblies with a specific interface, and then drop in the dll's you want executed for that specific application. Or even register them in your web.config, and have your general global.asax read the keys, and then load and execute the code you want.
I think this is better than putting app once code in a module and checking on a state variable.

How can I hook into the current FormsAuthenticationModule in a Medium Trust environment?

I've got an HttpModule in my application that hooks into the FormsAuthenticationModule's Authenticate event with the following code:
public void Init(HttpApplication context)
{
FormsAuthenticationModule faModule =
(FormsAuthenticationModule)context.Modules["FormsAuthentication"];
faModule.Authenticate +=
new FormsAuthenticationEventHandler(faModule_Authenticate);
}
Unfortunately, the call to context.Modules fails because the app needs to run in a medium-trust environment. Is there another way that I can hook into this event?
That's a tough one - you can't even access the Modules collection from within your Global application file.
You could try calling your custom code from the AuthenticateRequest handler in Global:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
// Call your module's code here..
}
You can't grab your custom module from the collection, either, so you'd need a static reference to your module's library.
Other than granting the AspNetHostingPermission (as detailed for other permissions here) to your site in the machine level web.config, I'm out of ideas!

Resources