How to: Write a value into the property bag
Posted
Monday, May 12, 2008 2:51 PM
by
CoreyRoth
I have ran into a few people that didn't even know what the property bag is on the SPWeb object, so I figured that would be a good topic for a post. The property bag exposed by the Properties and AllProperties collections on the SPWeb object gives the developer a way to cache information on a site. It can be compared to ASP.NET's cache object but the values are shared across the farm and will persist through server reboots. Unfortunately, using the property bag isn't as simple as you would think. In fact to get it working in all situations, it requires a number of settings to allow it to work. It's probably enough to scare a lot of developers away from it.
Before we start, let me talk about the differences between Properties and AllProperties. Properties is a collection of type SPPropertyBag. It is sort of considered deprecated. You can use it to add new items to the property bag, but it will only return a subset of everything in the property bag when retrieving data. Why is that? I have no idea. So instead, Microsoft later implemented the AllProperties Hashtable which contains everything in the property bag.
Here is what the start of our code block looks like to write to the property bag. This assumes it is inside a using block to get access to an SPWeb.
// unsafe updates are required to be able to write to the property bag
currentWeb.AllowUnsafeUpdates = true;
// you must check to see if the collection has a value in the assigned key already
if (!currentWeb.AllProperties.ContainsKey(key))
currentWeb.Properties.Add(key, myValue);
else
currentWeb.AllProperties[key] = myValue;
// update the properties
currentWeb.Properties.Update();
currentWeb.AllowUnsafeUpdates = false;
There are a number of things to note here. First, it requires AllowUnsafeUpdates to be set to true. Secondly notice that the AllProperties object actually has a method to check to see if a key exists. This is probably the only collection in SharePoint that lets you check to see if something exists without throwing an exception. This is of course because, the type is Hashtable and not a custom SharePoint collection. If the key does not already exists, you add it to the Properties object (not AllProperties). If a value already exists, you store the value using an indexer with the AllProperties object. You might be able to get this to work other ways, but after lots of attempts, this is what works for me. After the value is stored, you have to call Update on the Properties object if you want it to actually store the values. If you have multiple values to store, you only need to call this once. Lastly, be sure to remember and turn off AllowUnsafeUpdates.
The above code works great...as long as the account running it is an administrator. If you need this code to work when executed by a regular user, there is an additional step. You have to use SPSecurity.RunWithElevatedPrivileges. I am assuming you are already familiar with how this method works.
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite currentSiteCollection = new SPSite("http://myserver/mysite"))
{
using (SPWeb currentWeb = currentSiteCollection.OpenWeb())
{
// unsafe updates are required to be able to write to the property bag
currentWeb.AllowUnsafeUpdates = true;
// you must check to see if the collection has a value in the assigned key already
if (!currentWeb.AllProperties.ContainsKey(key))
currentWeb.Properties.Add(key, value.Value.ToString());
else
currentWeb.AllProperties[key] = value;
// update the properties
currentWeb.Properties.Update();
currentWeb.AllowUnsafeUpdates = false;
}
}
});
There is a complete example. One thing to remember is that the method inside RunWithElevatedPrivileges requires that you create a new SPSite object for it to work. Do not pass it a reference from one that already exists. Create a new one by passing it a URL. Since we need the SPWeb object in this case, I have things nested like I have mentioned in my post in the past.
Luckily, reading from the property bag is not as difficult. Just check to see if the key exists and read from it using AllProperties.
if (currentSite.AllProperties.ContainsKey("MyItem"))
myItem = currentSite.AllProperties["MyItem"].ToString();
This may seem like a lot to store and retrieve values in the property bag, but it's pretty easy to wraps this code up into a class.