HttpRuntime Close does not remove items from the Cache as advertised - asp.net

I've created my own cache manager for a web site I'm developing and I was looking to find the best way to clear the cache under certain circumstances.
I found many articles saying the proper way to clear the cache is to call HttpRuntime.Close()
However, in my unit tests setup I call the encapsulated function HttpRuntime.Close() and the cache is NOT being cleared out.
I expected it to perform something similar to
foreach (DictionaryEntry cacheItem in HttpRuntime.Cache)
{
HttpRuntime.Cache.Remove(cacheItem.Key.ToString());
}
The foreach loop works great in my encapsulated function, but the Close() never works right.
Am I misunderstanding the purpose of HttpRuntime.Close() or is there something more sinister going on here?

Don't use Close, it does more than the docs say. And the docs also say not to use it while processing normal requests...
This is the reflected source of Close():
[SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
public static void Close() {
if (_theRuntime.InitiateShutdownOnce()) {
SetShutdownReason(ApplicationShutdownReason.HttpRuntimeClose, "HttpRuntime.Close is called");
if (HostingEnvironment.IsHosted) {
HostingEnvironment.InitiateShutdown();
} else {
_theRuntime.Dispose();
}
}
}
Also, you cannot iterate over a collection and remove items from it at the same time, as this renders the enumeration invalid.
So, try this instead, which doesn't change what it loops over:
List<string> toRemove = new List<string>();
foreach (DictionaryEntry cacheItem in HttpRuntime.Cache) {
toRemove.Add(cacheItem.Key.ToString());
}
foreach (string key in toRemove) {
HttpRuntime.Cache.Remove(key);
}
That being said, really, you should try to use cache dependencies to have the invalid cache entries cleared automatically for you, and then all this becomes unnecessary.

I understand the issue with enumeration but for some reason the Cache doesn't seem to have a problem removing an item while walking through the list.
If you drill down to the detail implementation, you will find the Enumerator is created by CacheSingle.CreateEnumerator, a new Hashtable instance is created for enumeration.
That's why you can do the remove in a foreach loop.

you could simply implement your own Cache class, check the below one:
public sealed class YourCache<T>
{
private Dictionary<string, T> _dictionary = new Dictionary<string, T>();
private YourCache()
{
}
public static YourCache<T> Current
{
get
{
string key = "YourCache|" + typeof(T).FullName;
YourCache<T> current = HttpContext.Current.Cache[key] as YourCache<T>;
if (current == null)
{
current = new YourCache<T>();
HttpContext.Current.Cache[key] = current;
}
return current;
}
}
public T Get(string key, T defaultValue)
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentNullException("key should not be NULL");
T value;
if (_dictionary.TryGetValue(key, out value))
return value;
return defaultValue;
}
public void Set(string key, T value)
{
if (key == null)
throw new ArgumentNullException("key");
_dictionary[key] = value;
}
public void Clear()
{
_dictionary.Clear();
}
}
you could call items from cache or even clear them using the following:
// put something in this intermediate cache
YourCache<ClassObject>.Current.Set("myKey", myObj);
// clear this cache
YourCache<ClassObject>.Current.Clear();

Related

ASP.NET, thread and connection-string stored in Session

My application can connect with multiple data bases (every data base have the same schema), I store the current DB, selected by user, in Session and encapsule access using a static property like:
public class DataBase
{
public static string CurrentDB
{
get
{
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
HttpContext.Current.Session["CurrentDB"] = value;
}
}
}
Other pieces of code access the static CurrentDB to determine what DB use.
Some actions start background process in a thread and it need access the CurrentDB to do some stuff. I'm thinking using something like this:
[ThreadStatic]
private static string _threadSafeCurrentDB;
public static string CurrentDB
{
get
{
if (HttpContext.Current == null)
return _threadSafeCurrentDB;
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
if (HttpContext.Current == null)
_threadSafeCurrentDB = value;
else
HttpContext.Current.Session["CurrentDB"] = value;
}
}
And start thread like:
public class MyThread
{
private string _currentDB;
private thread _thread;
public MyThread (string currentDB)
{
_currentDB = currentDB;
_thread = new Thread(DoWork);
}
public DoWork ()
{
DataBase.CurrentDB = _currentDB;
... //Do the work
}
}
This is a bad practice?
Actually, I think you should be able to determine which thread uses which database, so I would create a class inherited from Thread, but aware of the database it uses. It should have a getDB() method, so, if you need a new Thread which will use the same database as used in another specific Thread, you can use it. You should be able to setDB(db) of a Thread as well.
In the session you are using a current DB approach, which assumes that there is a single current DB. If this assumption describes the truth, then you can leave it as it is and update it whenever a new current DB is being used. If you have to use several databases in the same time, then you might want to have a Dictionary of databases, where the Value would be the DB and the Key would be some kind of code which would have a sematic meaning which you could use to be able to determine which instance is needed where.

