Caching a dynamically/programmatically added user control - asp.net

I'm trying to learn about caching, and in particular, partial caching using controls.
My website is running slow on certain pages, so caching as much as possible will be helpful.
Having run a number of experiments from code I have found on SO and various other Google results, I am running into an issue with dynamically added controls.
I have set up a simple page, containing this code:
<%# Page Language="VB" Debug="true" %>
<%# Register TagPrefix="controls" TagName="control" Src="~/test/control.ascx" %>
<script runat="server">
Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
Label2.Text = "Present Time: "
Label2.Text += DateTime.Now.ToString()
End Sub
</script>
<html>
<body>
<form id="form1" runat="server">
<div>
<h2 style="color:Red">Output Caching</h2>
<asp:Label ID="Label2" runat="server"></asp:Label>
<controls:control ID='control1' runat='server' />
'------------------------------------------
<hr />
<div id='dyn2' runat='server' />
</div>
</form>
</body>
</html>
The control control.ascx looks like this:
<%# Control Language="VB" ClassName="control" %>
<%# OutputCache Duration="60" VaryByParam="r" %>
<script runat="server">
Sub Page_Load() Handles Me.Load
controlContent.InnerHtml = "Control time: " & DateTime.Now.ToString()
End Sub
</script>
<div id="controlContent" runat="server"></div>
This works well, and gives me a "live" time in the page, whilst the cached control shows me a time which is only updated after 60 seconds has passed, as per the OutputCache declaration.
I can see how I can use this for any application when I need to cache a part of a page and that part is explicitly entered into the page with a <controls> tag. The varyByParam option is useful to me too. (I've yet to investigate varyByCustom!)
However, in some cases I am loading a control into a page, programmatically based on specific needs.
In this case, I use code like this:
Dim theResult As test_control2 = CType(LoadControl("~\test\control2.ascx"), test_control2)
dyn2.Controls.Add(theResult)
This is programmatically adding my second test control, imaginatively entitled control2.ascx into the div with id "dyn2".
With no cache directive header in the control, or it's code-behind,l everything works fine, but I can't cache it (unless I cache the entire page).
However, if I add the cache header as per the control code above, I get this error:
Unable to cast object of type 'System.Web.UI.PartialCachingControl' to type 'test_control2'.
Googling doesn't seem to help me much with this, and investigating the PartialCachingControl types has lead me into further problems!
Can someone tell me what should be doing to enable me to cache these controls?
If it matters, I am coding in VB.net and also using .NET 2.0, so any advice on limitations on this platform would be appreciated too, if applicable.

Ah ha! Finally found another question on SO that helped
How to LoadControl a control that uses VaryByControl OutputCache, specifying values for properties
Basically, I was using the wrong Type when loading the control changin:
Dim theResult As test_control2 = CType(LoadControl("~\test\control2.ascx"), test_control2)
dyn2.Controls.Add(theResult)
to
Dim theResult As PartialCachingControl = DirectCast(LoadControl("~\test\control2.ascx"), PartialCachingControl)
dyn2.Controls.Add(theResult)
Sorted it!

Related

viewstate is not loading/missing for default.aspx

My default page isn't including the viewstate when doing the postback, i.e. the viewstate is missing, it's not being included in either the forms or params collections when I look at them during debugging. This also causes the IsPostBack property to show as False and the events associated with the postback to not be called.
My first thought was that somewhere viewstate was being turned off, but I have set it explicitly on the page and all of the controls (the page is extremely small, just 1 button and one list links). Still no luck. The funny thing is, this is only happening on the initial load of the page, if I follow one of the links out and then back to the default page, the viewstate is present.
Why would the viewstate not be created or generated? I'm not getting any validation errors. The first symptom I noticed was server events not being fired, but that made sense one I saw that the viewstate wasn't present, I just don't know why it is missing. I do have a form on the page, and the controls are within that form (and they all work when I come back to the page from elsewhere, just not on the initial landing).
Update: someone wanted some code, this is a minimum
<%# Page Language="vb" AutoEventWireup="false" Inherits="Mysite._default" Codebehind="default.aspx.vb" %>
<!DOCTYPE HTML>
<html>
<head>
<title>Title</title>
<link href="Includes/Style.css" type="text/css" rel="stylesheet">
</head>
<body>
<form id="Form1" method="post" runat="server">
<asp:Button CssClass="Button" Id="cmdDone" Text="Done" runat="server" />
</form>
</body>
</html>
Partial Class _default
Inherits Page
Private Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
If Not Page.IsPostBack andalso request.HttpMethod = "Post" andalso ViewState("test") is nothing Then
Throw new Exception("No, viewstate")
End If
ViewState("test") = true
End Sub
End Class
The problem is that when asp.net renders <form id="Form1" method="post" runat="server"> the resulting html is <form id="Form1" method="post" action="./"> and NOT <form id="Form1" method="post" action="./Default.aspx">.
This results in the post going to the index and not to the page itself, and getting filtered out sometime before the page is loaded.
The solution is simple, fill out the action so that it goes directly to the page, action="Default.aspx".

Invalid viewstate error when posting back to same page

I'm having some issues with an Invalid Viewstate error and I can understand why it's happening but I don't know how to fix it.
I have a page which is similar to this /story/?id=123 but I'm using a different page to Server.Transfer to this page.
So I've set up /info to Server.TransferRequest("/story/?id=123") and it works fine until the page does a postback to itself.
We have a login form on this page which simply reloads the page but when it does it seems to add /?id=123 onto the end of the URL so it ends up like this /info/?id=123 thus causing an Invalid Viewstate error.
I've already tried adding EnableViewStateMac="false" - this fixes the error but it doesn't log the user in as expected so it does not give the required result.
So my questions are:
Is there a better way to redirect to my page other than Server.TransferRequest but still keeping the nice URL? - I don't want to Response.Redirect if I can avoid it.
If not, is there an easy way to fix this error that doesn't require me adding EnableViewStateMac="false"?
http://support.microsoft.com/kb/316920
I believe that article will explain why you are having the problem and it gives a solution to fix it.
I know you don't want to use Response.Redirect, but I think that would also solve the problem.
PRB: "View State Is Invalid" Error Message When You Use Server.Transfer
This article was previously published under Q316920
Retired KB Content Disclaimer
This article was written about products for which Microsoft no longer
offers support. Therefore, this article is offered "as is" and will no
longer be updated.
SYMPTOMS
When you use HttpServerUtility.Transfer("page name", true), you
receive the following error message:
The View State is invalid for this page and might be corrupted
CAUSE
This problem occurs because the EnableViewStateMac attribute of the
<pages> element is set to true by default. When this attribute is
set to true, ASP.NET runs a message authentication check (MAC) on the
view state of the page when the page is posted back from the client.
This check determines if the view state of the page was modified on
the client. For security purposes, it is recommended that you keep
this attribute set to true.
When you call the Server.Transfer method and set the second
parameter to true, you preserve the QueryString and the Form
collections. One of the form fields is the hidden __VIEWSTATE form
field, which holds the view state for the page. The view state message
authentication check fails because the message authentication check
only checks each page. Therefore, the view state from the page that
calls Server.Transfer is not valid on the destination page.
View state is page scoped and is valid for that page only. View state
should not be transferred across pages.
RESOLUTION
To resolve this problem, use one of the following methods.
Resolution 1
Transfer values between pages to pass your server control values to
the other pages. For more information, refer to the following MSDN
documentation: Passing Server Control Values Between
Pages
This requires that you create public properties for each property of a
control that you want to access from the destination page.
If you have many controls, and if you want to access the properties of
these controls from another page, you can also declare those controls
as public variables. For example:
Page1.aspx
Public Class Page1
Public WithEvents TextBox1 As System.Web.UI.WebControls.TextBox
'Insert your code here.
End Class
Page2.aspx
Dim sourcePage As Page1
sourcePage = CType(Context.Handler, WebForm1)
Response.Write(sourcePage.TextBox1.Text)
Resolution 2
Do not pass the second parameter (which is false by default) when
you call Server.Transfer. For example:
Server.Transfer("<page name>")
This code does not send the QueryString and the Form fields to the
page that is called. When no data is transferred, ASP.NET does not run
the message authentication check.
MORE INFORMATION
Steps to Reproduce the Behavior
Create an .aspx page named WebForm1.aspx that transfers execution to another page. Add the following code to WebForm1.aspx:
<%# Page language="vb" AutoEventWireup="true" %>
<html>
<body>
<form id="WebForm1" method="post" runat="server">
<asp:TextBox id="txtName" runat="server">Your Name</asp:TextBox><br>
<asp:Button id="Button1" runat="server" Text="Submit" OnClick="Button1_Click"></asp:Button>
</form>
</body>
</html>
<script runat=server>
Sub Button1_Click(sender As Object, e As System.EventArgs)
Server.Transfer("WebForm2.aspx",true)
End Sub
</script>
Create another .aspx page named WebForm2.aspx, and then add the following code:
<%# Page language="vb" AutoEventWireup="true" %>
<html>
<body>
<form id="WebForm2" method="post" runat="server">
<asp:Label id="lblName" runat="server" >Web Form 2</asp:Label>
</form>
</body>
</html>
<script runat=server>
Sub Page_Load(sender As Object, e As EventArgs)
Dim thisPage As System.Web.UI.Page
Dim nameTextBox As TextBox
thisPage = CType(Context.Handler, System.Web.UI.Page)
nameTextBox = CType(thisPage.FindControl("txtName"), System.Web.UI.Control)
lblName.Text = "Your name is '" & nameTextBox.Text & "'."
End Sub
</script>
Open WebForm1.aspx in your browser, and then click Submit.

Does ASP.NET have variables and procedures like ASP?

I came across a question here which had loops in ASP.NET
<%for (int i = 0; i < 10; i++) %>
This actually did work in ASP.NET. I then investigate if ASP.NET has variables like ASP but I could not find any solid answer.
A demo of asp variable is here
<%
dim name
name="Donald Duck"
response.write("My name is: " & name)
%>
Yes, but (in webforms anyway) it is there mainly for compatibility with old code and you should avoid using it that way. Includes, especially, will not work as you expect, as each file is compiled separately and includes are processed at runtime rather than compile time... you can use them for html, but not so much for code.
The new options of Master pages, custom and user controls, data binding, and other features are much better mechanisms for building your pages. User controls, especially, are too often overlooked.
What you can do, for simple pages at least, is put one big server-side script block at the top of the page:
<%# Page ... %>
<script runat="server" ... >
Public Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
'...
End Sub
'Other variables and methods can go in here
</script>
<html>
<body>
<form runat="server" ...>
</form>
</body>
</html>

asp:linkbutton not functioning in production

I've got an asp:linkbutton as follows:
<asp:LinkButton ID="lb_new" runat="server" ForeColor="White">New Item</asp:LinkButton>
Protected Sub lb_new_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles lb_new.Click
ViewState("ItemID") = "0"
Dim myURL As String
myURL = String.Format("D002.aspx?e={0}&i={1}", ViewState("EventID"), ViewState("ItemID"))
Response.Redirect(myURL)
End Sub
Up until recently, it has functioned as it should. But for some reason, it has stopped working in production. As far as I can tell, it's not connecting to it's code-behind at all (I tried modifying it to simply change the text in one of the text boxes ont he page, and it fails that as well). Still works if I run the website through visual studio. But as soon I publish to our production server, it no longer works.
I'm stumped -- and still fiddling with it.
If anyone has experienced this, please share. Have been on this for a couple hours now, and am out of ideas.
Thank you!
UPDATE
The event handler has been suggested as missing by a couple of folks. This is actually handled in the code-behind by the 'Handles' clause (...Handles lb_new.Click).
Manually deleted the items in the production folder, then re-published. No joy.
Verified the files in the production folder are the new ones.
I created a brand new linkbutton -- it fails to connect to it's code-behind as well
I added an Onclick= to the mark-up. This shouldn't be necessary, considering the Handles clause in the code-behind. Regardless, the click still fails.
...still plugging away at it
UPDATE2
Removed the required field validators on the page, and it works. This does not make sense to me, because I had other controls on the page causing postbacks, and they still worked the whole time. Also, I had the fields that were being validated filled-in, so no reason (I can think of) that the validators would have been preventing the postback.
Now I just have to figure out how to do validation on the page without the required field validators.
...confused... :-)
Check the __EVENTTARGET and __EVENTARGUMENT variables on the post; these should match the values from the button that triggered the postback. That's at least the first clue...
Did you upgrade some third party DLL or upgrade from .NET 3.5 to .NET 4.0 or something like tha too?
HTH.
I was having this same problem and this thread pointed me to the answer (for me at least!). Just set the CausesValidation property of the linkbutton to false and the click event will fire ignoring the state of any validators on the page. I'm not doing anything in the click event or postback that requires any validation so it's fine for me to ignore it. If the same is true for you, this could well be your solution.
I think you need to define its "Click" event.
<asp:LinkButton ID="lb_new" runat="server" ForeColor="White" OnClick="lb_new_Click">New Item</asp:LinkButton>
(Edited)
For VB.NET: (Example From MSDN:http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.linkbutton.onclick(v=VS.90).aspx)
<%# Page Language="VB" AutoEventWireup="True" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>LinkButton Example</title>
<script language="VB" runat="server">
Sub LinkButton_Click(sender As Object, e As EventArgs)
Label1.Text = "You clicked the link button"
End Sub
</script>
</head>
<body>
<form id="form1" runat="server">
<h3>LinkButton Example</h3>
<asp:LinkButton id="LinkButton1"
Text="Click Me"
Font-Names="Verdana"
Font-Size="14pt"
OnClick="LinkButton_Click"
runat="server"/>
<br />
<asp:Label id="Label1" runat="server" />
</form>
</body>
</html>
Validation controls were preventing a postback -- or at least, removing those controls seems to have solved the problem This does not make sense to me, because I had other controls on the page causing postbacks, and they still worked the whole time. Also, I had the fields that were being validated filled-in, so no reason (I can think of) that the validators would have been preventing the postback. Anyway, thanks to everyone for all the ideas.

Proper use of MasterPages

I've been looking into different methods of implementing masterpages.
Use the masterpage only for layout, include common controls on every page
Include controls on the masterpage, use a masterpage abstract base class and override it's properties in the masterpage class. This caused the masterpage events to no longer wire up. I could probably fix this, but it's a long way to go just for a single textbox value.
use good 'ol Page.Master.FindControl()
I've read that findcontrol should be avoided (uses magic "label1" strings, supposedly uses too many resources) and masterpages are only for layout. If masterpages are only for layout, do I copy and paste common controls across 100's of pages?
What's the best practice that deals with displaying and accessing common site controls (like a search)? Considering the alternatives, using findcontrol to get a masterpage control doesn't seem that bad.
MasterPages are classes just like a normal Page object. That means you can expose internal controls through public properties to allow child Pages to access without having to resort to Master.FindControl(). To do this you just need to set the MasterType property in the page (I think it may work even without setting this but with this you get intellisense support and avoid having to do casts).
Here's a basic example (sorry it's in VB - this is copying and pasting from an old project):
Master page (.master):
<%# Master Language="VB" CodeFile="default.master.vb" Inherits="DefaultMaster" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form runat="server">
<ASP:TextBox ID="Search" RunAt="Server"/>
<ASP:ContentPlaceHolder ID="Content" RunAt="Server"/>
</form>
</body>
</html>
Master code-behind (.master.vb):
Partial Class DefaultMaster : Inherits MasterPage
Public ReadOnly Property SearchBox() As TextBox
Get
Return Search
End Get
End Property
End Class
Accessing page (.aspx):
<%# Page Language="VB" MasterPageFile="default.master" CodeFile="page.aspx.vb" Inherits="ExamplePage" %>
<%# MasterType TypeName="DefaultMaster" %>
<ASP:Content ContentPlaceHolderID="Content" RunAt="Server">
<p>This is some content on the page.</p>
</ASP:Content>
Accessing page code-behind (.aspx.vb):
Partial Class ExamplePage : Inherits Page
Sub Page_Load(ByVal Sender As Object, ByVal E As EventArgs) Handles MyBase.Load
Master.SearchBox.Text = "This page now has access to the master's search box."
End Sub
End Class
Agreeing that master pages are only for layout, would a sensible approach not to farm out the common controls to usercontrols and include them in the master page this way.

Resources