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();
...
}
Related
I have over 30 aspx pages, i have discovered a problem recently that if i did any kind of database transaction like insert, update, delete and then after the transaction is complete i pressed F5 or refreshed the page in anyway the same is transaction occur.
I searched for a solution but all i could found is that i have to check for viewstate on each button which is impossible, cause that means there will be a lot of work. There got to be generic solution, please help me in this problem.
Edit:
Here is the code on one of the buttons which change a value in data base to either true or false:
protected void btn_Publish_Click(object sender, EventArgs e)
{
if (Convert.ToBoolean(int.Parse(hf_Published.Value.ToString())))
{
publish(false);
}
else
{
publish(true);
}
}
After the execution of the code if refreshed the page the same code is executed, i noticed that since a break point was placed on this method.
You can try this. I used this in several project and working successfully.
public bool IsRefreshed
{
get
{
if (Convert.ToString(Session["RefreshTimeStamp"]) == Convert.ToString(ViewState["RefreshTimeStamp"]))
{
Session["RefreshTimeStamp"] = HttpContext.Current.Server.UrlDecode(System.DateTime.Now.ToString());
return false;
}
else
{
return true;
}
}
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
ViewState["RefreshTimeStamp"] = Session["RefreshTimeStamp"];
}
protected override void OnLoad(EventArgs e)
{
if (!Page.IsPostBack)
{
Session["RefreshTimeStamp"] = HttpContext.Current.Server.UrlDecode(System.DateTime.Now.ToString());
}
base.OnLoad(e);
}
There IS a generic solution, used for years by thousands of developers.
And the solution is: each time you perform an intrusive process at the server (insert/update/delete) you don't just render the page but rather you redirect the response with 302 to a fresh page with "your transaction succeeded" message.
This way, pressing the F5 will just refresh this message page, not the original page which triggers the transaction.
It is up to you whether or not this is directly applicable in your scenario.
http://en.wikipedia.org/wiki/Post/Redirect/Get
To fix this issue you could check the following:
Disable the submit button when necessary
Add some validation to your code and check for double entries
Redirect the user to another page after submit
Unless it's a real 'transaction' like payments etc. (which others already explained, do redirect etc.),
You could also try defining caching on your pages that interact with the Db or are bottlenecks for your app.
If you wanna have always live info (and it's that 'alive' type of app) then no luck with that solution (but even then), but usually, you can put some reasonable time expiration on how 'fresh' you want your data to be.
Caching ASP.NET Pages
Okay.
So basically i am working on a message system on a webpage.
Users on my webpage is able to send each other messages, but now i want the messages to "pop up" on the receivers screen when sent. Exactly like when somebody on facebook sends you a message while your online, the message thing goes red. To solve my problem i need every client to know which other clients are online at the moment. I have solved this by coding an Observer-like pattern in my Global.asax:
public static void AddObserver(Observer o)
{
if(!observers.Contains(o))
observers.Add(o);
System.Diagnostics.Debug.WriteLine("Observer tilføjet : " + observers.Count);
}
public static void RemoveObserver(Observer o)
{
if (observers.Contains(o))
observers.Remove(o);
System.Diagnostics.Debug.WriteLine("Observer fjernet : " + observers.Count);
}
public static void NotifyObserversNewMail(Observer observer)
{
foreach (Observer o in observers)
if(!o.Equals(observer))
o.UpdateNewMail();
}
And the observer in this case i simply the Site.Master, which i have made extend the Observer class :
public partial class SiteMaster : System.Web.UI.MasterPage, Observer
{
protected void Page_Unload(object sender, EventArgs e)
{
Session["observer"] = this;
Global.AddObserver(this);
}
protected void Page_Load(object sender, EventArgs e)
{
//ADD OBSERVER TO GLOBAL.ASAX
if (Session["observer"] != null)
Global.RemoveObserver((Observer)Session["observer"]);
public void Update()
{
DLMessages.DataSource = ServiceMessages.GetInstance().GetMessages();
DLMessages.DataBind();
UPMessages.Update();
}
Where DLMessages is a DataList inside the UpdatePanel UPMessages.
So we have a "sender" client, and a "receiver" client.
When the sender creates a new message this method gets called:
protected void MessageSend(object source, EventArgs args)
{
Page.Validate("ValGroupMessageTo");
if (Page.IsValid)
{
ServiceMessages.GetInstance().SendMessage(ServiceCommunity.GetInstance().GetUser(MessageTo.Text).Id, ((User)Session["user"]).Id, MessageMessage.Text);
Global.NotifyObserversNewMail((Observer)Session["observer"]);
ClosePopups(new object(), new EventArgs());
Update();
}
}
As you can might notice it calls the Notify on global.asax, and the update() directly on itself. The UpdatePanel on the "sender" side updates perfectly, but on the receiver side nothing happens. Not in the UpdatePanel anyways.
Cause if i alter the code in the Update() to run through the messages from the DB, i can see that the message gets called fine, and the new message is loaded. Just not updated to the UpdatePanel.
So i have been thinking a lot about why the updatepanel doesnt get updated on the "receiver" side when the data gets updated, and my conclusion is it is because theres no partial postback on the "receiver" side. Yeah sure, the Update() method gets called, but theres no postback. So my question is this:
Is it possible to "force" a partial post back from the code-behind? Or is there another solution that might work better?
Hope it makes sense :-)
Do yourself a favour and build the whole thing using SignalR. It is a library for real time communication between the server and the browser for .NET (includes client-side libraries).
If you insist on doing it the hard way you should use a timer to trigger the update panel update
I'm not a big fan of working with updatepanels, but I think you can use the timer-control to force a partial postback.
Have a look at: http://mattberseth.com/blog/2007/08/using_the_ajax_timer_control_a.html
Those solutions, comet etc will work but really your best bet is to use socket.io
Real Time Browser Applications
You could also combined it with backbone.js and have a really nice app. I helped make a real time web messenger based on these 2.
I'm not an expert but you could try the Javascript/Jquery SetTimeout function associated with a WebService (.svc) to make periodic request from the client and retrieve data from the server.
I am in a bit tricky situation. I am using JavaScript's PageMethod functionality where I am invoking a PageMethod which works like a gem. However I am having an issue in accessing the HttpContext's state which returns me a value "SYSTEM" for
HttpContext.Current.User.Identity.Name
which is not the actual current User Name.
I know there are couple options like storing HttpContext.Current in a Session or saving Context's state in some other custom container but given a web farm environment I am assuming that this will not work as expected.
Here is the code I am working on with
function MyFunction(){
PageMethod.MyPageMethod();
}
here is the signature of the server method
[System.Web.Services.WebMethod()]
public static void MyPageMethod()
{
// gives me "SYSTEM"
var user = HttpContext.Current.User.Identity.Name;
}
Also if I use the above code to access user name in OnLoad event of the page then it works fine and returns me the CurrentUserName.
I am trying to get the above code to work in an ASP.NET Webform... :)
So I am wondering if there is a way to access the current actual user in page methods without making use of sessions.
Any help will be deeply appreciated.
NiK...
After quite some reading I think I was trying to do something which is not correct as to how page methods work. It gets quite tricky when your application's authentication system is windows based and these page methods when you invoke from JavaScript will not cause a postback and do not invoke the HttpModules. Instead it just calls that page method.
FYI, we had our own custom HTTPModule to handle security.This is even before any other HttpModule occurs and this was not being invoked while calling the page method as we are not doing a postback or even a partial postback (so the whole "niche" of a HTTPPost was missing). Moreover this led to a conclusion that we were making service calls without any authentication and was potentially a big security issue for us.
The bottom line is it was a bad design, well having said that I would like to mention about the solution/workaround we came up with and here is what we did. So, the only option we had is to do a postback keeping the UI alive and we wanted to update a label's message asynchronously and we achieved it by doing a hack using Sys.Application.add_init.
<script language="javascript" type="text/javascript" >
Sys.Application.add_init(function() {
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(endRequest);
});
function beginProcess() {
processCurrentItem();
}
var currentItem = 0;
function processCurrentItem() {
if (currentItem < 5) {
currentItem = currentItem + 1;
__doPostBack('updatePanel', currentItem);
}
}
function endRequest() {
processCurrentItem();
}
</script>
The markup we had in place was pretty simple with a label in the update panel and a button that invokes the "beginProcess()" function. Finally in the OnLoad we had the following code in place
protected override void OnLoad(EventArgs e)
{
if (this.IsPostBack)
{
this.lblLabel.Text = "text you may wanna update with";
// Call the Method you may wanna call
// also you may use Request["__EVENTARGUMENT"] to know who caused the
// postback and Request["__EVENTTARGET"] to access the argument you may
// have passed in.
}
}
And this solution is no longer using the JavaScript Page methods. And based on this solution if anyone thinks I am missing something here or think there is any other other way of doing this then do update this post with your suggestions.
NiK
I have developed an asp.net web application containing 40-50 pages. I have implemented logger using Log4net and each of my pages has this in its page_load event
protected void Page_Load(object sender, EventArgs e)
{
try
{
log.Debug("In Page Load Function");
if (!IsPostBack)
{
if (Session["OrgId"] != null)
{
// my logic
}
else
{
Response.Redirect("Homepage.aspx?Sid=1", false);
}
log.Debug("Page load function successfull");
}
}
catch (Exception ex)
{
log.Error(ex.Message, ex);
}
}
This method has many strings like "In page Load function","OrgId","Homepage.aspx?Sid=1","Page load function successfull". Now i would like to maintain these string which are common throughout my application in a seperate class file and use it with a variable...
Can you suggest what class can be used for this or your way of handling this.
You can create a base page that has this function, where you have // my logic add a call to an abstract method (load_page, for instance). Then subclass all of your other pages from this and override load_page from them, have it containing all the logic you need.
This will keep all of these strings in one place, though I don't see why you would need to change them to variables.
Maybe storing your strings in a resource file would help. Example here.
You could also think of inheriting that behavior from a base class.
It is clear that member variables of an ASP.NET page should lose scope every time the page is refreshed.
I have the following code to display a grid of "Alerts" with edit capabilities:
public partial class Alerts : System.Web.UI.Page
{
private _WikiSyntaxEngine;
protected void Page_Load(object sender, EventArgs e)
{
_WikiSyntaxEngine = new WikiSyntaxEngine();
}
protected void gvAlerts_RowDataBound(object sender, GridViewRowEventArgs e)
{
var alert = (DataModel.Alert)(e.Row.DataItem);
// Below is the only line of interest, because only when published
// to the server would it throw an exception...
string html = _WikiSyntaxEngine.ConvertWikiTextToHTML(alert.Description);
}
}
It was only on the development server to which I published did this code fail (i.e. throws a NullReferenceException because _WikiSyntaxEngine was null) during an update to the row.
However, on my local machine the code runs just fine - the row is updated and the grid refreshes as expected.
I have since fixed the code to make the WikiSyntaxEngine a singleton, or the way it should have been designed from the beginning because it shouldn't have to be instantiated everywhere it is used.
My question then, is how can the Page_Load event be called before gvAlerts_Databound() on my local machine (as expected), but not called on the development machine resulting in the NullReferenceException for _WikiSyntaxEngine?
I'm assuming that your gvAlerts GridView is defined in the ASP.NET markup of the page. If this is the case, it is probably being DataBound in the OnInit() event, not on Page_Load(), where you're instantiating WikiSyntaxEngine.
You probably should use OnInit() to instantiate your WikiSyntaxEngine if your GridView is getting bound before Page_Load.
As for why you're getting different behavior in development and production is still a mystery to me.