ASP.net cache access causing Collection Modified exception in foreach loop

Ok first things first. This is some exception information given by the support team. I know the line and code where it happens. It happens in a FirstOrDefault call over a dictionary obtained from cache.
1) Exception Information
*********************************************
Exception Type: System.InvalidOperationException
Message: Collection was modified; enumeration operation may not execute.
Data: System.Collections.ListDictionaryInternal
Now I wanted to simulate the problem and I could do it in a simple ASP.net application.
My page has 2 Buttons - Button_Process and Button_Add
The code behind is as follows:
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
var data = Cache["key"];
if (data == null)
{
var dict = new Dictionary<int, string>();
for (int i = 0; i < 10; i++)
{
dict.Add(i, "i");
}
Cache["key"] = dict;
}
}
}
protected void ButtonProcess_Click(object sender, EventArgs e)
{
var data = Cache["key"] as Dictionary<int, string>;
if (data != null)
{
foreach (var d in data.Values) //In actual code there is FirstOrDefault here
{
Thread.Sleep(1000);
if (d.Contains("5"))
{
//some operation
}
}
}
}
protected void Button2_Click(object sender, EventArgs e)
{
var data = Cache["key"] as Dictionary<int, string>;
if (data != null)
{
data.Add(new Random().Next(), "101");
Cache["key"] = data;
}
}
}
Now assume there are 2 requests:
Request 1 - Someone clicks on button_Process and some operation on cache object is taking place
Request 2 - Someone clicks on button_Add and the first person gets an exception - collection modified blah blah
I understand the problem - it is happening because we are accessing same bit of memory. I have 2 solutions in my mind:
1. I use a for loop instead of for each (to replace FirstOrDefault in actual code) - I dunno how efficient this operation will be after I make the changes. - I don't ever delete any item from cache so I was thinking of this solution
2. I put some lock over cache object or something on those lines - but I dunno exactly where and how should I lock this object.
Please help me with this. I am not able to figure out an efficient solution. What is the best way to handle such situations?
This happens because you're working directly with object, locating in cache. Good practise, to avoid those exceptions and other wierd behavior (when you accidentally modify cache object) is working with copy of cache data. And there are several ways of achieving it, like doing clone or some kind of deep copy. What i prefer is keeping objects in cache serialized (any kind you like - json/xml/binary or w/e else), since (de)serialization makes a deep copy of your object. Following small code snippet will clarify things:
public static class CacheManager
{
private static readonly Cache MyCache = HttpRuntime.Cache;
public static void Put<T>(T data, string key)
{
MyCache.Insert(key, Serialize(data));
}
public static T Get<T>(string key)
{
var data = MyCache.Get(key) as string;
if (data != null)
return Deserialize<T>(data);
return default(T);
}
private static string Serialize(object data)
{
//this is Newtonsoft.Json serializer, but you can use the one you like
return JsonConvert.SerializeObject(data);
}
private static T Deserialize<T>(string data)
{
return JsonConvert.DeserializeObject<T>(data);
}
}
And usage:
var myObj = new Dictionary<int, int>();
CacheManager.Put(myObj, "myObj");
//...
var anotherObj = CacheManager.Get<Dictionary<int, int>>("myObj");
Check Task Parallel Library for .NET 3.5. It has Concurrent Collections such as ConcurrentStack, ConcurentQueue and ConcurrentDictionary.
http://www.nuget.org/packages/TaskParallelLibrary
The problem is that the cache object is global for appdomain and the data stored in are shared between all request.
The only solution to this problem is to activate a lock when you want to access to the collection and then release the lock (https://msdn.microsoft.com/en-us/library/vstudio/c5kehkcz%28v=vs.100%29.aspx).
(sorry form my bad english)

ASP.Net AppFabric Cache missing Flush/Clear and Count/GetCount methods?

I am trying to convert a solution using EntLib into using AppFabric caching. By help of a few extension methods this is a fairly pain-free process.
Extension methods I use:
public static bool Contains(this DataCache dataCache, string key)
{
return dataCache.Get(key) != null;
}
public static object GetData(this DataCache dataCache, string key)
{
return dataCache.Get(key);
}
But there are two features of EntLib I find difficult to convert. Namely "Count" (counting number of keys in cache) and "Flush" (removing all data from cache). Both could be solved if I could iterate the keys in cache.
There is a method called ClearRegion(string region), but that required me to specify a region name on all Get/Put/Add-methods I use, which would require some manual error-prone work.
Is there any way to get a list of keys in cache?
Is there a default region name I can use?
How can I flush the cache when I haven't used a region name?
See my previous answer for my speculation as to how the cache works internally when you don't specify a region, and how you can get the count of objects that aren't in a named region.
We can build a Flush method using the same technique:
public void Flush (this DataCache cache)
{
foreach (string regionName in cache.GetSystemRegions())
{
cache.ClearRegion(regionName)
}
}
As I said there, I think named regions are probably the way to go - it seems to me that using them solves more problems than it creates.
If anyone will have problems in future (like me) - here is the full code for clearing cache.
private static DataCacheFactory _factory;
private const String serverName = "<machineName>";
private const String cacheName = "<cacheName>";
static void Main(string[] args)
{
Dictionary<String, Int32> cacheHostsAndPorts = new Dictionary<String, Int32> { { serverName, 22233 } };
Initialize(cacheHostsAndPorts);
DataCache cache = _factory.GetCache(cacheName);
FlushCache(cache);
Console.WriteLine("Done");
Console.ReadLine();
}
private static void FlushCache(DataCache cache)
{
foreach (string regionName in cache.GetSystemRegions())
{
cache.ClearRegion(regionName);
}
}
public static void Initialize(Dictionary<String, Int32> cacheHostsAndPorts)
{
var factoryConfig = new DataCacheFactoryConfiguration
{
Servers = cacheHostsAndPorts.Select(cacheEndpoint => new DataCacheServerEndpoint(cacheEndpoint.Key, cacheEndpoint.Value))
};
_factory = new DataCacheFactory(factoryConfig);
}

ASP.NET Cache and File Dependancies

I want a ASP.NET cache item to be recycled when a specific file is touched, but the following code is not working:
HttpContext.Current.Cache.Insert(
"Key",
SomeObject,
new CacheDependency(Server.MapPath("SomeFile.txt")),
DateTime.MaxValue,
TimeSpan.Zero,
CacheItemPriority.High,
null);
"SomeFile.txt" does not seem to be checked when I'm hitting the cache, and modifying it does not cause this item to be invalidated.
What am I doing wrong?
Problem Solved:
This was a unique and interesting problem, so I'm going to document the cause and solution here as an Answer, for future searchers.
Something I left out in my question was that this cache insertion was happening in a service class implementing the singleton pattern.
In a nutshell:
public class Service
{
private static readonly Service _Instance = new Service();
static Service () { }
private Service () { }
public static Service Instance
{
get { return _Instance; }
}
// The expensive data that this service exposes
private someObject _data = null;
public someObject Data
{
get
{
if (_data == null)
loadData();
return _data;
}
}
private void loadData()
{
_data = GetFromCache();
if (_data == null)
{
// Get the data from our datasource
_data = ExpensiveDataSourceGet();
// Insert into Cache
HttpContext.Current.Cache.Insert(etc);
}
}
}
It may be obvious to some, but the culprit here is lazy loading within the singleton pattern. I was so caught up thinking that the cache wasn't being invalidated, that I forgot that the state of the singleton would be persisted for as long as the worker process was alive.
Cache.Insert has an overload that allows you to specify a event handler for when the cache item is removed, my first test was to create a dummy handler and set a breakpoint within it. Once I saw that the cache was being cleared, I realized that "_data" was not being reset to null, so the next request to the singleton loaded the lazy loaded value.
In a sense, I was double caching, though the singleton cache was very short lived, but long enough to be annoying.
The solution?
HttpContext.Current.Cache.Insert(
"Key",
SomeObject,
new CacheDependency(Server.MapPath("SomeFile.txt")),
DateTime.MaxValue,
TimeSpan.Zero,
CacheItemPriority.High,
delegate(string key, object value, CacheItemRemovedReason reason)
{
_data = null;
}
);
When the cache is cleared, the state within the singleton must also be cleared...problem solved.
Lesson learned here? Don't put state in a singleton.
Is ASP.NET running under an account with the proper permissions for the file specified in the CacheDependency? If not, then this might be one reason why the CacheDependency is not working properly.
I think you'll need to specify a path:
var d = new CacheDependency(Server.MapPath("SomeFile.txt"));
Prepend with ~\App_Data as needed.
Your code looks fine to me. However, beyond this snippet, anything could be going on.
Are you re-inserting on every postback by any chance?
Try making your cache dependency a class field, and checking it on every postback. Modify the file in between and see if it ever registers as "Changed". e.g.:
public partial class _Default : System.Web.UI.Page
{
CacheDependency dep;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
dep = new CacheDependency(Server.MapPath("SomeFile.txt"));
HttpContext.Current.Cache.Insert(
"Key",
new Object(),
dep,
DateTime.MaxValue,
TimeSpan.Zero, CacheItemPriority.High, null);
}
if (dep.HasChanged)
Response.Write("changed!");
else
Response.Write("no change :("); }}
The only way I am able to reproduce this behavior is if the path provided to the constructor of CacheDependency does not exist. The CacheDependency will not throw an exception if the path doesn't exist, so it can be a little misleading.

