Dynamically disable/enable outputcache for a page during runtime - asp.net

I am using a CMS system, by the name of Composite C1. It renders all of it's content through single page (Page.aspx), which has a custom output cache profile attached.
This is all good, but I have run into a problem.
I want to have caching, but there are certain URLs that I would like to disable outputcaching for.
I know there is the varybycustom attribute that I can add to the cache profile, but I don't think this will give me exactly what I want. I want to be able to disable the cache completely when hitting specific URLs (or perhaps some other condition).
This seems to be very tricky as every page/url renders through the single Page.aspx file with it's outputcache profile defined.
Does anyone have any advice on how I might be able to solve this problem?

In \Global.asax you'll find an override of GetVaryByCustomString that calls into Composite C1 to evaluate if the response should be cached or not. You could intervene that, and only call into Composite if the request is not for one of the urls that you don't want cached:
if (context.Request.Url.AbsolutePath != "/dont-cache-this")
{
return ApplicationLevelEventHandlers.GetVaryByCustomString(context, custom) ?? base.GetVaryByCustomString(context, custom);
}
return null;
Note that upgrading Composite C1 later on might replace \Global.asax and wipe your changes.
Also see http://msdn.microsoft.com/en-us/library/5ecf4420.aspx

You can insert the following code to functions which are on the pages that shouldn't be cached. Or add a small function that would do just that:
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);

Related

Huge ASP.NET Web Forms

I looking for strategies others have taken for handling ASP.NET web forms with huge numbers of fields. For example, we have a single page that can have around 200 fields and 3 data entry grids in user controls, Now were looking to add even more. It seems to me that at some point the viewstate, or something, is going to break down. So I'm interested to hear how others have handled this level of fields.
MORE INFO BASED ON GOOD FEEDBACK BELOW: I'm thinking maybe changing my main form to more of a dashboard, and when the user wants to enter/edit a data section they get redirected to a new page entirely. When they're done it redirects back. We already have user controls for the 3 grids (totally different types of data). But User Controls I'm finding are nightmares as far as when they render, interactions with the "parent" etc.
I have a form with over 1500 form fields, no issues as of yet. You should be fine unless your server is resource anemic or you have extraordinary loads.
You should however take note of this massive gotcha that took me unawares:
http://support.microsoft.com/kb/2661403
By default, 1000 form controls is the max you can submit to your page. And there is no error thrown, the page will only accept the first 1000 items, and ignore the rest. Pretty awesome discovery in a production environment...
Fortunately you can override that default with this in your web.config:
<appSettings>
<add key="aspnet:MaxHttpCollectionKeys" value="10000" />
</appSettings>
I wouldn't recommend forms with that many controls, but it was at the insistence of the client :)
I looking for strategies others have taken for handling ASP.NET web
forms with huge numbers of fields.
I like to share "a trick" that I use on the rare case of a form with hundreds of fields. On post back I eliminate the fields that actually not change, or they have some default input. On post back I know the fields that I have eliminate and usually for the default action I do not need to do anything. Eg a not select check box, or an input field that have not change, etc....
That way, the actually post is significant smaller. For example on jQuery here is a simple code:
function cOnSubmit()
{
jQuery(".MyInputCss").each(function(index, domElem)
{
var me = jQuery(domElem);
// just an example - if the default have selected of no action, I eliminate it.
if(me.find("input[type='radio']:checked").val() == "-1")
{
// removing the name is not take part on the post back
me.find("input").removeAttr("name");
}
});
return true;
}
and on the form I call this function as:
<form .... onsubmit="return cOnSubmit();">
It seems to me that at some point the viewstate, or something, is
going to break down.
1) If you have a lot of control, you will end up with Operation is not valid due to the current state of the object.
It can easily be fixed by using MaxHttpCollectionKeys like Chris Hardie's suggested.
Note: a label server control is also counted as one control in addition to textbox server control.
2) The another problem I can think of will be large ViewState.
In order to solve this, you can either store ViewState in StateServer or SQL Server.
Update 12/20/2013:
Sorry, I forget to mention how to save ViewState to Session.
Since you already store SessionState is SQL Server, all you need is to inherit the aspx pages from this BasePage.
public class BasePage : Page
{
protected PageStatePersister _persister;
protected override PageStatePersister PageStatePersister
{
get { return _persister ?? (_persister=new SessionPageStatePersister(this));}
}
}

Preventing users from skipping pages in an ASP.NET form

