Server.Transfer losing local variables - asp.net

For my application, I have a page that redirects to another page (within the same application) via Server.Transfer. I need to do this because the original page has an object that I need to access by using the Page.PreviousPage property.
Once my "destination" page has been fully loaded, a local deep clone that I made of the source page's object is suddenly released from memory once I perform a postback? Is this by design--something to do with the Server.Transfer?
An example...
Page1.aspx:
Public Structure myCustomObject
Implements ICloneable
Dim someField as String = "default value" ' Default value
Public Function Clone() As Object Implements System.ICloneable.Clone
Dim temp as new myCustomObject
temp.someField = Me.someField
Return temp
End Function
End Structure
Dim obj As myCustomObject
Public ReadOnly Property objProp as myCustomObject
Get
Return obj
End Get
End Property
objProp.someField = "changed value from source page"
Server.Transfer("page2.aspx", True)
Page2.aspx:
(onLoad)
Dim newObj As myCustomObject
newObj = Page.PreviousPage.objProp.Clone()
Debug.Write(newObj.someField) ' Output: "changed value from source page"
At this point, EVERYTHING works as it should. Stuff got cloned over correctly and all is well.
(Let's say this is on a button click event)
Debug.Write(newObj.someField) ' Output: "default value"<- This is NOT "changed value from source page" for some reason when it was working literally a few lines ago!
It's around here that I get the problem. My guess is that the Server.Transfer stops any association with the source page after the new page loads.
Is there a better way for cross-page object passing?

Just pass a variable in the HttpContext, you will have to handle your casting, not sure what Page.PreviousPage is:
Current Page:
HttpContext CurrContext = HttpContext.Current;
CurrContext.Items.Add("PreviousPage", Page.PreviousPage);
Transfered to page:
HttpContext CurrContext = HttpContext.Current;
var previousPage = CurrContext.Items["PreviousPage"];
Sorry for the C#, there was no code and the question wasn't marked with VB.NET when I answered. Someone feel free to convert.

If anyone is still looking for the solution to this issue, it's more than likely the settings.AutoRedirectMode = RedirectMode.Permanent; setting in your RouteConfig.cs. Change it to settings.AutoRedirectMode = RedirectMode.Off;
public static void RegisterRoutes(RouteCollection routes)
{
var settings = new FriendlyUrlSettings();
// settings.AutoRedirectMode = RedirectMode.Permanent;
settings.AutoRedirectMode = RedirectMode.Off;
routes.EnableFriendlyUrls(settings);
}

Related

RegisterForEventValidation can only be called during Render

I have a webmethod which will be called from jquery ajax:
[WebMethod]
public string TestMethod(string param1, string param2)
{
StringBuilder b = new StringBuilder();
HtmlTextWriter h = new HtmlTextWriter(new StringWriter(b));
this.LoadControl("~/Pages/Controls/Listing.ascx").RenderControl(h);
string controlAsString = b.ToString();
return controlAsString;
}
(it's a non-static method and we are able to hit it. That's not an issue)
When the loadControl() method is executed, I get an error saying: RegisterForEventValidation can only be called during Render.
I have already included EnableEventValidation="false" for the current aspx, disabled viewstate also. but still i get the same error. Any ideas on this?
Solution is to disable the Page's Event Validation as
<%# Page ............ EnableEventValidation="false" %>
and Override VerifyRenderingInServerForm by adding following method in your C# code behind
public override void VerifyRenderingInServerForm(Control control)
{
/* Confirms that an HtmlForm control is rendered for the specified ASP.NET
server control at run time. */
}
Refer the Below Link
http://www.codeproject.com/Questions/45450/RegisterForEventValidation-can-only-be-called-duri
As per Brian's second link, without hopping further, just do this:
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
Html32TextWriter hw = new Html32TextWriter(sw);
Page page = new Page();
HtmlForm f = new HtmlForm();
page.Controls.Add(f);
f.Controls.Add(ctrl);
// ctrl.RenderControl(hw);
// above line will raise "RegisterForEventValidation can only be called during Render();"
HttpContext.Current.Server.Execute(page, sw, true);
Response.Write(sb);
You need to notify ASP.Net that not to validate the event by setting the EnableEventValidation flag to false.
You can set it in the Web.Config in the following way
<%# Page Language="C#" AutoEventWireup="true" EnableEventValidation = "false"

ASP.NET Is it possible to handle all the null session checking in masterpage

I have the following steps in my webpage
1) User Logs in and I set the following session variables
Session("userName") = reader_login("useremail").ToString()
Session("userId") = reader_login("user_ID").ToString()
Session("firstName") = reader_login("firstName").ToString()
2) Now on my logged in VB.NET templates I reference a MasterPage called LoggedIn.Master. In Which I added the following method to check for the above null session variables. And if they null to redirect back to login page.
Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
'#Check that User is Logged in, if not redirect to login page
If (Session("userId") Is Nothing) Or (Session("userName") Is Nothing) Or (Session("firstName") Is Nothing) Then
Response.Redirect(ConfigurationManager.AppSettings("site_base_url").ToString & "login/", False)
End If
3) Now my question is if I want to use any above 3 Session variables in different .net templates or usercontrols referencing the above master page do i need to AGAIN add the check
If (Session("userId") Is Nothing) Or (Session("userName") Is Nothing) Or (Session("firstName") Is Nothing) Then
Response.Redirect(ConfigurationManager.AppSettings("site_base_url").ToString & "login/", False)
End If
In the respective pages or will the check in master page do. Because at the moment i.e. if in a usercontrol I attempt to do i.e.
customerName.Text = Session("userName").ToString()
or
Response.Write(Session("userName").ToString())
I am getting the error
Object reference not set to an instance of an object.
customerName.Text = Session("userName").ToString()
You can write a wrapper around the Session to handle null values and just call the wrapper when you access the items:
Public Class SessionWrapper
Public Shared ReadOnly Property Item()
'Access session here and check for nothing
End Property
End Class
And use it like this
SessionWrapper.Item("itemName")
In answer to your question - as long as the masterpage checks the session and redirects before all your controls and page code make a reference to Session, you should be fine.
You were using OnInit() which seems reasonable, but see this article for a good understanding of the timing of events.
Incidentally, I strongly discourage the use of ad-hoc calls to Session in your page and control code. Instead, I recommend you create a static SessionManager class that does the Session referencing for you. That way, you get to benefit from strong typing, and won't be able to accidentally make hard-to-debug 'session key' typos in your code like Session["FiirstName"]. Also, you can incorporate your null-session check right into the call for the session value:
EXAMPLE (in C#, sorry!)
public static class SessionManager
{
private static void EnsureUserId()
{
if (Session["userId"] == null)
{
Response.Redirect("YourLogin.aspx", false);
}
}
public static string FirstName
{
get
{
EnsureUserId();
if (Session["firstName"] == null)
Session["firstName"] = "";
return (string)Session["firstName"];
}
set
{
Session["firstName"] = value;
}
}
}
You can create an http module that asks about the session objects and if they are null, it will redirect to the login page and by developing this http module, in each page request the module will do the check and then you can use it normally without checking.
A better way to handle this would be to add a base class for all controls that require this session variables to be present. You can then add properties to wrap access to the session and other cool stuff and the check will work even if the controls are used with a different master page.

IHttpHandler for images producing a stackoverflow in IE

I have a directory of images that reside outside the context of my web application that I need to serve to the user. Currently I'm using an IHttpHandler to serve the images and using some javascript to navigate through a set of images (the navigation is primitive for now). I followed examples for using IHttpHandler to serve images closely but when I view the images in firefox the browser hangs and when I view in IE I get a "Stack overflow at line: 0".
Code for the IHttpHandler
Public Class ShowImage : Implements IHttpHandler
Public Sub ProcessRequest(ByVal context As HttpContext) _
Implements IHttpHandler.ProcessRequest
Dim picid As String
If context.Request.QueryString("id") IsNot Nothing Then
picid = context.Request.QueryString("id")
Else
Throw New ArgumentException("No parameter specified")
End If
'' Convert Byte[] to Bitmap
context.Response.Cache.SetCacheability(HttpCacheability.NoCache)
context.Response.Cache.SetNoStore()
context.Response.Cache.SetExpires(DateTime.MinValue)
Dim newBmp As Bitmap = GetPhoto(picid)
If newBmp IsNot Nothing Then
Dim imgGraphics As Graphics = Graphics.FromImage(newBmp)
imgGraphics.DrawImageUnscaled(newBmp, 0, 0, 640, 480)
context.Response.StatusCode = 200
context.Response.ContentType = "image/jpeg"
newBmp.Save(context.Response.OutputStream, ImageFormat.Jpeg)
newBmp.Dispose()
Else
'' Return 404
context.Response.StatusCode = 404
context.Response.End()
End If
End Sub
...
Public ReadOnly Property IsReusable() As Boolean _
Implements IHttpHandler.IsReusable
Get
Return True
End Get
End Property
End Class
Here is the javascript code that's calling the IHttpHandler defined above:
function updateImage(){
var ddlPhotos = document.getElementById("ddlPhotos");
var selected = ddlPhotos.options[ddlPhotos.selectedIndex].value;
if( selected != -1 ){
// Update the image
retrievePicture(document.getElementById("propertyImage"), selected)
}
}
function retrievePicture(imgCtrl, picid)
{
imgCtrl.src = 'ShowImage.ashx?id=' + picid;
}
Finally here's the img tag that is the "place holder":
<img src="#"
alt="Property Photo"
width="640px"
height="480px"
id="propertyImage"
onload="retrievePicture(this, '<%= pictureId.value %>');"
/>
I'm confused as to why the javascript seems to spiral out of control...
My guess - not being a JavaScript expert - is that the onload event is triggered any time the image finishes loading. In other words, as soon as the image is loaded, it triggers loading a new one... which triggers loading a new one... which triggers loading a new one etc.
You will probably be able to see that in multiple calls to the server for the same image - unless the browser is caching it, of course. Anyway, you'll either need to trigger it in some other way, or make the trigger detect that the image which has been loaded is already the right one, and there's no need to replace it.
I suspect the act of changing the src and loading a new image may be triggering the "onload" event of the image again.
Try clearing the event before setting the source, will probably look similar to this:
function retrievePicture(imgCtrl, picid)
{
imgCtrl.onload = null;
imgCtrl.src = 'ShowImage.ashx?id=' + picid;
}

load a user control programmatically in to a html text writer

I am trying to render a user control into a string. The application is set up to enable user to use tokens and user controls are rendered where the tokens are found.
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
HtmlTextWriter writer = new HtmlTextWriter(sw);
Control uc = LoadControl("~/includes/HomepageNews.ascx");
uc.RenderControl(writer);
return sb.ToString();
That code renders the control but none of the events called in the Page_Load of the control are firing. There's a Repeater in the control needs to fire.
I've been using the following code provided by Scott Guthrie in his blog for quite some time:
public class ViewManager
{
public static string RenderView(string path, object data)
{
Page pageHolder = new Page();
UserControl viewControl = (UserControl) pageHolder.LoadControl(path);
if (data != null)
{
Type viewControlType = viewControl.GetType();
FieldInfo field = viewControlType.GetField("Data");
if (field != null)
{
field.SetValue(viewControl, data);
}
else
{
throw new Exception("ViewFile: " + path + "has no data property");
}
}
pageHolder.Controls.Add(viewControl);
StringWriter result = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, result, false);
return result.ToString();
}
}
The object data parameter, enables dynamic loading of data into the user control, and can be used to inject more than one variable into the control via an array or somethin similar.
This code will fire all the normal events in the control.
You can read more about it here
Regards
Jesper Hauge
You would need to attach the control to a Page by adding it to a Controls collection of the Page or a Control on the page. This won't solve all of your problems unless you do something to explicitly disable rendering during the normal page render event.
I took Hauge's/ Scott Guthrie's method above and tweaked it so that you don't need to use reflection, or modify a UserControl to implement any special interface. The key was I added a strongly typed callback that the RenderView method above calls, instead of doing reflection.
I blogged the helper method and usage here
HTH,
Jon

How can I use a page_load local variable in event handler of a button

Im making a site in Visual Studio using vb and I have a variable in page_load but need its value in the event handler of a button for passing on session.
Any suggestions on how I can do this? Any help is appreciated
You can store a value in the CommandArgument property of a Button:
btn.CommandArgument = "Your value"
And then when handling the event you can pull it out:
CType(sender, Button).CommandArgument
You could also make a new class that extends Button and create new properties like below if you need multiple arguments:
Class SessionButton
Inherits Button
Public Property SessionGUID() As Guid
Get
Dim s As Guid = Nothing
If Not String.IsNullOrEmpty(ViewState("SessionGUID")) Then
s = New Guid(ViewState("SessionGUID").ToString())
End If
Return s
End Get
Set(ByVal value As Guid)
ViewState("SessionGUID") = value.ToString()
End Set
End Property
End Class
couldn't you just make the variable a class scoped variable, instead of local?
You can store it in a viewstate backed property:
Public Property MyStringVar() As String
Get
If ViewState("MyStringVar") = Nothing Then
Return String.Empty
End If
Return ViewState("MyStringVar").ToString()
End Get
Set
ViewState("MyStringVar") = value
End Set
End Property
Now using this property you can save your variable on page load and access it in the button click event handler.
EDIT: updated to VB
you declare the variable outside the page_load and then you can use it where you want :)
You could also create a class that encapsulates the action. Then you can capture any data you want and easily add to it later.
void Page_Load()
{
myBtn.Click += new MyButtonClass(variable).Run;
}
class MyButtonClass
{
int Param;
MyButtonClass(int param)
{
Param = param;
}
public void Run(object sender, EventArgs args)
{
// Do something usefull
}
}
If the data is created/retrieved on every Page_Load, this will work. If it is wrapped around an (! IsPostBack), the data must be stored in a session. Fortunatly, the class can be easily modified to store/load the variable from a session parameter.
I'm sorry for the c# code, maybe someone else can translate it then remove this message?

Resources