How can I place an ASP.NET menu control into cache? - asp.net

I have a SharePoint2010 site that I have created an ASP.NET menu control for.
The ASP.NET menu's content is initially empty, and on Page_Load I load its content from standard HTML files on the server:
protected void Page_Init(object sender, EventArgs e)
{
string MenuPath = (string)ConfigurationSettings.AppSettings["RootMenuPath"].ToString();
Menu1.Items[0].ChildItems[0].Text = File.ReadAllText(MenuPath + "\\About.htm");
//etc...
}
I notice this is a horrible way to do things. It hits the disk every single time a user loads a page.
How can I either:
a) Cache the code and asp.net menu item so that it stays in memory?
b) Use another method to ensure it isn't loaded from the disk?
Thanks

You can wrap data load into property and use at least page Cache there:
readonly object cacheLock = new object();
string AboutHTM
{
get
{
if (Cache.Get("page.about") == null)
lock (cacheLock)
{
if (Cache.Get("page.about") == null)
Cache.Insert(File.ReadAllText(MenuPath + "\\About.htm"));
}
return Cache["page.about"].ToString();
}
}

You could indeed use the cache or some variable that is initialized only once in Application_Start and reused later but I am afraid that you are doing some premature optimization here. You probably shouldn't be doing it unless you have identified that this is a bottleneck for your application performance. Reading files from disk is a fast operation especially if they are small.

If possible, I would store the menu data in an XML file, and cache the XML file.
XmlDocument xDoc = new XmlDocument();
if (Cache.Get("MenuData") == null)
{
xDoc.Load(Server.MapPath("/MenuData.xml"));
Cache.Insert("SiteNav", xDoc, new CacheDependency(Server.MapPath("/MenuData.xml")));
}
else
{
xDoc = (XmlDocument)HttpContext.Current.Cache.Get("MenuData");
}

Related

Response.Redirect(Request.Url.AbsoluteUri) doesn't update page data

I have Page_load method like this:
private void Page_Load(object sender, System.EventArgs e)
{
if (!IsPostBack)
{
// Load Content
LoadContent();
return;
}
// some code here
}
And I use Response.Redirect(Request.Url.AbsoluteUri) at the end of methods to prevent re-post actions cased by page refrashing. When I run my app from source code that works well (debug or run mode), but when I publish the app (even on the same machine) page data (which loads on LoadContent) is not updated on updated page (but re-post actions is prevented).
Please, could anyone tell me why it happens?
ADDED:
There is LoadContent() method:
// firstly I get an supervisedGroups list TIBCO iProcess Engine via .NET vendor library, and then:
if (supervisedGroups != null)
{
rptSupervisedGroups.DataSource = supervisedGroups; // rpt.. is Repeater
rptSupervisedGroups.DataBind();
}
ADDED:
Method where Response.Redirect are used:
private void removeFromGroup(string strGroupName)
{
using(SqlConnection con = DBHelper.GetNewConnection())
{
con.Open();
// here comes query to DB
}
// Reload Page
Response.Redirect(Request.Url.AbsoluteUri);
}
You have two ways to solve this cache issue.
One is to give instructions to the browser to not cache this page, for example on page load you run:
Response.Cache.SetExpires(DateTime.UtcNow.AddYears(-4));
Response.Cache.SetValidUntilExpires(false);
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
Response.Cache.SetNoStore();
But a better solution is to add a random number at the end of the url when you make the redirect, or even better add the new id from the data that you have insert, eg:
Response.Redirect("samepage.aspx?newid=" + NewId);
that way the page will forced to be readed again, and you still have the cache functionality.
Most likely your page is cached. Try to hit shift-f5 to check the content. You can make all redirect urls unique to prevent the browser showing a cached page. Or disable caching for the specific page.

ASP.NET: Compress ViewState