I have a data entry form that I've broken apart into 5 pages and I want to make sure that the users go through the pages in the proper order each time. The navigation is set up to take them through it properly, including a breadcrumb to go back and change previous pages. I want to make sure, however, that they don't get to a later page without going through the earlier pages first (i.e., go through pages A and B then skip to D by entering the URL directly, or start out on D using a bookmark).
I've made some attempts to prevent this, with my latest one being this, which checks for required session variables specific to each page:
public static string CheckForPageSkipping()
{
var redirectTo = "";
if (HttpContext.Current.Session["TestDate"] == null) redirectTo = "~/Auth/SignOff.aspx";
if (HttpContext.Current.Session["Repairs"] == null) redirectTo = "~/Auth/RepairNotes.aspx";
if (HttpContext.Current.Session["MeterNum"] == null) redirectTo = "~/Auth/TestResults.aspx";
if (HttpContext.Current.Session["PremiseOwner"] == null) redirectTo = "~/Auth/PremiseInfo.aspx";
return redirectTo;
}
I called this during each Page_Load event and redirected to the page it returned if it was not the page they were on. This almost worked, until I realized that it prevents you from being able to navigate backwards (it always sends you back to the latest page you haven't completed). I'm sure I can cobble something together to make this work, but I wanted to see if there is a better way to do it. Something either built in to ASP.NET, or something more elegant that someone had built in the past.
My search attempts, both in Google and on StackOverflow, yielded no useful results. Maybe I was using the wrong terms, but it was mostly stuff like I've written here (the suggestions made at the top now have not been helpful either).
It seems like you have control of the pages so why not get rid of them and put the code into user controls, register these controls in one page in panels or placeholders and control the visibility of these panels in the code so that you can properly enforce the order in which these are visited?
Have you looked at the <asp:wizard> built-in control? I think it will fit your needs and you can configure it for many different scenarios.
Here is a link describing its use: http://msdn.microsoft.com/en-us/magazine/cc163894.aspx
Also see the current official documentation: MSDN Library
There is a good pattern explained here for using multi-part forms using panels.
Multi-Part Form using Panels
If you don't want to use the Wizard control and you don't want to use Panels, either Server.Transfer or Cross Page Posting should work. With either method, checking the PreviousPage should cover your page skipping concerns.
Server.Transfer has the URL oddity you mentioned, but is otherwise pretty straightforward.
Cross-Page Posting is quite appropriately tailored to your needs as well.
Post each form to the next form page.
Receive and process the data there (with strong typing if you set it
up correctly).
Only load the new page if everything checks out (PreviousPage is
correct, data is valid).
I admit, I don't have direct experience using either of these methods myself, but I can see how they could be implemented to handle your situation.

Mapping RouteTable on PreInit() - Will it cause any problems?

My page route mapping changes depending on certain criteria. To determine this criteria I need access to HttpRequest which means I can't do my route mapping in Application_Start(). On that note I have done it on PreInit() on my default page and it seems to work without an issue. However, all the examples I have seen with Route Tables are doing it in Application_Start, is this purely to avoid clearing the route list and adding them again? Will there it cause any harm to my web application doing in the way I am doing it?
EDIT: Example:
I load controls from different folders based on the project number and whether the website is being viewed on a mobile device, on that note I need to know this information before mapping my routes, like so:
RouteCollection.MapPageRoute("OneParam", "{Action}.html", String.Format("~/{1}{2}/Default.aspx", ProjectNumber, MobilePathStr));
which would map to something like ~/1234/Mobile/Default.aspx or could map to ~/1234/Default.aspx.
Don't do that.
Instead, you should create your own RouteBase class which looks up that information for each request and runs the appropriate handler.
To send to an ASPX page, you can either return BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(Page)) as Page; or create a PageRouteHandler and delegate to it.

ASP.NET - how to change requested page names transparently?

Very new to this subject and under the gun to come up with a solution. The problem is how I could load one of several versions of the same ASPX page, for any given page. E.g. unknown to the unsuspecting user who requests catalog.aspx, I would actually serve one of catalog_1.aspx, catalog_2.aspx, or catalog_3.aspx, etc.
Strange request indeed. It's due to an inherited decade-old product having inlined styles all over the ASPXs. Instead of re-writing the hundreds of ASPXs to be flexible, I'm trying to regexp-replace them to get versions suitable for various screen sizes. I'd then choose the best one after measuring window size at user login (and perhaps store the size in a cookie).
I thought this would involve some lower level object like an http handler. Close?
LJ
Update: I ended up doing this through url rewriting which works much better. The easiest place to do this in asp.net is apparently global.asax, and under Application_BeginRequest event. Call context.RewritePath(newpath, False) to send the request to a different page than requested.
In the way I did it, the destination page can change from request to request, and that apparently upsets postbacks, if the recipient of the postback isn't the exact version of the page that generated the viewstate. I tried to turn off viewstate validation but didn't help. So had to prevent flipping between versions once a user's logged in. Hope this helps someone.
Server.Transfer is probably the quickest way of doing just that.
string TransferTo = string.Empty;
if( Something )
TransferTo = "catalog_1.aspx";
else if( SomethingElse )
TransferTo = "catalog_2.aspx";
else
TransferTo = "catalog_3.aspx";
Server.Transfer( TransferTo, false );
Documentation
Note
If the subsequent pages have postback controls on them, they will reveal the true URL of the page at that point. If that matters, then this method will not work.
I don't like this method, but maybe you could use a full-window IFRAME to hold the appropriate page - catalog.aspx would be nothing but a big frame, and you could set the source of that frame in your codebehind.

Output caching a page except a user control in it

I have a page which contains a user control. The structure of the page is as shown below:
Incase your not able to see the above image, please check it at http://i54.tinypic.com/2r4id5f.jpg Now, apart from the contents of the UserControl, I'd like to cache the entire page. I tried using the OutputCache attribute in the .aspx page, however it caches the contents of the UserControl as well.
Kindly let me know how will I be able to cache the contents of the page except that of the user control.
Thanks in advance.
I think you can use the asp.net Substitution control to achieve this. Here is a link to ScottGu walking through an example.
The basic idea is that you cache you whole page as per usual, but mark parts for substitution that can be replaced for each request.
I think you are looking for VaryByControl. Also check out this post on fragment caching
Look at using substitutions.
This should help
However, the snag is, since substitution is done outside of the Page lifecycle, you can't render a user control for your substitution. You have to write a method that returns a string for the substitution. But this may work for you.
Have you tried adding the #OutputCache to both the usercontrol and the page but the usercontrol set the varyByParam="qsvalue;postvalue" where qsvalue is a generated query string you make random for every call of the page and postvalue is the same for postback.
The user control will still get cached, but in theory it should never get a chache hit as the qsvalue/postvalue is always different from that cached. It may not scale well - best set duration to the minimum as well, to prevent large numbers of them building up in the cache.

Resources