In the words of Zeddicus Z’ul Zorander, “Bags! Nothing is ever easy!” (from the Sword of Truth series). That makes me think part of Zedd’s wizard training must have involved working with SharePoint, but I digress. For a project I’m working on, I’m using the WCF REST Service Kit to provide a syndication feed based on data contained inside of SharePoint. No, the OOTB RSS feed for a SharePoint list doesn’t cut the mustard for this one. I’m running MOSS 2007 on a Windows Server 2008 machine. I needed the WCF service to run with anonymous authentication. I wanted to call the WSS object model directly, rather than having to use the SharePoint web services. I’m still a novice when it comes to WCF, so some things that I learned along the way:
1. Calling the SharePoint object model from a web application can be tricky. One of the problems you’re likely to run into initially if you’re working on 64-bit machine, is this error:
“Could not load file or assembly ‘Microsoft.SharePoint.Search, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c’ or one of its dependencies.”
Most of what you’ll find online will say something about trying to run a 64-bit application in a 32-bit process, or my favorite, if you want to call the SharePoint object model from an application, you need to have SharePoint installed (don’t just copy over the SharePoint DLLs from the server to your dev machine). Ultimately, this article on MSDN is what I needed to see.
2. You may have a problem getting the WCF service set up on your server and get an error message like this:
“IIS specified authentication schemes ‘Basic, Anonymous’, but the binding only supports specification of exactly one authentication scheme. Valid authentication schemes are Digest, Negotiate, NTLM, Basic, or Anonymous. Change the IIS settings so that only a single authentication scheme is used.”
This article from Sahil Malik is a great explanation of what you need to do to get the WCF service set up on your server. This will help you avoid the error above. The key here is to host the WCF service in a web site that does not host a SharePoint application.
3. Once I had the WCF service set up in its own website, I was able to browse to the .svc file successfully, but when I tried to retrieve the syndication feed, I got the Internet Explorer page that says there’s a connection problem, displaying a “Diagnose Connection” button. At first I thought, “huh?”, and then saw that I was getting a 403-Forbidden error. Okay, so now I know it’s an authentication issue of some sort. I went into IIS and switched the web site to use Windows authentication. Now, when browsing to the syndication feed, after entering my credentials when prompted, everything worked. But I don’t want to be prompted for credentials. I reset the web site to use anonymous authentication.
So, when debugging the code, I was able to see that it failed when trying to make a call against the SharePoint object model. I tried many different things, but ultimately what worked, was to use the SPSecurity.RunWithElevatedPrivilege method to get a SPUserToken, and then create the SPSite object by passing in the user token. Once I did this, everything worked perfectly.
I thought I’d put this blog post together hoping it summarizes the common issues you may run across trying to create a WCF service that calls the SharePoint object model. I read many, many posts and articles online, and I didn’t get my answers from any single post, so I wanted to put together something to highlight the key ones that helped me out.
I started off with the Atom Feed WCF Service project template. This bit of code is what I used to retrieve data from SharePoint and create a List of SyndicationItem objects that can be used to provide a syndication feed:
List<SyndicationItem> items = new List<SyndicationItem>();
SPUserToken systemAccountUserToken = null;
SPSecurity.RunWithElevatedPrivileges(
delegate()
{
using (SPSite site = new SPSite(Sites.AdminSite))
{
systemAccountUserToken = site.SystemAccount.UserToken;
}
});
using (var site = new SPSite(Sites.AdminSite, systemAccountUserToken))
{
using (SPWeb web = site.OpenWeb())
{
IList<Announcement> announcements = _repository.GetAll(web);
foreach (Announcement announcement in announcements)
{
var item = new SyndicationItem(
announcement.Title,
announcement.WebAnnouncementBody,
new Uri(Links.AnnouncementLink),
announcement.Id.ToString(),
announcement.AnnouncementStartDate);
items.Add(item);
}
}
}