What are the latest and greatest ways to compress the ASP.NET ViewState content?
What about the performance of this? Is it worth it to keep the pages quick and minimize data-traffic?
How can I make:
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPDwUKMTM4Mjc3NDEyOWQYAQUeX19Db250cm9sc1JlcXVpcmVQb3N0QmFja0tleV9fFgkFLGN0b
DAwJENvbnRlbnRQbGFjZUhvbGRlcl9NYWluQ29udGVudCRSYWRCdXQxBSxjdGwwMCRDb250ZW50UGxhY2VIb
2xkZXJfTWFpbkNvbnRlbnQkUmFkQnV0MQUsY3RsMDAkQ29udGVudFBsYWNlSG9sZGVyX01haW5Db250ZW50J
FJhZEJ1dDIFLGN0bDAwJENvbnRlbnRQbGFjZUhvbGRlcl9NYWluQ29udGVudCRSYWRCdXQyBSxjdGwwMCRDb
250ZW50UGxhY2VIb2xkZXJfTWFpbkNvbnRlbnQkUmFkQnV0MwUsY3RsMDAkQ29udGVudFBsYWNlSG9sZGVyX
01haW5Db250ZW50JFJhZEJ1dDQFLGN0bDAwJENvbnRlbnRQbGFjZUhvbGRlcl9NYWluQ29udGVudCRSYWRCd
XQ0BSxjdGwwMCRDb250ZW50UGxhY2VIb2xkZXJfTWFpbkNvbnRlbnQkUmFkQnV0NQUsY3RsMDAkQ29udGVud
FBsYWNlSG9sZGVyX01haW5Db250ZW50JFJhZEJ1dDXz21BS0eJ7991pzjjj4VXbs2fGBw==" />
Into sometning like this:
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPDwUKMTM4Mjc3N==" />
The simple answer might not be what you want to hear. Too often, controls on the page have viewstate by default when they really don't need it. It's a good idea to switch off viewstate until you know you're going to need it, and only switch it on for the (hopefully) few cases where you actually want to keep the view state.
Avoid using ViewState
Use compression on the IIS server.
You can wireup something that will compress the viewstate into and out of a page by doing something like:
public abstract class PageBase : System.Web.UI.Page
{
private ObjectStateFormatter _formatter = new ObjectStateFormatter();
private static byte[] Compress( byte[] data )
{
var compressedData = new MemoryStream();
var compressStream = new GZipStream(output, CompressionMode.Compress, true);
compressStream.Write(data, 0, data.Length);
compressStream.Close();
return compressedData.ToArray();
}
private static byte[] Uncompress( byte[] data )
{
var compressedData = new MemoryStream();
input.Write(compressedData, 0, compressedData.Length);
input.Position = 0;
var compressStream = new GZipStream(compressedData, CompressionMode.Decompress, true);
var uncompressedData = new MemoryStream();
var buffer = new byte[64];
var read = compressStream.Read(buffer, 0, buffer.Length);
while (read > 0)
{
uncompressedData.Write(buffer, 0, read);
read = compressStream.Read(buffer, 0, buffer.Length);
}
compressStream.Close();
return uncompressedData.ToArray();
}
protected override void SavePageStateToPersistenceMedium(object viewState)
{
var ms = new MemoryStream();
_formatter.Serialize(ms, viewState);
var viewStateBytes = ms.ToArray();
ClientScript.RegisterHiddenField("__COMPRESSED_VIEWSTATE"
, Convert.ToBase64String( Compress(viewStateArray)) );
}
protected override object LoadPageStateFromPersistenceMedium()
{
var compressedViewState = Request.Form["__COMPRESSED_VIEWSTATE"];
var bytes = Uncompress( Convert.FromBase64String( compressedViewState ) );
return _formatter.Deserialize( Convert.ToBase64String( bytes ) );
}
}
I realize this is an old thread, but we have been using Telerik's RadCompression HttpModule for a while now and it works incredibly well at compressing ViewState, AJAX and Web Service responses. You can also cheat and save ViewState in session - good for low traffic sites.
http://www.telerik.com/help/aspnet-ajax/radcompression.html
Again, after some research into this I summarized my findings in a blog-post about Compressing View State.
To save a compressed View State, this is what I did:
protected override void SavePageStateToPersistenceMedium(object state) {
SaveCompressedPageState(state);
}
private void SaveCompressedPageState(object state) {
byte[] viewStateBytes;
using(MemoryStream stream = new MemoryStream()) {
ObjectStateFormatter formatter = new ObjectStateFormatter();
formatter.Serialize(stream, state);
viewStateBytes = stream.ToArray();
}
byte[] compressed = CompressionHelper.Compress(viewStateBytes);
string compressedBase64 = Convert.ToBase64String(compressed);
ClientScript.RegisterHiddenField(ViewStateFieldName, compressedBase64);
}
And for the loading-part, this code made it work for me:
protected override object LoadPageStateFromPersistenceMedium() {
return LoadCompressedPageState();
}
private object LoadCompressedPageState() {
string viewState = Request.Form[ViewStateFieldName];
if(string.IsNullOrEmpty(viewState)) {
return string.Empty;
}
byte[] decompressed = CompressionHelper.Decompress(viewState);
string decompressedBase64 = Convert.ToBase64String(decompressed);
ObjectStateFormatter formatter = new ObjectStateFormatter();
return formatter.Deserialize(decompressedBase64);
}
Seb, ViewState is already compressed... that is what you are seeing... a compressed version of your controls. If you want less overhead, then don't use viewstate :)
Viewstate use should be kept to a minimum!
This is an XML-lized visualization of your posted viewstate:
<viewstate>
<Pair>
<Pair>
<String>1382774129</String>
</Pair>
</Pair>
</viewstate>
<controlstate>
<HybridDictionary>
<DictionaryEntry>
<String>__ControlsRequirePostBackKey__</String>
<ArrayList>
<String>ctl00$ContentPlaceHolder_MainContent$RadBut1</String>
<String>ctl00$ContentPlaceHolder_MainContent$RadBut1</String>
<String>ctl00$ContentPlaceHolder_MainContent$RadBut2</String>
<String>ctl00$ContentPlaceHolder_MainContent$RadBut2</String>
<String>ctl00$ContentPlaceHolder_MainContent$RadBut3</String>
<String>ctl00$ContentPlaceHolder_MainContent$RadBut4</String>
<String>ctl00$ContentPlaceHolder_MainContent$RadBut4</String>
<String>ctl00$ContentPlaceHolder_MainContent$RadBut5</String>
<String>ctl00$ContentPlaceHolder_MainContent$RadBut5</String>
</ArrayList>
</DictionaryEntry>
</HybridDictionary>
</controlstate>
Basically just a few radiobuttons which like to know of their existance. (browsers don't send an <input type="radio"> field with the postdata if it is not checked). This is pretty minimal already.
It can likely be compressed by hooking in the load/save methods or HTTP modules, but this may not be really practical nor really needed.
In case the viewstate is much bigger in your real app, avoid getting objects in the viewstate at all. This can be achieved by initializing the controls in the OnInit() or Page_Init() methods instead of the default Page_Load().
The rationale behind this can be found at
http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/Truly-Understanding-Viewstate.aspx
and http://msdn.microsoft.com/en-us/library/ms972976.aspx
A quick summary:
ViewState is just the backing store for almost all control properties, including defaults.
After the defaults are set by OnInit(), the TrackViewState() method will is called.
Any subsequent changes (e.g. by Page_Load()) or an eventhandler, will be tracked and submitted to the client. This way those controls can restore their state at the next request.
Instead of relying at the framework to restore objects, restore objects in OnInit() when needed. (e.g. repopulating the options of a DropDownList from the database).
One exception:
If a control is dynamically added to the control tree, it plays a catch-up. Their OnInit() method may run at a later moment, causing those properties to end up in the viewstate after all. If the initialization of the control can't happen in OnInit(), setting EnableViewState="false" can be used as workaround.
Each time my viewstate grows unexpectedly, I'm using the "ViewState Decoder 2.2" app to find out what ended up in the viewstate. Often, it's not needed for the data to be there.
And a final word:
The viewstate is not used for repopulating forms!!
Those values are already submitted with the postdata.
Compressing view state fails in certain cases:
- If you are using update panel on page don’t use compression mode.
- If somehow you are changing the view state in result of ICallBack code don’t use compression mode, as this will don’t reflect the correct view state on post back.
The best way to minimize the view state is just to not use it. It will cause you to do some extra work programming (repopulating control values etc on post back, but it will save you on the amount of information you send to the browser). You can't tamper with it.
Here is a link to the view state on MSDN:
http://msdn.microsoft.com/en-us/library/ms972976.aspx
Here is a link describing some best practices:
http://mnairooz.blogspot.com/2007/01/aspnet-20-viewstate-and-good-practices.html
And One on disabling the ViewState:
http://www.codeproject.com/KB/aspnet/ASPNET_Best_Practices.aspx

Catch upload of to big files

Asp.Net has an upper limit for file uploads. I try to catch this situation on the server side. According to the documentation I found, it should be possible to override Application_Error in Global.asax, but it does not work for me. The second option would be to override OnError of the receiving page, but that also does not work.
Can anybody show some working code on how to catch this error on the server side?
Put following in Golobal.asax.cs:
void Application_Error(Object sender, EventArgs e)
{
HttpException ex = Server.GetLastError() as HttpException;
if (ex != null)
{
if ((ex.GetHttpCode() == 500 || ex.GetHttpCode() == 400) && ex.ErrorCode == -2147467259)
{
Server.ClearError();
Response.Redirect("~/MaximumFileError.aspx", false);
}
}
}
This worked for me, but I'm not sure if it works for all cases.
Uploadify is a jquery and flash uploader that allows you to specify a max size for files to be downloaded. This way you can prevent the user from downloading the file in the first place and dont have to worry about catching it after.
Rather than catch the error can't you check the size of the file against the maximum size specified in the web.config file? You can use the following to get the max size:
System.Configuration.Configuration config = WebConfigurationManager.OpenWebConfiguration("~");
HttpRuntimeSection section = config.GetSection("system.web/httpRuntime") as HttpRuntimeSection;
double maxFileSize = section.MaxRequestLength;
"Can anybody show some working code on
how to catch this error on the server
side?"
Nope. It's not possible to use code to catch this error, as it occurs before any code is started.
As far as I have found, you can't even specify an alternative error page for this error, the web server just refuses to accept the request when it's too large.
First you should understand few things about maxRequestLength. Using a server side approach validation you cannot predict what will be file size. Setting value to high is increasing the risk of DoS Attack.
I set in web.config maxRequestLength to 8MB:
<httpRuntime maxRequestLength="8192" executionTimeout="3600" />
I check in my code-behind of form if uploaded by user file is not greater than half of given in maxRequestLength, but this checking may never happen if the size of uploaded file turn out to be greater than max RequestLength specified in web.config, because the exception will be thrown. Such exception should be catch on the level of Global.asax. There I check whether the Exception is containing words identifying our problem, because System.Web.HttpUnhandledException can be thrown in many other situations! Good hint may be checking from which page the exception come, to be sure that we deal with certain form, what is important by redirecting user back to form.
void Application_Error(object sender, EventArgs e){
Exception wyjatek = Server.GetLastError();
if (wyjatek.InnerException != null && wyjatek.InnerException.Message.Contains("Maximum request length exceeded"))
{
Server.ClearError();
Response.Redirect("FormWithFile.aspx?alert=Za-duzy-plik");
}
}
If I recognize in Global.asax this exception I redirect user to the page with alert(given in GET).
In my code-behind of ASPX page:
First I retrive value of MaxRequestLength from web.config by this tree lines, and half it:
static System.Configuration.Configuration config = WebConfigurationManager.OpenWebConfiguration("~");
static HttpRuntimeSection section = config.GetSection("system.web/httpRuntime") as HttpRuntimeSection;
int maxFileSize = (section.MaxRequestLength/2)*1024;
Then in Action connected with insert button, I proceed as follows:
protected void InsertButton_Click(object sender, EventArgs e)
{
if (((FileUpload)FormView1.FindControl("FileUpload1")).HasFile) // WHEN USER WANT TO UPLOAD FORM WITH FILE (its, optional)
{
HttpPostedFile file = (HttpPostedFile)(((FileUpload)FormView1.FindControl("FileUpload1")).PostedFile);
int iFileSize = file.ContentLength;
if ((file != null) && (file.ContentLength > 0))
{
if (iFileSize > maxFileSize) // checking image SIZE!
{
MessageForUser.Text = "<h1 class=error>Bad File! File is to big!</h1>";
}
else
{
byte[] plik = ((FileUpload)FormView1.FindControl("FileUpload1")).FileBytes;
// HERE COMES CODE FOR INSERT OF FORM WITH FILE
MessageForUser.Text = "<h1>Insert was sucessfull</h2>";
}
}
}
else
{
// HERE COMES CODE FOR INSERT OF FORM WITHOUT FILE
MessageForUser.Text = "<h1>Insert was sucessfull</h2>";
}
}
In Page_Load I define also how to retrive communicate given by GET from Global.asax, to inform the user what happend.
if (Request.QueryString["alert"]!=null)
{
string temp = Request.QueryString["alert"].Replace('-',' ');
MessageForUser.Visible = true;
MessageForUser.Text = "<h1 class=error>ERROR: " + temp + "</h1>";
}
This solution has of course its drawbacks:
Still, we can be under DoS attack with 8MB files, what we recognize first at level of server, what is already very late.
The state of user form is lost in case of redirection from Global.asax, but this with some bit of code can be overcome.
User expirience is rather poor, because of checks on the server side, and by the load of many users, applicaiton can become slow.
Temporarly to the server come files even greater than 8MB, but such which manage in executionTimeout
Possible alternatives:
Use some flash technology to check on client side the file size
Use some stream techniques, to transfer bites in small packets, and in the moment, when given threshold is reached, throw own exception and handle it.
apropriate reading: Sending File in Chunks to HttpHandler

Postback problem for my custom control load wizard

I have some problem that happens when controls are loaded in init and it still doesn't help me to get proper postback event fired on time.
I am trying to create a rich wizard control that will enable switching, links with description, completely customized steps, integration of substeps - by using dynamic control load that is avoids standard asp.net wizard way of loading.
Idea is to have on left part navigation, on right part content, or substeps that are run from right part and that go over whole area.
Download source project
Ok, I re-read the question, and here is what you have to do. You have to re-load these controls on each postback, give them always the same "Id". This can be done in Page_Init or in Page_Load event. And of course, you have to re-attach event handlers on each post back.
Many thanks.. well i found the answer - id was the problem, in load control method. I was doing this wizard.. well most of things work now.
If someone is interested to see how does this works.. there are some updates:
public void LoadSplitViewControl(string path)
{
SwitchNavigationView(NavigationView.SplitView);
LastNavigationView = NavigationView.SplitView;
LoadControl(SplitControlLoader, path, "LoadedControlSplit");
}
public void LoadSingleViewControl(string path)
{
SwitchNavigationView(NavigationView.SingleView);
LastNavigationView = NavigationView.SingleView;
LoadControl(SingleControlLoader, path, "LoadedControlSingle");
}
public void LoadSingleViewControlAsClear(string path)
{
SwitchNavigationView(NavigationView.SingleView);
LastNavigationView = NavigationView.SingleView;
LoadControlAsClear(SingleControlLoader, path, "LoadedControlSingle");
}
private void LoadControl(PlaceHolder holder, string path, string ID)
{
UserControl ctrl = (UserControl)Page.LoadControl(path);
ctrl.ID = ID;
LastControlPath = path;
holder.Controls.Clear();
holder.Controls.Add(ctrl);
}
//as i am using steps loaded controls using splitview and substeps controls using single view sometimes viewstate will not be valid so error will be thrown but u can resolve this by using LoadSingleViewControlAsClear that will load below method.
private void LoadControlAsClear(PlaceHolder holder, string path, string ID)
{
UserControl ctrl = (UserControl)Page.LoadControl(path);
ctrl.ID = ID;
LastControlPath = path;
ctrl.EnableViewState = false;
holder.Controls.Add(ctrl);
}
/another cool idea i am using for such an wizard is that i am not using viewstate but rather session object for saving values collected over steps. My session object key is generated by authenticated username and pageguid - so u can have many loaded pages and each of them will handle different session object./
public Guid PageGuid
{
get
{
if (PageGuidField.Value == "")
{
var _pageGuid = Guid.NewGuid();
PageGuidField.Value = _pageGuid.ToString();
return _pageGuid;
}
return new Guid(PageGuidField.Value);
}
}

TreeView manipulation, saving adding etc

Here is what I am trying to do. I have a TreeView server side control (asp.net 2.0) and I need the user to be able to add nodes to it, then after all the nodes desired are added, the data should be saved to the database.
Here are some things I would like to pay attention to:
1) I don't want to save the tree data each time the new node is added, but rather keep the data in session until the user decides to save the entire tree. The question here is: can I bind the tree to ArrayList object and keep that object in session (rather than keeping the whole tree in session)? Then each time the node is added I will have to rebind the tree to the ArrayList rather than database.
2) I wish to minimize ViewState, any tips? What works best: compressing viewstate or keeping it all on the server at all times?
Thanks!
Use TreeNodeCollection as your internal array to hold in either ViewState or Session. Here's a rough mock-up of an approach you can use; far from perfect, but should set you on the right track.
TreeView tv = new TreeView();
// Button click event for 'Add Node' button
protected void AddNode(object sender, EventArgs e)
{
if (SaveNodeToDb(txtNewNode.Text, txtNavUrl.Text))
{
// Store user input details for new node in Session
Nodes.Add(new TreeNode() { Text = txtNewNode.Text, NavigateUrl = txtNavUrl.Text });
// Clear and re-add
tv.Nodes.Clear();
foreach (TreeNode n in Nodes)
tv.Nodes.Add(n);
}
}
public bool SaveNodeToDb(string name, string url)
{
// DB save action here.
}
public TreeNodeCollection Nodes
{
get
{
if (Session["UserNodes"] ! = null)
return (TreeNodeCollection) Session["UserNodes"];
else
return new TreeNodeCollection();
}
set
{
Session["UserNodes"] = value;
}
}

Resources