In part 1 of this series I introduced the Silverlight News web part and described all steps necessary to prepare your SharePoint servers and development machine. In this post I will describe how I created the WCF service.
Part 1 - Introduction and preparation
Part 2 - Create the WCF News service
Part 3 - Create the Silverlight web part
Part 4 - Create the Silverlight application
WCF is one of the ways to get data out of SharePoint to your Silverlight client. Using the out of the box SharePoint web services is another. But SharePoint does not have a web service to aggregate data from multiple sites into 1 single view. So I had to create my own service anyway. Because I was also behind on getting up to speed on WCF, I decided to build a WCF service. I started off with starting Visual Studio and create a new project of type “WCF Service Library”:
You can also create a class library and add a svc file manually. The advantage of using a WCF Service Library is that you can reference the service from the Silverlight client as a project reference in Visual Studio directly. I first started off with a class library and referenced the service after deploying it to SharePoint. This gave me some authentication headaches, so I switched to the Service Library which made the service reference work instantly.
Step 1 - Define the service contract
In this step we define the interface for the service. This is an abstract defnition of what the service will look like. By decorating the interface with the “ServiceContract” attribute from the System.ServiceModel namespace, we mark our interface as a contract for a WCF interface. The methods that our service will support need to be decorated with the “OperationContract” attribute. Currently my service definition looks like this:
[ServiceContract(Name = "NewsService", Namespace = "http://schemas.blackbelts.com/2008/11/news")]
public interface INewsService
{
[OperationContract]
String SiteTitle();
[OperationContract]
List<NewsItem> GetNewsItems(NewsScope scope);
[OperationContract]
List<NewsItem> GetNewsItemsFiltered(NewsScope scope, NewsFilter filter);
}
It has 3 methods:
- SiteTitle
This is a test method that returns the title of a SPWeb. It can be used to test if the WCF service is running in the correct context when called from a client.
- GetNewsItems
This function returns all announcement for a scope. The scope can be site, site including sub sites and site collection.
- GetNewsItemsFiltered
This returns the news items for the scope, but now only items that are valid according to the NewsFilter that is applied.
Step 2 – Define the data contract
After describing the service in the service contract, we need to describe our objects that are sent to and from the service. There are 2 ways to do this. You can use the DataContractSerializer and the XmlSerializer. I decided to use the first option and describe my objects by using the DataContract and DataMember attributes from the System.Runtime.Serialization namespace. In the example below you will find the definition of the News:
[DataContract(Namespace = "http://schemas.blackbelts.com/2008/11/news")]
public class NewsFilter
{
[DataMember(IsRequired = true)]
public string FieldName { get; set; }
[DataMember(IsRequired = true)]
public string Value { get; set; }
public NewsFilter(string fieldName, string value)
{
this.FieldName = fieldName;
this.Value = value;
}
public bool IsValid
{
get
{
return (!string.IsNullOrEmpty(FieldName) && !string.IsNullOrEmpty(Value));
}
}
public string CAMLQuery
{
get
{
if (!IsValid)
{
return string.Empty;
}
return string.Format("<Contains><FieldRef Name=\"{0}\" /><Value Type=\"Text\">{1}</Value></Contains>", FieldName, Value);
}
}
}
The class is marked to be a data member by using the DataContract attribute. The first parameter of this attribute puts the object in the correct namespace. All properties that need to be passed to and from the client are identified by the DataMember attribute. The IsValid and CAMLQuery properties are internal for the service and will not be available in the client. This is called the Opt-in strategy. All visible members are explicitly exposed. The Opt-out strategy by default includes all properties, and you need to exclude the ones that do not need to be part of the data contract. The XmlSerializer uses the opt-out strategy.
Step 3 – Write the implementation
The code snippet below shows the implementation of the NewsService. The code of how to get the news items is not essential for this post, so I skipped that. It will be available in the ZIP file in the last post of this series.
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class NewsService : INewsService
{
public string SiteTitle()
{
return SPContext.Current.Web.Title;
}
public List<NewsItem> GetNewsItems(NewsScope scope)
{
DataTable announcements = GetAnnouncements(scope);
return GetNewsItems(announcements);
}
public List<NewsItem> GetNewsItemsFiltered(NewsScope scope, NewsFilter filter)
{
DataTable announcements = GetAnnouncements(scope, filter);
return GetNewsItems(announcements);
}
}
Please note the AspNetCompatibilityRequirements attribute the is set on the class. In this attribute, the AspNetCompatibilityRequirementsMode is set to Allowed. The reason for this is that in WCF the HttpContext, and therefore the SPContext, is not available. This is because the WCF service knows nothing about the transport mechanism. By switching on this mode, we will have the HttpContext available in our service.
Step 4 – Create the service file
Next thing to do is to create or update the service file. This file tells which class in which assembly to load when the service is loaded. My svc file looks like this:
<%@ Assembly Name="TST.BlackBelt.Silverlight.WCFServices, Version=1.0.0.0, Culture=neutral, PublicKeyToken=28ec3cd925274d6c"%>
<%@ ServiceHost Service="TST.BlackBelt.Silverlight.WCFServices.NewsService"%>
Step 5 – Configure the service
Now we will create a new web.config file the will configure our service. The configuration contains a number of elements that can be found in the example configuration below:
- ASPNET compatibility mode is enabled in the serviceHostingEnvironment element
- The bindings element contains the configuration of the binding between server and client.
- The service element contains the name of the service and the configuration of the endpoint. In WCF there are always 2 endpoints. One for the service and one for the client. This config file will configure the endpoint for the service.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<bindings>
<basicHttpBinding>
<binding
name="MOSSBinding"
bypassProxyOnLocal="true">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" proxyCredentialType="Ntlm" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service
behaviorConfiguration="TST.BlackBelt.Silverlight.Services.Behavior"
name="TST.BlackBelt.Silverlight.WCFServices.NewsService">
<endpoint
name="NewsService"
contract="TST.BlackBelt.Silverlight.WCFServices.INewsService"
address="http://moss/_layouts/sbb/TST.BlackBelt.Silverlight.WCFServices.News.svc"
binding="basicHttpBinding"
bindingConfiguration="MOSSBinding"
/>
<endpoint
name="CBTmex"
contract="IMetadataExchange"
address="mex"
binding="basicHttpBinding"
bindingConfiguration="MOSSBinding"
/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="TST.BlackBelt.Silverlight.Services.Behavior">
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="True" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
The options that are set in the binding depend on your infrastructure. What type of authentication are you using, are you using http or https? How is the service connected through a proxy? This example is the example of my development machine, which is a single server deployment of SharePoint running in a VM.
I found that the configuration of these services is not very easy. The SvcConfigEditor tool can help you with this. And you probably need to read some articles or a WCF book to understand the basic principles.
Step 6 – Deploy the service
Last step before we can test our service is to deploy the service. There are a number of options of where you want to host your WCF services. I have choosen to deploy my service to a sub folder of the LAYOUTS folder. The main reason for this is that I can deploy my service to SharePoint using SharePoint Solution Deployment. Another options is to deploy to the _vti_bin folder. Make sure that you deploy your service to a folder that makes it available as a relative url of the site where you are. Because my service is deployed to LAYOUTS, I can access the service at http://moss/_layouts/sbb/TST.BlackBelt.Silverlight.WCFServices.News.svc, but also at http://moss/news/2009/_layouts/sbb/TST.BlackBelt.Silverlight.WCFServices.News.svc.
My deployment folder contains 3 files. Apart from the service file and the config file, it also contains the silverlight application, the xap file. This is covered in the next post in this series. The assembly is deployed to the GAC.
Step 7 – Test the service
Last thing to do is to test if the service is available in SharePoint. If you enter a url to the SVC file that you just deployed to the LAYOUTS folder, your screen must look like this:
If you get error messages (I had some, because WCF was pretty new to me…) make sure you have registered the virtual path provider (see previous post) correctly. Otherwise you probably will have some issues in the binding configuration. I found that Fiddler can be a great help here.