The Other Projects
Now that our feature is wired up, we can start building it out. As you implement more and more features of your site, you are constantly updating your script; you are writing deployment code in conjunction with application code. If you need to provision a list, add the proper provisioning logic to your script before any code that consumes it is written. That way, there's no chance of having any missing dependencies.
Recall the introduction on Deployment Driven Design, when we started with our web part, and then looked at all the prerequisites and postrequisites we would need. The idea is to continually go through a similar (yet simpler) mental excise so that with each new component that's rolled out, we have the means to deploy it and its dependences in our back pocket.
It's really cool to watch your script grow and change as you work your way through your project. You'll find yourself constantly refactoring things and reorganizing things and doing things better. However, the script isn't the only piece of All Code; there are a few other supporting components required to keep the gears well-greased and spinning. Before we continue on with our feature, let's take a look at these supporting characters.
The Dependencies Project
This is something that I'm surprised Visual Studio and/or TFS doesn't do better. Where do you put your externally-referenced DLLs so that the solution compiles for any new team member doing their first get? Even though there's no explicit best practice here, architects from different companies I've worked with have all done it the same way I've ever done it. I wonder if this is one of the community-home-grown solutions that Microsoft will adopt.
I should point out that NuGet is close. It's a great way to include fully-baked dependencies into your project across development machines. Example of these are the latest jQuery bits or newest version of Elmah. However, for just loose DLLs or other goodies that haven't been explicitly packaged up and published to NuGet, I still prefer the following technique to quickly keep everything you need for your SharePoint site nearby.
The first thing to do is create an empty ASP.NET web project in Visual Studio. Go ahead and delete the web.config file that comes along with it. I usually name the project "Dependencies" or "<name of solution>.Dependencies" depending on the current convention. Feel free to create any folders underneath it pursuant to your level of OCD organization. Then follow this procedure to house all of your external DLLs:
Create the project (only one time).
Adding a new project
After the project is created, go to the
desired folder, (or root of the project) right click, and select "Add->"
then "Existing Item..."
Adding an existing item
Navigate to where the DLL is on your
hard drive (for example, 15\ISAPI for the SharePoint DLLs). If you downloaded
something from CodePlex or anywhere else, you might have to go into the file
properties for the assembly, and click "Unblock" first or it won't compile.
(I'm only using log4net as an example below.)
Viewing the assembly properties
- Visual Studio will copy the file this location on the file system, and add it to your project.
- When you're done, check in the project (optional if you're using TFS).
I do this with the following DLLs at
the start (and add more as needed):
This way, whenever someone gets the latest, all the DLLs will come down on their machine. This sounds pretty straight forward, but like I've been saying, it's amazing how often the needless adventure of scrounging around for DLLs while trying to get a new project from TFS to compile is embarked upon. "Oh, just download it from Microsoft's site..." Is NOT a valid solution to this problem!
But this only gets us half way there. Now that we're welding these assemblies into our Visual Studio solution, we need to reference them properly so that the dependent projects compile. Even though it's not nearly as bad a problem, I hate having to expand the "References" folder of a project, delete everything with a yellow exclamation mark, and re-reference the DLLs proper.
In order to not force people to have to do this, when referencing assemblies in your dependencies project, use the "Browse..." option in the "Add Reference" popup. Then navigate to the DLL on the file system under the Dependencies project. Visual Studio will add the reference and lock it into your project via a relative file path. This way, the dependencies project will force TFS to beam you all the DLLs, and your application projects will happily reference them wherever they are downloaded.
The only downside to this is that you have to hang out while TFS uploads and downloads your assemblies, especially the 25+ MB of ones and zeros the Microsoft.SharePoint v15 assembly weighs in at. It can be argued that this will bloat the TFS database. But it can also be argued that no one cares because storage is cheap and both SQL and TFS can handle this just fine. If you have ten SharePoint projects, this will only take up ~250 MB of space which, in this millennium, is nothing.
This is also a good place to stash any PowerShell scripts, example web.config files, readmes, etc. I don't want to start blurring the line between what should be stored in TFS source control verses what should really live in a TFS-specific SharePoint document library, so don't go too nuts: requirements documents, wireframes, etc. shouldn't really neighbor your code or other more technical development assets.
The Common Project
Every Visual Studio solution should have a separate project to house your common logic. This is a separate tier from the standard three (UI, Services/Business, Data) because it contains code that any of them can call. Whether it's converting a string from Pascal case to common English or provisioning a content type, you should consider refactoring anything that can be modeled as a static class into this project.
We'll add it as a class library project (called "DDD.Common") and delete Class1.cs that comes along for the ride.
Since our SharePoint code lives in the GAC, anything that references it should as well. I like to use the same public key file for all projects in a solution, so I can blindly use the same fully qualified name for all my assemblies.
Copy-and-paste the "key.snk" file from
the SharePoint project into the Common project.
Copying the keyPasting the key
Use this new copy of the file to sign
the Common project and save it.
Using the key
There are two main static classes that belong in the common project for all All Code deployments: Constants and Utilities. The former is where all the hard-coding goodness I've alluded to happens. The latter is a repository where I refactor any logic that is called at least twice from anywhere across the entire application.
It is important that your common project has no "internal" (project) references for two reasons. First of all, this project should be able to be referenced in any .NET solution (within reason of course; SharePoint code won't work too well in a Silverlight application for example) and provide its common functionality to it. This might be challenging if it needs stuff from a dependencies project that would have to follow it around, but with a bit of diligence, you should be able to port this code around very easily.
Secondly, we want to stay clear of circular references. Even though Visual Studio won't actually let it happen, we don't want to waste too much time coding ourselves against this wall. What always happens to me is I'll write some slick UI code, see an opportunity to factor some of it, spend an hour reshaping it in my utility class, kick off a build that fails, and only then find that I had a reference to something else in the UI project, and have to put it all back to avoid the circular reference.
The Constants Class
This is where all hard-coding happens. Recall, however, that hard-coding is a good thing in deployment logic...especially SharePoint deployment logic. Since so many APIs in SharePoint force developers to refer to things via strings (site column names to get at list fields, list titles, URLs to open sub webs, search managed properties, etc.) it's best to bite the bullet only one time and hard-code it statically.
So if we did fat finger something, then at least we only have to go to one place to fix it. But beyond strings, the occurrence of hard-coded Guids (my preference is the pronunciation that rhymes with "squids" rather than the unphonetic "goo-ids") also comes up in SharePoint. Usually it is a total hack / bad practice when developers start hard-coding unique identifiers. But leave it to our favorite little collaboration server to not only bring it up, but to actually provide a fairly compelling case to support it.
You'll see in the code that provisions content types that we can generate these babies (via their SPContentTypeId) with a pre-existing Guid. I discovered this while trying to figure out a problem where SPMetal was working beautifully for me but not at all for a team member of mine. Here's what happened:
- I provisioned a site with All Code, but used string names to create my content types, not Guids.
- I generated my SPMetal objects off of this site, compiled, tested, and checked everything in.
- My team member got the latest, compiled, and provisioned a site of his own.
- He received run time errors from our web parts that consumed SPMetal.
The problem was that the generated content type Ids SharePoint used to create his structure didn't match the hard-coded Guids in the SPMetal designer code that I checked in! So in Visual Studio, I went to "Tools" -> "Create GUID" and copy-and-pasted the result into my constants class as a static Guid. Then I used this to create an SPContentTypeId object, and passed it to an overload of the SPContentType class' constructor.
That way, everything matched up, and our deployment script worked majestically across our team's local development environments. Only in SharePoint can hard-coded globally unique identifiers save the day! I'll go into the details from the above story when we start looking at code (I'm sure you're sick of reading that by now). But the overarching theme here, as it basically has been throughout the book so far, is that bad practices might very well be the best tools for the right jobs.
Structurally, the Constants class isn't really a class; it's a namespace wrapper around a bunch of static classes, each of which canonically encapsulates a logic grouping of constants. For example, consider our site columns. If our project's name is "DDD.Common," then this file will have single namespace declaration of "DDD.Common.Constants" and the static class within that contains the "Title" site column will be "DDD.Common.Constants.SiteColumns.Title." Each column will also be its own static class that exposes its id and other optional fields (display name, internal name, etc.). This convention makes both .NET and PowerShell code happy. Here's an example of how this will look:
Code Listing 3: Constants.cs
- using System;
- using Microsoft.SharePoint;
- using Microsoft.SharePoint.Publishing;
- namespace DDD.Common
- public static class Constants
- public static class SiteColumns
- public class Title
- public const string FieldName = "Title";
- public const string DisplayName = "Title";
- public static readonly Guid Id = SPBuiltInFieldId.Title;
The Utilities Class
I love utilities classes. These are the Swiss Army knives of software development. Not only is it fun to refactor every last reusable line of code into a nice neat little package, but being all static methods in static classes, this code is pure logic. This means that it's not subject to any rules or regulations or any patterns or practices; it's just code that does stuff.
It operates acontextually, almost ethereally. In fact, it's not even necessarily object oriented. It's what I like to call "slutty code" because it can be used and re-used in a lot of different places with very little overhead cost. You'll see a lot of instances of this patternless pattern in All Code: many little methods that work together to form an application/paradigm-specific SharePoint API.
Finally, add a reference to the Common project for the SharePoint project. Although this will allow us to use our Common code, we need to go a step further to ensure the DLL will be included in our WSP upon deployment. For this and every other project reference we have to our SharePoint project, we need to make sure the packaging process includes our assemblies. Here's how:
- Add a project reference from Common to SharePoint normally as you would any other.
- Double click the "Package" node under DDD.Web.
Click the "Advanced" tab at the top.
The "Additional Assemblies" screen is shown.
Viewing the additional assemblies
- Click "Add" and select "Add Assembly from Project Output..." (I like how this UI uses a WPF-ish "button dropdown" for this interaction.)
Select "DDD.Common" from the "Source
Referencing the Common project
- Leave everything else as it is and click "OK" to ensure our Common DLL is GAC'd when the WSP is deployed.
After adding static classes for our Constants and Utilities in Common, Here's what our Solution Explorer should look like so far: