- When you deactivate your Feature, it keeps some Site Columns and deletes other ones.
- You notice some of the Site Columns and Content Types are still hanging around in the lists you applied them to, even after you deactivated your Feature.
- You try doing an in-place upgrade of your Feature, and nothing happens; everything looks exactly like before. The columns in your Content Type haven’t changed or anything.
This can be very confusing and aggravating, and this is a scenario I’ve walked through with several clients. Why does this happen? And what should be a proper approach for upgrading a Feature that includes Site Columns or Content Types?
Site Columns and Content Types in List
When you add a Content Type, or a Site Column, to a list, SharePoint actually makes a copy of that item in the list. However, the copy seems to retain the same GUID/Identifier as the original item. This allows several things:
- This acts as a “delta” of sorts; when you add a Site Column to a list you can say, “I want to use this Site Column, except in this list, I want it to display 7 lines of text instead of 6,” etc. By having a copy, SharePoint can modify the properties of your Site Column or Content Type in your list, while keeping it separate from the “original” version in your Site Collection root. The same applies for Content Types: what if you wanted to attach a Workflow to your Content Type, just for this list? It needs a copy of that Content Type in that list.
- Have you noticed how, when you’re modifying a Site Column or a Content Type, you have that little check box that asks if you want to push your change down to everything that uses it? You have two choices: either you will, or you won’t. If you don’t want to apply your changes to existing items, how would SharePoint handle that? Well, it essentially leaves the existing copies of the Site Columns that are applies to lists alone, and just uses your changes when creating new copies of that Site Column in the future. By using a copy of the Site Column in lists, it’s possible to have multiple definitions for a Site Column being used at once.
- If you decide you do want to apply your changes to everything that uses that item, how does SharePoint do it? Essentially, it looks for any Site Column or Content Type in the site collection hierarchy that is using the same GUID (or ID, the case of a Content Type). That provides a sort of implicit connection between the two. (Think of it like siblings: they’re different people, but they have the same last name and the same parents.)
Deactivating Your Feature
What happens when you deactivate your feature? Typically, if a Site Column or Content Type is not being referenced in any list or anywhere else in the site, SharePoint will remove it from the Site Collection. However, if it’s being used, SharePoint will often delete the ones that aren’t be used, but leave the ones that are. On occasion, deactivating the Site Columns will actually remove the items. However, the copies of the assets will remain in the underlying lists. This might seem confusing at first, but it’s because the lists contain copies. (Using our analogy again, if siblings’ parents go on vacation, the siblings still retain their last name and their sibling relationship, whether the parents are around or not.)
Activating Your Feature
If we think about it in reverse, what happens when we activate a feature? SharePoint uses the CAML markup we used to define our Site Column or Content Type, and it puts it into the database. This is a one-type event. What do I mean by that? If you create a Master Page via a Feature, you do put a reference to that Master Page in SharePoint, so that you get your new Master Page showing up in the Master Page Gallery for your site. However, all that reference does is point to the file system. Any time SharePoint to use that Master Page, it says, “Hmm, looks like I should look at the file on the file system in the Features folder instead of a file stored in my database.” However, with Content Types, once the Feature has been activated, SharePoint uses the CAML to add the Site Column or Content Type to its database, and it never uses that asset on the file system again. Basically, it’s the same as using a Feature Receiver that executes code once when the Feature is activated, and never again. (Please note: I have no inside information from Microsoft about this! This is just what I’ve inferred based on the behavior of these assets.)
Creating Site Columns and Content Types Using a Feature Receiver
So this begs the question, why not just use a Feature Receiver that executes code which instantiates these things into SharePoint, and just forget the CAML markup altogether? Well, remember what we were saying about the relationship between GUIDs in the lists and their parent items in the Site Collection? It’s often important that if you’re activating and deactivating a Feature in a Site Collection that the GUIDs remain the same, so-as to prevent orphaning those assets in the child lists. Furthermore, if you’re activating the Feature in multiple Site Collections, you probably want continuity between the assets in different Site Collections.
When you create a new Site Column via code, using the SiteColumnCollection.Add() method, SharePoint automatically creates a GUID for that Site Column. The only way you could hard-code that GUID would be to create the Site Column by passing in the entire XML definition. Well, if you’re going to do that, you might as well just use the CAML way of doing things. And what about Content Types? I looked, and I don’t even see a way of specifying the unique identifier for a Content Type. It’s true, you can pass in the parent Content Type, and the way SharePoint constructs ID’s for Content Types, its ID includes the ID of its parent. However, I have found of no way of passing in what the newly-created Content Types unique part of the ID should be. (If you’re confused about what I’m talking about when referring to Content Type IDs, take a look here: http://msdn.microsoft.com/en-us/library/aa543822.aspx.)
Based on all these factors, my suggestion is this:
- Create two Features: one for the original markup and one for making changes. (Or you can put them in the same Feature; I just want to differentiate between where you do what.)
- The original Feature should contain the CAML for Site Columns and Content Types. This ensures the IDs have been assigned ahead of type and remain constant.
- If you want to update a Site Column by changing nearly anything about it except its Field type, do it using a Feature Receiver. By doing this, you can call the Update method and pass in a boolean indicating if you want all the existing assets in the site that inherit from this to update to, (something you couldn’t do via the CAML.)
- You can also add an existing Site Column (that you provisioned via the CAML feature) to an existing Content Type (that was provisioned via the CAML feature). This is helpful if the Column was not part of that Content Type before, etc.
- In a scenario like the one I just mentioned in the last bullet point, it’s necessary to deactivate and reactive the CAML feature (to provision the new assets) before calling your Feature Receiver. What will this mean for the site? Since all the Site Columns and Content Types in the lists in the site are using the same ID’s as the ones provisioned in the Site Collection root, removing its parent from the Site Collection won’t change that. It might leave it orphaned temporarily, (i.e. there will be no relationship between that item and an item in the Site Collection root, but it will function the same way it always has, since it’s really a fully-functioning copy of the original item) until you reactivate the Feature that puts the item back in the Site Collection. It’s like the parents are going on vacation when you deactivate the Feature, and are coming back home when you activate the Feature again.
You have a choice when it comes to how you maintain the CAML and the Feature Receiver, since you have two scenarios: existing Site Collections and new ones.
- You could make a policy that every time you write code in your Feature Receiver to update a Site Column or Content Type, you have to make the change in your CAML as well. That would mean that every time you activated the CAML Feature in a “fresh” Site Collection, the CAML would be up-to-date and accurate; there would be no need to run the “updater” feature. (In your Feature Receiver, you should make sure you do some extra checking to make sure a Site Column doesn’t already belong to a Content Type before adding it, etc. in case that change is already in place before the code executes.) This approach means you only have to execute one Feature when creating a new Site Collection, but it also means you’re maintaining changes in two places: in your Feature Receiver for making changes to existing sites, and in your CAML for new sites. It’s a cleaner approach, but also contains an element of redundancy, which always leaves room for human error.
- The other approach is to simply assume that every time the base CAML feature is activated, you’re always going to execute the Feature Receiver. This approach says the only time you’d change the CAML is to add a new Site Column or new Content Type; otherwise, all the changes happen in the Feature Receiver. This approach reduces redundancy, but also means your Feature Receiver code could get quite large with all your changes over time, and it could leave your CAML as very much “legacy” over time.
Deleting Site Columns and Content Types
If you’ve noticed, I haven’t written much about deleting Content Types and Site Columns. That’s because deleting a Column could easily mean deleting the content stored in that Column. As we’ve already noted, deleting a Site Column won’t delete the copies of that Site Column from lists in the site that use it. If you want to delete a Column from a list, you’ll need to write code that will explicitly allow this, since it means you’ll also lose any content that was stored in that column in the list. You’ll have to explicitly set a property called AllowDeletion on the SPField object you’re trying to delete.
In terms of deleting a Content Type from a site, the same thing goes, in terms of deleting a Content Type from the Site Collection root won’t necessarily remove the copies of that Content Type in lists. Also, removing a Content Type from a List after the Content Type has been removed from the Site Collection won’t necessarily remove the Columns from the list, if it now thinks those Columns are just part of its own definition, and not a Content Type’s columns.
In my experience, this is where things can get hairy, since you’re dealing with copies without parents, etc. It’s imperative that you do things in the right order, and practice in a development environment before trying to execute this sort of scenario in production.
Dealing with the issue of updating Site Columns and Content Types in SharePoint can be a tricky issue when using Features, but hopefully this will provide you some guidance about how to carry this out in your own environment.
Update December 16, 2011: SharePoint Foundation 2010 provides the ability to version your Features now, and provide upgrade instructions to your Features when upgrading them to newer versions. In these instructions, you can now tell your Feature that new columns need to be added to existing Content Types when the upgrade happens. This cuts down on a lot of issues we dealt with in Windows SharePoint Services. You can read more about upgrading features here: Upgrading Features.