Say that I have an ASP.NET page with a Label control and the following static class which executes a scheduled job:
public static class Job
{
// The Execute method is called by a scheduler and must therefore
// have this exact signature (i.e. it cannot take any paramters).
public static string Execute()
{
// Do work
}
}
When the job is done, the execute method should update the value of the Label control on the page.
I've done some research and the only way seems to be to use HttpContext.Current.CurrentHandler. However, this is undesirable for me since it can potentially return null.
Since the Execute method cannot take any parameters (see comment), passing the Page instance as an argument is not an option.
Is there any other way to update the control from the static class?
NOTE: the Execute method must be static because I'm creating an EPiServer scheduled job, which requires a static Execute method (that doesn't take any parameters).
If the job is not executed synchronously (or even if it is), I think that you may want to consider the order of control.
What I suggest in a case like this is a structure similar to the following:
1) The web page issues the request for the job
2) Somewhere, a unique reference to the job is created and stored (such as GUID or an identity column in a database table)
3) The job is executed asynchronously by code-behind and then the unique identifier is returned to the web page.
4) The web page, on startup, initiates a javascript method (using window.timeout, for example) that on a regular basis, issues an ajax query to the web server to check on the status of the job.
5) When the job is complete, it updates the global reference with the appropriate information.
6) When the javascript sees that the job is complete, it updates the label.
This process allows the user to carry on with other work if necessary and not have to worry about timeouts due to long postback times, etc.
For your specific scenario, you could add a GUID property to the Job class (which would be passed back to the client).
When Execute is complete, you could add this GUID to a static collection (i.e. Dictionary<Guid, string>) which the ajax request would check (the string value could store status or completion information).
When the ajax request fires, it would check this static collection and, when it finds its job, remove it and return the value to the caller.
You can create a static property that is updated by your Execute method and bind the Text property of the Label to the static property in the aspx's OnInit method, Label.Text = Job.StaticProperty, if you need a somewhat dynamic response you could use the Ajax Timer Control to call a method on the aspx page to return the same static value from the aspx Page.
public static class Job
{
public static string UpdatedValue { get; private set; } // Or whatever the property is you wish to expose.
// The Execute method is called by a scheduler and must therefore
// have this exact signature (i.e. it cannot take any paramters).
public static string Execute()
{
// Do work
Job.UpdatedValue = "Execute Completed";
}
}
protected override OnInit(EventArgs e)
{
base.OnInit(e);
this.TextLabel.Text = Job.UpdatedValue;
}
// Using MSDN basic sample
protected void Timer1_Tick(object sender, EventArgs e)
{
this.TextLabel.Text = Job.UpdatedValue;
}
Try with a global variable (static) or raise an event from Execute() method.
Related
I'm calling a number of methods that have been decorated with [WebMethod] via jQuery ajax.
These require a database connection to be set up in an external library that will be the same for each method.
My original code looked like this:
public partial class Server : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// code to set up DB connections
ExternalLibrary.SetupDB();
}
[WebMethod]
public static string AjaxAccessibleMethod()
{
try
{
// get some data from the database via the external library
ExternalLibrary.CallDatabase();
}
catch(Exception ex)
{
// handle errors
}
}
}
This was working, but then started throwing exceptions claiming that the ExternalLibrary's database hadn't been initialized.
Placing breakpoints in my code I found that the Page_Load event wasn't being called when calling my AjaxAccessibleMethod, I also tried moving the DB setup stuff into the Page_Init event but likewise that wasn't called.
Can anyone explain to me the aspx page life cycle when using WebMethods? The fact that this worked initially seems to imply that Page_Load was called, but it no longer is.
Notice that the method you are using as WebMethod is static, this should be the first hint to the fact that Page object is not created at all.
Page Methods is a simple alternative to full blown web services, and as such, its life cycle is more similar to web service than to page. That is, request goes through the general ASP.NET pipeline, with objects like HttpContext, Request and such. But then the difference happens: for page requests and postbacks page object is created and the whole series of page events happens, whereas for page methods page object is not created, and method is simply called as Server.AjaxAccessibleMethod().
There is really no way to mix the two, because this would unnecessarily complicate processing of calls to page methods. So the only path forward for you here is duplicate necessary code:
protected void Page_Load(object sender, EventArgs e)
{
// code to set up DB connections
ExternalLibrary.SetupDB();
}
[WebMethod]
public static string AjaxAccessibleMethod()
{
ExternalLibrary.SetupDB();
...
}
I am using C# 5.0, VS 2012, MVC4. I have a scenario where I need to cache employees data and query the cache when performing a search on employees info.
I am not displaying all employees initially but wanted to initiate a thread to cache all employees. So in index method when view is displayed, I am doing this
//Starting a thread to load the cache if its null
if (HttpRuntime.Cache["AllEmployees"] == null)
{
thCacheAllEmployees = new Thread(new ThreadStart(CacheAllEmployees));
thCacheAllEmployees.Name = "CacheAllEmployees";
thCacheAllEmployees.Start();
}
CacheAllEmployees is a separate method which will query LDAP and stores all employees in Cache. It will take about 15 secs for LDAP query. But within these first 15 secs after view is loaded and cache is not yet loaded, then when user starts typing in search box, I am making a ajax method call to GetFilteredEmployees action method. I want to access previously started thread, check if its alive, then to wait for that thread to complete so that I don't need to do a fresh LDAP query again.
if (thCacheAllEmployees.IsAlive)
{
thCacheAllEmployees.Join();
if (HttpRuntime.Cache["AllEmployees"] != null)
return (List<CMSUser>)HttpRuntime.Cache["AllEmployees"];
}
But the problem is, when its ajax call seems like it will be a new Main Thread and doesn't know about thCacheAllEmployees. So thCacheAllEmployees will be null object. So I need to get the instance of this thread from currently all active threads in application.
I can store thread id of thCacheAllEmployees when view is loaded first time in a session variable, but how can I access that thread from pool of threads when making ajax method call ?
Is there any better way of doing this ? please give ur suggestions.
When you think threads think actions not data. When you want to store data you do not put that data on a thread, you put it into memory, then that memory is accessible by one ore more threads depending on the scope.
There are lots of ways you can store that data. I'm not sure if the data to cache is unique for different user sessions or if you want just one global cache. Anything that you want to access from anywhere you can put in a static variable. You just have to be sure to use locks so that multiple threads do not try to access that data at the same time which is never safe.
Model
public static class MyCache
{
private static object LockToken = new object();
private static List<CMSUser> _Users { get; set; }
static MyCache()
{
_Users = GetUsers();
}
public static List<CMSUser> Users
{
lock (LockToken)
{
return _Users;
}
}
}
Controller
public class UsersController : ApiController
{
public List<CMSUser> Get()
{
return MyCache.Users;
}
}
View
$.ajax({
url: '/api/users',
dataType: 'json',
success: function(users) {
// do something with users here
}
});
Why wait for the first call to cache data? You could do this during the application start by adding it to the Application_Start function in Global.asax.
This will mean you will have a 15 sec overhead when your application starts, but after that you are good to go.
If you do want to use a thread here too, you could put its id in a static variable, and use that to check if the list has loaded.
Does anyone know if it's possible to cancel output caching in code? What I mean is if I place output caching on a child action as follows can I then based on a condition cancel that caching from inside the child action?
[ChildActionOnly]
[OutputCache(Duration = 36000, VaryByParam="tagslug")]
public virtual ActionResult MostViewed(string tagslug, int count)
{
// Make an API call here. If not data returned do not cache the ChildAction as specified above
}
Skimming the framework source it looks like the only logic is don't-cache-on-exception:
// Only cache output if this wasn't an error
if (!wasException) {
ChildActionCacheInternal.Add(uniqueId, capturedText,
DateTimeOffset.UtcNow.AddSeconds(Duration));
}
I can't see a brilliant way to solve this: I think you'll have to make your own custom OutputCachingAttribute based on the original source from the ASP.NET MVC source from CodePlex, and then either add an extra check to that line for the output returned e.g.
if (!(wasException || capturedText.Contains("results:0"))) {
or similar, or find a way to pass that code a flag to this from your controller. The existing code uses an object to store a value on the session; you could copy this, e.g.
define a new static object key the same as _childActionFilterFinishCallbackKey e.g. _noCacheResultKey
add a public static method to the attribute that you can call e.g.
public static void FlagNoCache(HttpContext httpContext) {
httpContext.Items[_noCacheResultKey] = true;
}
extend ClearChildActionFilterFinishCallback to remove this from .Items[] as well as the callback
extend the above test to check this too e.g.
if (!(wasException
|| filterContext.HttpContext.Items.ContainsKey(_noCacheResultKey))) {
from your controller call MyOutputCacheAttribute.FlagNoCache(Context); as necessary.
It may also be possible to throw an exception from your code and then catch it in a different IExceptionFilter so that it doesn't get passed up beyond the OutputCacheAttribute but I don't know how sorry.
I'm trying to do something that I thought would be easy but can't figure out how to write to a label inside my stating function.
public static void StartProcessing(object data)
{
lblError.Text = "Blah Blah"
}
I get the error "An object reference is required for the non-static field, method, or property..."
So I tried creating a new instance of the label and adding it to a new instance of a control (Panel) but the lblError isn't getting displayed
public static void StartProcessing(object data)
{
Panel Panel1 = new Panel();
Label lblError= new Label();
Panel1.Controls.Add(lblError);
lblError.Visible = true;
lblError.Text = "Blah Blah";
}
there must be an easy way to do this? Which i've overlooked..
The function is getting called as follows: If I change the above to not be static I get an error message on the second line below saying the same "An object reference is required for the non-static field, method, or property..." When this function isn't static?
public object LaunchNewProcess(object data)
{
ThreadPool.QueueUserWorkItem(
new WaitCallback(ProcessStatuses.StartProcessing),
new object[] {newProcess, allProcesses}
);
Pass the label to your static function when you call it:
public static void StartProcessing(object data, Label lblError)
{
lblError.Text = "Blah Blah"
}
Static functions don't have access to controls because the controls belong to the instance of the page (class).
Static means that all instances of a class share the same function or variable. So, an instance of a class has access to a static variable or function. However, since a static is not "aware" of any instances of the class, it cannot access the members of an instance. In fact, a static method or variable does not even require any instance of the class to exist, so how could it?
Removing the static key word from your function will also work, as others have mentioned, but I'm assuming you made it static for a reason.
EDIT
Alright, this is more complex.
So, you have some class that launches a bunch of threads, and you want it to display to the user if something went wrong? Well, your current approach is flawed because you cannot access controls of a page without the instance of the page. Also, I am not sure how this approach would fit within the page lifecycle.
Your best approach (sorry for lack of code, it's going to depend a lot on your implementation) could be something like this:
//do this before you start spawning threads
List<bool> successes = new List<bool>();
ThreadPool.QueueUserWorkItem(
new WaitCallback(ProcessStatuses.StartProcessing),
new object[] {newProcess, allProcesses, successes}
);
//you MUST wait for all your threads to complete before proceeding!
if(successes.Any(s => !s))
{
//update your error label
}
public static void StartProcessing(object data, Label lblError)
{
var dataArray = (object[3]) data;
//if there is an error
dataArray[2] = false;
}
What you want to do is actually quite difficult.
You want to create a page, start an asynchronous task, send the page to the user, and then update content on the page after the asynchronous job finishes.
The problem is that by the time the asynchronous task finishes the page has already been sent, and based on the way HTTP works once you've sent your response you're done; there's not more communicating with the client for you. You need to wait for the client to send another request if you want to update them.
This means that you need to have JavaScript code that is constantly polling the server basically asking, "Are you done yet, are you done yet, are you done yet?" until eventually the server says, "Yes, here's something to display on the page".
Fortunately, you don't need to start from scratch. Here you'll find an example by Microsoft that does all of this; you can modify it to suit your needs. It's also worth mentioning that in addition to being non-trivial to program, it also consumes a lot of resources to constantly poll the server, so be sure you really need to do this.
Option 2 is to just not start the other tasks in new threads, and execute the code serially before the page is ever returned to the user. They'll be staring at a blank screen for a while, but it'll be MUCH easier to program. The one downsize to keep an eye on here is on timeouts if the task is REALLY long running.
I have function along these lines:
public void view(string msg)
{
messagebox.show(msg);
}
.
.
I want to pass arguments to this it from a thread.. I'm using .Net 1.1. How can I do this?
For .NET 1.1 there is no direct way but to use an object having both the method and the state, in this example (modified from the MSDN sample), the ThreadState class is instanced and passed the desired state, then its method is called and made to use the passed state.
public class ThreadState {
private string msg;
// The constructor obtains the state information.
public ThreadState(string msg) {
this.msg = msg;
}
public void view() {
//Do something with msg
}
}
public class Example {
public static void Main() {
ThreadState ts = new ThreadState("Hello World!");
// Create a thread to execute the task, and then
// start the thread.
Thread t = new Thread(new ThreadStart(ts.view));
t.Start();
t.Join();
}
}
For .NET > 1.1 (original question didn't state version.)
You pass the argument in the Start method. You will receive an object which you need to cast back to the proper type.
Thread t = new Thread(view);
t.Start("Hello");
public void view(object msg)
{
string m = (string)msg;
//Use msg
}
Either that, or use a ParameterizedThreadStart delegate.
The ParamaterizedThreadStart delegate is not present in .NET 1.1 (2003) nor does thread.Start take an Object param in 1.1. However you could try something like:
dict[mythread.name] = myval; //dict is a class scoped variable.
mythread.Start();
There are various ways to do that. One is to use the ParameterizedThreadStart delegate, which allows passing a single parameter of type Object to the thread. Change your view method to accept Object, then cast Object to String inside the method.
Create a class, set your properties as arguments and run related function of the class.
Dim run = New Runner()
run.mile = 20
run.pace = "slow"
Thr.Start(run.Process())
Or you need to use a global scope variable. Unfortunately no other way for .NET 1.1
The first code snippet provided by Vinko is exactly what you need under .NET 1.x as the original Threading namespace offered no in-built mechanism for parameterizing _beginthread.
Thus, you create a state class which contains the data you need, as well as the method which will be used for the delegate in the "new Thread()" statement, as Vinko has shown.
I was about to tap out a sample, and then I saw his sample code. If it's "not working" it's worth being explicit as to why, because by the naked eye that code looks all too familiar.
One "spin" on this is to properly encapsulate the thread construction as well as the thread start/join behavior.
Another option is to use ThreadPool.QueueUserWorkItem(), which isn't quite the same and can result in poor ThreadPool performance if over-used.
Another option is to create a thread that is blocked on a signal, e.g. ManualResetEvent, which is set when a data member has been initialized. The target data member can be as simple as an object reference as shown above, or you could use an ArrayList, Queue or Stack object (with proper locking) to implement a sort of "queue processor" where work items can be enqueued and processed by a dedicated thread, and where the dedicated thread remains dormant (blocked waiting for a signal) until there is work available.