For my blog I am wanting to use the Output Cache to save a cached version of a perticular post for around 10 minutes, and thats fine...
<%#OutputCache Duration="600" VaryByParam="*" %>
However, if someone posts a comment, I want to clear the cache so that the page is refreshed and the comment can be seen.
How do I do this in ASP.Net C#?
I've found the answer I was looking for:
HttpResponse.RemoveOutputCacheItem("/caching/CacheForever.aspx");
The above are fine if you know what pages you want to clear the cache for. In my instance (ASP.NET MVC) I referenced the same data from all over. Therefore, when I did a [save] I wanted to clear cache site wide. This is what worked for me: http://aspalliance.com/668
This is done in the context of an OnActionExecuting filter. It could just as easily be done by overriding OnActionExecuting in a BaseController or something.
HttpContextBase httpContext = filterContext.HttpContext;
httpContext.Response.AddCacheItemDependency("Pages");
Setup:
protected void Application_Start()
{
HttpRuntime.Cache.Insert("Pages", DateTime.Now);
}
Minor Tweak:
I have a helper which adds "flash messages" (Error messages, success messages - "This item has been successfully saved", etc). In order to avoid the flash message from showing up on every subsequent GET, I had to invalidate after writing the flash message.
Clearing Cache:
HttpRuntime.Cache.Insert("Pages", DateTime.Now);
Hope this helps.
Using Response.AddCacheItemDependency to clear all outputcaches.
public class Page : System.Web.UI.Page
{
protected override void OnLoad(EventArgs e)
{
try
{
string cacheKey = "cacheKey";
object cache = HttpContext.Current.Cache[cacheKey];
if (cache == null)
{
HttpContext.Current.Cache[cacheKey] = DateTime.UtcNow.ToString();
}
Response.AddCacheItemDependency(cacheKey);
}
catch (Exception ex)
{
throw new SystemException(ex.Message);
}
base.OnLoad(e);
}
}
// Clear All OutPutCache Method
public void ClearAllOutPutCache()
{
string cacheKey = "cacheKey";
HttpContext.Cache.Remove(cacheKey);
}
This is also can be used in ASP.NET MVC's OutputCachedPage.
On the master page load event, please write the following:
Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
and in the logout button click:
Session.Abandon();
Session.Clear();
Hmm. You can specify a VaryByCustom attribute on the OutputCache item. The value of this is passed as a parameter to the GetVaryByCustomString method that you can implement in global.asax. The value returned by this method is used as an index into the cached items - if you return the number of comments on the page, for instance, each time a comment is added a new page will be cached.
The caveat to this is that this does not actually clear the cache. If a blog entry gets heavy comment usage, your cache could explode in size with this method.
Alternatively, you could implement the non-changeable bits of the page (the navigation, ads, the actual blog entry) as user controls and implement partial page caching on each of those user controls.
If you change "*" to just the parameters the cache should vary on (PostID?) you can do something like this:
//add dependency
string key = "post.aspx?id=" + PostID.ToString();
Cache[key] = new object();
Response.AddCacheItemDependency(key);
and when someone adds a comment...
Cache.Remove(key);
I guess this would work even with VaryByParam *, since all requests would be tied to the same cache dependency.
why not use the sqlcachedependency on the posts table?
sqlcachedependency msdn
This way your not implementing custom cache clearing code and simply refreshing the cache as the content changes in the db?
HttpRuntime.Close() .. I try all method and this is the only that work for me
Related
I am facing an issue in MVC that i am able to visit the previous page on browser back button click even after getting logged out. I have few approaches:
1) Disable the Browser back button using window.history.forward().
This will give bad user experience.
2) Using outputCacheAttribute by providing the duration=0 but this will restrict both server side and client side caching. SO don't want to use this.
3) Adding below method in global.asax.cs
protected void Application_BeginRequest()
{
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetExpires(DateTime.UtcNow.AddHours(-1));
Response.Cache.SetNoStore();
}
Third approach will not allow to make the copy of the cache to the browser. Also to make this work I have to add the [Authorize] attribute on each controller.
This is not the best option for me as I have hundreds of controller. And tomorrow if I will add new controller then again I have to decorate the Authorize attribute to that new controller.
Is there any other approach that any one of you can suggest.
You can only add this attribute:
[OutputCache(NoStore = true, Duration = 0, Location = OutputCacheLocation.None)]
Public ActionResult SomeAction()
{
//
}
Disabling cache in specific action should be enough I guess.
Or if you still do not want to destroy cache just couple of places,
you can do that in your LOGIN function, you can add previous Attribute, or just use this when someone signs out:
Session.Clear();
Session.Abandon();
I wish it will help, since I havent got much time on testing it myself.
The image of a login page (clickable image):
Is it seems (though the debug viewer), the ViewEngines are both (razor+webforms, im using the first) there, in the ViewEngines collection, and them both can find the views (manually) and display them, also tried manually.
So why is that they cannot do it on their own? This is simply an action method, nothing more:
public ViewResult Login()
{
return View();
}
Major Update
I have overridden the OnResultExecuting method:
protected override void OnResultExecuting(ResultExecutingContext filterContext)
{
if (filterContext.Result is ViewResult)
{
if (((ViewResult)filterContext.Result).ViewEngineCollection.Count == 0)
{
((ViewResult)filterContext.Result).ViewEngineCollection.Add(new RazorViewEngine());
}
}
base.OnResultExecuting(filterContext);
}
And it turned out that every time and every view I do now need to add new IViewEngine to a ViewResult. Now why is that so? Even if the ViewEngines.Engines collection is not empty?
Minor update.
The ViewEngineCollection stopped being empty at some point of idling.
Do you have a master page defined?
If you have a _ViewStart.chstml file in the root of your solution then it is possible the engine is trying to load a master page to combine with your view.
To skip the master page tell your view to not use a master page. Add the following to the View
#{
Layout = null;
}
or make sure that the master page referenced in the _viewStart.cshtml file exists.
You may not have a correct ViewEngines configured (or none).
Try adding to view engines manually to identify if there is a configuration error somewhere.
Add something like this to Application_Start() method of Global.asax.
protected void Application_Start()
{
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new RazorViewEngine());
// Other code
}
This should initialise the Razor view engine, and if as a result mvc starts searching locations, you have somehow turned off razor in the configuration.
The ViewEngineCollection stopped being empty at some point of idling.
This only might refer to some ASP.NET caching of old assemblies which was not for some reason updated by the new ones. This happens from time to time, and I have to clear the cache in order for some new code to work.
How can I clear browser cache only on logout, sure I can use the below:
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetExpires(DateTime.UtcNow.AddHours(-1));
Response.Cache.SetNoStore();
But this particular page which is a shopping bag page is accessible by both login and non-login users. How can I set it in such a way whereby the login user is able to access this page without clearing the browser cache but Only clears it when he/she logs out so that another user will not be able to access the history contents.
I have tried the solutions here:
http://www.codeproject.com/Tips/135121/Browser-back-button-issue-after-logout
made some changes but still couldn't figure out how to deal with this issue.
I also cleared my session on logout as below but I understand the browser cache will still stay.
FormsAuthentication.SignOut();
Session.Abandon();
Response.Redirect("~/");
Please advice. Thanks.
I am not a c# expert, but I am pretty sure what you have above only tells the browser to not cache the page you are on. There is no way to tell the browser to clear cache on any page. This would be a problem if there was such a way. Sounds like the solution you need is to not cache any page at all, regardless of logging out or not.
Perhaps you are getting muddled with the difference between server and client cache?
If you set output cache on your aspx page, that's server-side cache, and you have a scenario where .NET can decide whether to send pre-cached content or not, and still apply ACL rules.
If you set cache requirements on the HTTP you return using Response.Cache, that's client-side caching. Once the browser obeys the cache rules you send here, the only opportunity you will have to retract your cache rules is the next time the browser requests the page. If you set the cache to expire tomorrow, that's the next chance you'll get to amend the caching. Assuming the browser is obeying you, by the way, of which there is no guarantee.
In short, dynamic pages should not attempt to set client-side caching if you want them to stay dynamic. In fact you should actively use techniques such as the ones you mentioned to suppress Caching on those pages at all times.
Client-side caching should really only be used to assist with performance and bandwidth on the static parts of your site.
I am trying to solve a similar problem myself. This is just speculation, but if i could track a user specific header in my requests I was going to try using
HttpContext.Current.Response.Cache.VaryByHeaders["login"] = true;
in the global.asax
public override string GetVaryByCustomString(HttpContext context, string arg)
{
if (arg == "login")
{
return User().Name;
}
return base.GetVaryByCustomString(context, arg);
}
There is a way to do it. If you are caching a page, you can add a vary parameter. For Example
[OutputCache(Duration = 60, Location = System.Web.UI.OutputCacheLocation.Client, VaryByParam = "random")]
[CompressFilter]
public ActionResult Page(PageModel model)
{
...
}
In the example above, if I pass a random variable like the ticks of the current datetime object, that will prevent the cache.
For my blog I am wanting to use the Output Cache to save a cached version of a perticular post for around 10 minutes, and thats fine...
<%#OutputCache Duration="600" VaryByParam="*" %>
However, if someone posts a comment, I want to clear the cache so that the page is refreshed and the comment can be seen.
How do I do this in ASP.Net C#?
I've found the answer I was looking for:
HttpResponse.RemoveOutputCacheItem("/caching/CacheForever.aspx");
The above are fine if you know what pages you want to clear the cache for. In my instance (ASP.NET MVC) I referenced the same data from all over. Therefore, when I did a [save] I wanted to clear cache site wide. This is what worked for me: http://aspalliance.com/668
This is done in the context of an OnActionExecuting filter. It could just as easily be done by overriding OnActionExecuting in a BaseController or something.
HttpContextBase httpContext = filterContext.HttpContext;
httpContext.Response.AddCacheItemDependency("Pages");
Setup:
protected void Application_Start()
{
HttpRuntime.Cache.Insert("Pages", DateTime.Now);
}
Minor Tweak:
I have a helper which adds "flash messages" (Error messages, success messages - "This item has been successfully saved", etc). In order to avoid the flash message from showing up on every subsequent GET, I had to invalidate after writing the flash message.
Clearing Cache:
HttpRuntime.Cache.Insert("Pages", DateTime.Now);
Hope this helps.
Using Response.AddCacheItemDependency to clear all outputcaches.
public class Page : System.Web.UI.Page
{
protected override void OnLoad(EventArgs e)
{
try
{
string cacheKey = "cacheKey";
object cache = HttpContext.Current.Cache[cacheKey];
if (cache == null)
{
HttpContext.Current.Cache[cacheKey] = DateTime.UtcNow.ToString();
}
Response.AddCacheItemDependency(cacheKey);
}
catch (Exception ex)
{
throw new SystemException(ex.Message);
}
base.OnLoad(e);
}
}
// Clear All OutPutCache Method
public void ClearAllOutPutCache()
{
string cacheKey = "cacheKey";
HttpContext.Cache.Remove(cacheKey);
}
This is also can be used in ASP.NET MVC's OutputCachedPage.
On the master page load event, please write the following:
Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
and in the logout button click:
Session.Abandon();
Session.Clear();
Hmm. You can specify a VaryByCustom attribute on the OutputCache item. The value of this is passed as a parameter to the GetVaryByCustomString method that you can implement in global.asax. The value returned by this method is used as an index into the cached items - if you return the number of comments on the page, for instance, each time a comment is added a new page will be cached.
The caveat to this is that this does not actually clear the cache. If a blog entry gets heavy comment usage, your cache could explode in size with this method.
Alternatively, you could implement the non-changeable bits of the page (the navigation, ads, the actual blog entry) as user controls and implement partial page caching on each of those user controls.
If you change "*" to just the parameters the cache should vary on (PostID?) you can do something like this:
//add dependency
string key = "post.aspx?id=" + PostID.ToString();
Cache[key] = new object();
Response.AddCacheItemDependency(key);
and when someone adds a comment...
Cache.Remove(key);
I guess this would work even with VaryByParam *, since all requests would be tied to the same cache dependency.
why not use the sqlcachedependency on the posts table?
sqlcachedependency msdn
This way your not implementing custom cache clearing code and simply refreshing the cache as the content changes in the db?
HttpRuntime.Close() .. I try all method and this is the only that work for me
I've got a client that, during testing, is giving me conflicting information. I don't think they are lying but more confused. So, I would like to setup some simple auditing in my ASP.Net application. Specifically, right when any page is called, I want to immediately insert the Querystring and/or form POST data into a log table. Just the raw values.
Querystring is easy. But there doesn't seem to be a way to get the raw form POST'ed data without using BinaryRead, and if I do that, then I screw myself out of using the Request.Form collection later on.
Does anyone know a way around this?
EDIT: tvanfosson suggested Request.Params. I was looking for something that was easier to use (like Request.Querystring, only for POST), but I guess I could just as easily loop through all params and build a string of name=value&, etc).
You can create a custom HttpModule to capture all request made to your application so you don't need to touch every page and you can use it only during testing just not to slow down performance in production.
A sample implementation would be:
public class CustomModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.EndRequest += new EventHandler(context_BeginRequest);
}
private void context_BeginRequest(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
// you can use the context.Request here to send it to the database or a log file
}
}
You need to add the module to your web.config
<httpModules>
<add name="CustomModule" type="CustomModule"/>
</httpModules>
All of the form data should be in Request.Params. You'd need to do this on every page, though or maybe use an HttpModule.
[EDIT] If you want to get the form parameters separately use Request.Form, along with Request.QueryString
I would recommend implementing and HttpHandler or an HttpModule for this type of scenario. You can get to the POST Data from the Page_Load event but implementing this logging facility here is not as maintainable.