I have been doing a lot of document library work lately and in my work I had a need to create a series of subfolders in the document library using a particular custom content type. This meant I was going to need to write a little code. I decided to keep the list of folders i needed to create in an XML document since this could easily represent the hierarchy I needed. I also need to set some properties on the content type as well as implement security on a per-folder level.
Well thinking about how my XML document was going to work out I started dreading navigating the XML document using the traditional .NET APIs. Let's face it. They are not fun to use. Looking for a better alternative, I decided to use LINQ to XML. I had heard about it but had not read anything on it yet, so I decided to look into it more. LINQ to XML gives you the power to get IntelliSense into your XML document using anonymous types. This turned out to make things a ton easier. To get started I used an XML document similar to the one below. My final version had some additional elements in it for my security settings, but I omited those for this example.
<Folder Name="Folder 1" ParentFolderType="" FolderType="">
<Folder Name="Subfolder 1" ParentFolderType="Blah" FolderType="Blah"></Folder>
<Folder Name="Subfolder 2" ParentFolderType="Blah" FolderType="Blah"></Folder>
<Folder Name="Folder 2" ParentFolderType="" FolderType=""></Folder>
<Folder Name="Folder 3" ParentFolderType="" FolderType=""></Folder>
This document is pretty simple. In this case I have a couple of folders with some nested subfolders. I also have some attributes that I was using to set custom properties on the content type. First, I am going to open up my XML file using the XDocument class. It has a constructor which takes a path to a file or URL among other things. I then use the Root property of the XDocument and call .Elements with a parameter of Folders. It naturally matches up all elements named Folder. Elements will return all elements of a given node. If you wanted all elements in the entire tree that matched you could call Descendants.
The next part of the LINQ query creates a new anonymous type. In my type, I copy over the values of a few attributes as well as get all child elemnts named Folder to get a list of subfolders. This is what I use to do recursion. Lastly I assign RoleMappings, but first I check to see if the value exists first using Any(). That along with the use of ? and : are the proper way to check for nulls. Also of note I am using the new var type to receive a collection of the anonymous type (again don't confuse these with the old VB6 variants). While debugging, you can see that this is actually an IEnumberable<XElement>.
// get the xml document from the feature folder
XDocument documentLibraryXml = XDocument.Load(folderStructureFilename);
// iterate through the root folder elements
var folders = from folder in documentLibraryXml.Root.Elements("Folder")
Name = folder.Attribute("Name").Value,
FolderType = folder.Attribute("FolderType").Value,
ParentFolderType = folder.Attribute("ParentFolderType").Value,
SubFolders = folder.Descendants("Folder"),
RoleMappings = folder.Elements("Security").Any() ? folder.Element("Security").Elements("RoleMapping") : null
At this point I have a nice anonymous type which provides full IntelliSense access to my XML. Now I just need to iterate on it. Inside my foreach, I call my methods to create my new SPFolder, set permissions, and then recursively call a method to create the subfolders. Things like folder.Name, folder.SubFolders all show up via IntelliSense.
So far I can say LINQ to XML has saved me a ton of time. This is a brief overview, ScottGu has a bit of info on LINQ to XML to help get you started. I think it is really powerful for any scenario where you are going to be working with a lot of XML. I didn't include any of the details about how I set security or created the folders themselves, but I have posted info on some of that in the past. If you need more info, feel free to contact me.
Read the complete post at http://www.dotnettipoftheday.com/blog.aspx?id=403