I'm trying to create a Caching Class to cache some objects from my pages. The purpose is to use the Caching system of the ASP.NET framework but to abstract it to separate class.
It seems that the caching doesn't persist.
Any ideas what I'm doing wrong here? Is it possible at all to cache object out side the Page it self?
EDIT: added the code:
Insert to cache
Cache c = new Cache();
c.Insert(userid.ToString(), DateTime.Now.AddSeconds(length), null, DateTime.Now.AddSeconds(length), Cache.NoSlidingExpiration,CacheItemPriority.High,null);
Get from the cache
DateTime expDeath = (DateTime)c.Get(userid.ToString())
I get null on the c.Get, even after I did have the key.
The code is in a different class than the page itself (the page uses it)
Thanks.
There are numerous ways you can store objects in ASP.NET
Page-level items -> Properties/Fields on the page which can live for the lifetime of the page lifecycle in the request.
ViewState -> Store items in serialised Base64 format which is persisted through requests using PostBack. Controls (including the page itself - it is a control) can preserve their previous state by loading it from ViewState. This gives the idea of ASP.NET pages as stateful.
HttpContext.Items -> A dictionary of items to store for the lifetime of the request.
Session -> Provides caching over multiple requests through session. The session cache mechanism actually supports multiple different modes.
InProc - Items are stored by the current process, which means should the process terminate/recycle, the session data is lost.
SqlServer - Items are serialised and stored in a SQL server database. Items must be serialisable.
StateServer - Items are serialised and stored in a separate process, the StateServer process. As with SqlServer, items must be serialisable.
Runtime - Items stored in the runtime cache will remain for the lifetime of the current application. Should the applciation get recycled/stop, the items will be lost.
What type of data are you trying to store, and how do you believe it must be persisted?
Right at the beginning of last year I wrote a blog post on a caching framework I had been writing, which allows me to do stuff like:
// Get the user.
public IUser GetUser(string username)
{
// Check the cache to find the appropriate user, if the user hasn't been loaded
// then call GetUserInternal to load the user and store in the cache for future requests.
return Cache<IUser>.Fetch(username, GetUserInternal);
}
// Get the actual implementation of the user.
private IUser GetUserInternal(string username)
{
return new User(username);
}
That was nearly a year ago, and it has been evolved a bit since then, you can read my blog post about it, let me know if thats of any use.
Your cache reference needs to be accessible to all items in your code - the same reference.
If you are newing up the Cache class every time, you are doing it wrong.
I have done almost the same things, but with a different code (and it work for me) :
(CacheKeys is an enum)
using System;
using System.Configuration;
using System.Web;
using System.IO;
public static void SetCacheValue<T>(CacheKeys key, T value)
{
RemoveCacheItem(key);
HttpRuntime.Cache.Insert(key.ToString(), value, null,
DateTime.UtcNow.AddYears(1),
System.Web.Caching.Cache.NoSlidingExpiration);
}
public static void SetCacheValue<T>(CacheKeys key, T value, DateTime expiration)
{
HttpRuntime.Cache.Insert(key.ToString(), value, null,
expiration,
System.Web.Caching.Cache.NoSlidingExpiration);
}
public static void SetCacheValue<T>(CacheKeys key, T value, TimeSpan slidingExpiration)
{
HttpRuntime.Cache.Insert(key.ToString(), value, null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
slidingExpiration);
}
public static T GetCacheValue<T>(CacheKeys key)
{
try
{
T value = (T)HttpRuntime.Cache.Get(key.ToString());
if (value == null)
return default(T);
else
return value;
}
catch (NullReferenceException)
{
return default(T);
}
}
Related
We are refactoring our site to use an external cache and the first step we took was using a custom OutputCacheProvider. First, we created a simple provider that just wraps MemoryCache and found problems with the way we are managing dependencies.
We have a custom OutputCacheAttribute that adds an extra key dependency to be able to invalidate a set of pages when certain entities change and to keep this feature I see some options:
Removing manually the CachedVary that ASP.NET stores in the cache, assuming that the key is "a2" + query". This seems to work but I'm not sure about the reliability.
Add cache keys that contain an array of the pages that have to be evicted from the cache then the key is removed o use the external cache key dependency feature in case it has it. This should be enough to emulate the key dependency we used but in a more complex way.
Forget about this, put a short cache period and let them expire without worrying much about that.
Do our own page caching and forget about ASP.NET output cache, not very appealing.
I'm sure there are other ways. Any tips, experiences or recommendations?
I answer my own question with the solution we adopted just for the record.
In our OutputCacheAttribute we add an empty cache object with a key that depends on the requested URL and some parameters. This will be used to invalidate a page externally.
Then, we also add another object with a key that depends on the current request and contains the previous cacheKey.
Finally, a static ValidationCallback is set up. The callback gets the value of the key for the current request, which is the dependency key. Then if it's not null, gets the value of the dependency, if it's null, the dependency has been evicted and we set the validationStatus to HttpValidationStatus.Invalid.
Some code to illustrate:
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
base.OnResultExecuting(filterContext);
// Build dependencies
BuildDependencies(paramsToDepend, filterContext.Controller, this.Duration);
}
private void BuildDependencies(IEnumerable<string> paramsToDepend, ControllerBase controller, int duration)
{
string[] valuesToInclude = GetValuesToInclude(paramsToInclude, controller.ControllerContext);
// Build the caché key for the current request
var cacheKey = CacheKeyProvider.GetCacheKeyFor(controller, paramsToDepend);
var cache = controller.ControllerContext.HttpContext.Cache;
var cacheValue = cache.Get(cacheKey);
if (cacheValue == null)
{
// The key is created if not exists
Provider.Add(cacheKey, new object(), Context.CurrentDateTime.AddSeconds(duration).ToUniversalTime());
}
// Add the dependency
Provider.Set(CachePrefix + controller.ControllerContext.HttpContext.Request.Path, cacheKey, Context.CurrentDateTime.AddSeconds(duration).ToUniversalTime());
// Register callback
controller.ControllerContext.HttpContext.Response.Cache.AddValidationCallback(new HttpCacheValidateHandler(ValidationCallback), null);
}
public static void ValidationCallback(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
var provider = OutputCache.Providers[OutputCache.DefaultProviderName];
var dependency = provider.Get(CachePrefix + context.Request.RawUrl) as string;
if (dependency == null) return;
var depValue = provider.Get(dependency);
// If it's null, someone has invelidated the caché (an entity was modified)
if (depValue == null)
{
validationStatus = HttpValidationStatus.Invalid;
}
}
I'm still relatively new to .NET and ASP.NET MVC, and I have had a few occasions where it would be nice to store information retrieved from the DB temporarily so it can be used on a subsequent server request from the client. I have begun using the .NET Session to store this information, keyed off of a timestamp, and then retrieve the information using the timestamp when I hit the server again.
So a basic use case:
User clicks 'Query' button to gather information from the system.
In JS, generate a timestamp of the current time, and pass this to the server with request
On server, gather information from DB
On server, use unique timestamp from client as a key into the Session to store the response object.
Return response object to client
User clicks 'Generate Report' button (will format query results into Excel doc)
Pass same timestamp from #2 down to server again, and use to gather query results from #4.
Generate report w/o additional DB hit.
This is the scheme that I have begun to use in any case where I use the Session as temporary storage. But generating a timestamp in JS isn't necessarily secure, and the whole things feels a little... unstructured. Is there an existing design pattern I can use for this, or a more streamlined/secure approach? Any help would be appreciated.
Thanks.
You may take a look at TempData which stores the data in Session.When you pull something out of TempData it will be removed after the Action is done executing.
So, if you put something in TempData in an Action, it will live in TempData across all other actions until its requested TempDatafrom TempData again.
You can also call TempData.Peek("key") which will keep it in memory until you call TempData["key"] or TempData.Remove("key")
Ok, I'm not sure I understand you correctly as the JS timestamp step seems superfluous.
But this is what I would do.
public static string SessionReportKey = "Reports";
public static string ReportIDString = "ReportID";
public Dictionary<string, object> SessionReportData
{
get
{
return Session[SessionReportKey] == null ?
new Dictionary<string, object>() :
(Dictionary<string, object>) Session[SessionReportKey];
}
set
{
Session[SessionReportKey] = value;
}
}
public ActionResult PreviewReport()
{
//retrive your data
object reportData = GetData();
//get identifier
string myGUID = new GUID().ToString();
//might only need [SessionReportData.Add(myGUID, reportData);] here
SessionReportData = SessionReportData.Add(myGUID, reportData);
//in your view make a hyperlink to PrintReport action with a
//query string of [?ReportID=<guidvalue>]
ViewBag[ReportIDString] = myGUID;
return View(reportData);
}
public FileContentResult PrintReport()
{
if(SessionReportData[QueryString[ReportIDString]] == null)
{
//error no report in session
return null;
}
return GenerateFileFromData(SessionReportData[QueryString[ReportIDString]]);
}
#In
Identity identity;
Boolean newValue = identity.hasPermission(target, action);
Any call to the above method also does a "select role from Role r" call, which is called from the underlying seam engine. How do I set the query cache for this call as a query hint (e.g. org.hibernate.cacheable flag) so that it doesn't get called again.
Note: Role information is never bound to change, hence I view this as a unnecessary sql call.
I am not in hibernate, but as this question is still unanswered: we extended the standard Identity class of seam for several reasons. You might want to extend it as well to help you caching the results.
As this cache is session scoped, it will have the possible benefit that it will be reloaded when the user logs on/off again - but this depends on your requirements.
Best regards,
Alexander.
/**
* Extended Identity to implement i.e. caching
*/
#Name("org.jboss.seam.security.identity")
#Scope(SESSION)
#Install(precedence = Install.APPLICATION)
#BypassInterceptors
#Startup
public class MyIdentity extends Identity {
// place a concurrent hash map here
#Override
public boolean hasPermission(Object name, String action) {
// either use the use the cached result in the hash map ...
// ... or call super.hasPermission() and cache the result
}
}
The users of my web application may have more than one browser window (or tab) open and pointed to the same page. We're using cookie based session id's, and the user will usually work within the same session id in both browsers/tabs. I would like to be able to uniquely identify which browser window (and tab) that requested an ASP.NET page (in order to make sure, that data stored in the session does not get mixed up).
(e.g. I would be happy if the browser would generate and send a window/tab-id with the http request, as it publishes HTTP_USER_AGENT)
Any ideas?
--thomas
If I was going to implement something like this I would probably start with a Dictionary<Type, List<Guid>> and store this in the users session. I would also probably make this be a custom type that delegates the dictionary and have a factory method that works similar to
public Guid GeneratePageIdentifier(Page thepage)
{
var guid = Guid.New();
if(_dictionary[thepage.GetType()] == null)
_dictionary[thepage.GetType()] = new List<Guid> { guid };
else
((List<Guid>)_dictionary[thepage.GetType()]).Add(guid);
return guid;
}
Then embed the guid that's returned from that method on the VIewState of the page. On your page methods that execute actions that you need to validate which page it is you would be able to validate that guid is inside the collection do something. You might also want to implement a custom a type with a guid property to enscapulate more information about why you're doing this or what you need for it to be meaningful.
The Viewstate on each page will be different, maybe you can use some kind of unique identifier created on every page loaded?
It is by default not possible due to the stateless nature of the web, but you could add a "page identifier" that gets generated with each opened page and transmitted for every action.
I'd recommend that you refactor the application in a way that those mixups can't happen, no matter from which page/tab/window the request originates.
As Mark Redman said, you can use Viewstate + Session in order to store values specific to the page.
ViewState is good for storing the key (string), Session for storing whatever type of complex objects.
Use the ViewState or a hidden field in order to load at the first call a GUID.
public string PageUid
{
get
{
if (ViewState["UID"] == null)
ViewState.Add("UID", Guid.NewGuid().ToString());
return ViewState["UID"].ToString();
}
}
Then use the session to get/set your values using this key:
string MyPagesessionVariable
{
get
{
if (Session["MYVAR" + PageUid] == null)
{
Session["MYVAR" + PageUid] = "VALUE NOT SHARED WITH OTHER TABS/WINDOWS";
}
return Session["MYVAR" + PageUid];
}
set
{
Session["MYVAR" + PageUid] = value;
}
}
Let's say I have a list of categories for navigation on a web app. Rather than selecting from the database for every user, should I add a function call in the application_onStart of the global.asax to fetch that data into an array or collection that is re-used over and over. If my data does not change at all - (Edit - very often), would this be the best way?
You can store the list items in the Application object. You are right about the application_onStart(), simply call a method that will read your database and load the data to the Application object.
In Global.asax
public class Global : System.Web.HttpApplication
{
// The key to use in the rest of the web site to retrieve the list
public const string ListItemKey = "MyListItemKey";
// a class to hold your actual values. This can be use with databinding
public class NameValuePair
{
public string Name{get;set;}
public string Value{get;set;}
public NameValuePair(string Name, string Value)
{
this.Name = Name;
this.Value = Value;
}
}
protected void Application_Start(object sender, EventArgs e)
{
InitializeApplicationVariables();
}
protected void InitializeApplicationVariables()
{
List<NameValuePair> listItems = new List<NameValuePair>();
// replace the following code with your data access code and fill in the collection
listItems.Add( new NameValuePair("Item1", "1"));
listItems.Add( new NameValuePair("Item2", "2"));
listItems.Add( new NameValuePair("Item3", "3"));
// load it in the application object
Application[ListItemKey] = listItems;
}
}
Now you can access your list in the rest of the project. For example, in default.aspx to load the values in a DropDownList:
<asp:DropDownList runat="server" ID="ddList" DataTextField="Name" DataValueField="Value"></asp:DropDownList>
And in the code-behind file:
protected override void OnPreInit(EventArgs e)
{
ddList.DataSource = Application[Global.ListItemKey];
ddList.DataBind();
base.OnPreInit(e);
}
Premature optimization is evil. That being a given, if you are having performance problems in your application and you have "static" information that you want to display to your users you can definitely load that data once into an array and store it in the Application Object. You want to be careful and balance memory usage with optimization.
The problem you run into then is changing the database stored info and not having it update the cached version. You would probably want to have some kind of last changed date in the database that you store in the state along with the cached data. That way you can query for the greatest changed time and compare it. If it's newer than your cached date then you dump it and reload.
If it never changes, it probably doesn't need to be in the database.
If there isn't much data, you might put it in the web.config, or as en Enum in your code.
Fetching all may be expensive. Try lazy init, fetch only request data and then store it in the cache variable.
In an application variable.
Remember that an application variable can contain an object in .Net, so you can instantiate the object in the global.asax and then use it directly in the code.
Since application variables are in-memory they are very quick (vs having to call a database)
For example:
// Create and load the profile object
x_siteprofile thisprofile = new x_siteprofile(Server.MapPath(String.Concat(config.Path, "templates/")));
Application.Add("SiteProfileX", thisprofile);
I would store the data in the Application Cache (Cache object). And I wouldn't preload it, I would load it the first time it is requested. What is nice about the Cache is that ASP.NET will manage it including giving you options for expiring the cache entry after file changes, a time period, etc. And since the items are kept in memory, the objects don't get serialized/deserialized so usage is very fast.
Usage is straightforward. There are Get and Add methods on the Cache object to retrieve and add items to the cache respectively.
I use a static collection as a private with a public static property that either loads or gets it from the database.
Additionally you can add a static datetime that gets set when it gets loaded and if you call for it, past a certain amount of time, clear the static collection and requery it.
Caching is the way to go. And if your into design patterns, take a look at the singleton.
Overall however I'm not sure I'd be worried about it until you notice performance degradation.