How would I serialize a LINQ-to-SQL lazy list

I have a linq query and I am trying to put that in to a serializable object for a distributed caching (Velocity) but its failing due to a LINQ-to-SQL lazy list
like so
return from b in _datacontext.MemberBlogs
let cats = GetBlogCategories(b.MemberBlogID)
select new MemberBlogs
{
MemberBlogID = b.MemberBlogID,
MemberID = b.MemberID,
BlogTitle = b.BlogTitle,
BlogURL = b.BlogURL,
BlogUsername = b.BlogUsername,
BlogPassword = b.BlogPassword,
Categories = new LazyList<MemberBlogCategories>(cats)
};
LazyList is the same class Rob Conery uses in his MVC storefront...
all three classes are marked serializable (MemberBlogs,MemberBlogCategories,LazyList... any ideas?
If you are putting it in a distributed cache you will need to avoid the LazyList altogether. You can then call .ToList() around the whole LINQ statement as in:
(from x select new MemberBlogs).ToList()
This should then be cachable because it forces the queries to be evaluated.
I'm just guessing, but I'd say the problem is that it is serializing the query instead of the results; I don't know what the implementation of the LazyList looks like, but you can probably add an OnSerializing method that actually executes the query prior to serializing it; Something like:
[OnSerializing]
private void ExecuteLinqQuery(StreamingContext context)
{
if (!SomethingThatIndicatesThisLinqQueryHasNotBeenExecuted)
LinqVariable.ToList()
}
This way you get to keep the Lazy Load (for anything that doesn't go into your cache), but then also if it does hit the cache, it'll execute the linq query and cache the results.
If you're caching it why are you using a lazy list? Don't use a lazy list, use caching, and the problem goes away.
I know this is an old post but I had the same issue as I wanted to execute my LazyList and put them into the AppFabric Cache. I ended up putting some custom serialization logic into the LazyList type.
The first part now looks like this:
public class LazyList<T> : IList<T>, ISerializable
{
public LazyList()
{
this.query = new List<T>().AsQueryable();
}
public LazyList(SerializationInfo info, StreamingContext context)
{
try {
this.inner = (List<T>)info.GetValue("InnerList", typeof(List<T>));
}
catch (Exception ex)
{
this.inner = null;
}
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (this.inner != null)
info.AddValue("InnerList", this.inner.ToList());
}
public LazyList(IQueryable<T> query)
{
this.query = query;
}
public LazyList(List<T> l)
{
inner = l;
}
}

Resources