Lessons Learned Integrating Silverlight in MOSS 2007, Part 1
The November 2008 edition of MSDN magazine contained the following article:
Light Up SharePoint With Silverlight 2 Web Parts
https://msdn.microsoft.com/en-us/magazine/dd148643.aspx
While the article provides a good introduction to integrating a Silverlight application into a SharePoint site, it definitely left me wanting more.
We've been using Silverlight on my current project for almost four months now, and we've definitely learned a few things the hard way when it comes to Silverlight and SharePoint. Note that this is an Internet-facing customer service portal built on Microsoft Office SharePoint Server (MOSS) 2007.
It's also worth noting that I didn't develop the original Silverlight application we are using on the site, but I've made a number of changes to it in order to resolve a few issues and improve the overall implementation.
Before diving into the issues and improvements, it is helpful to first understand the structure of the solution. If you've seen my walkthrough of the "DR.DADA" approach to SharePoint (or if you've worked with me on any SharePoint projects), then the following solution structure should seem very familiar:
- Fabrikam.Portal.sln
- Fabrikam.Portal.CoreServices.csproj
- Fabrikam.Portal.CoreServices.DeveloperTests.csproj
- Fabrikam.Portal.Web.csproj
- Fabrikam.Portal.Web.DeveloperTests.csproj
- Fabrikam.Portal.Web.ServiceWheel.csproj
- Fabrikam.Portal.Web.ServiceWheel.Test.csproj
For the sake of brevity, I've intentionally omitted a number of other projects in the solution that are not relevant to this post.
The Fabrikam.Portal.CoreServices project is where we put code for core services, such as logging.
The Fabrikam.Portal.Web project contains the bulk of our solution and is comprised of various SharePoint features. For example, we have a feature for publishing layouts (including master pages and page layouts), another feature for our custom Web Parts, an Announcements feature, etc. This project contains a folder structure that mimics the hierarchy under %ProgramFiles%\Common Files\Microsoft Shared\web server extensions:
- 12
- TEMPLATE
- CONTROLTEMPLATES
- FEATURES
- ...
- LAYOUTS
- TEMPLATE
The Fabrikam.Portal.Web.ServiceWheel project is the Silverlight application that provides a rather slick menu of the various features provided by the portal, while also promoting value differentiation and branding for the company.
The Fabrikam.Portal.Web.ServiceWheel.Test project is a simple ASP.NET application that allows developers to quickly and easily debug the Silverlight application outside of the SharePoint site. Making changes to the Silverlight application itself is much faster if you don't have to deploy it to your SharePoint site to debug or see the results of your changes.
Note that the output of the Fabrikam.Portal.Web.ServiceWheel project is an XAP file, which is subsequently deployed via the WSP file generated by the Fabrikam.Portal.Web project. The Silverlight application is hosted inside of the SharePoint site by a user control (ServiceWheel.ascx) which itself is hosted within a generic User Control Web Part (similar to SmartPart for SharePoint).
One of the first issues that I discovered in the Silverlight piece was the way the original developer integrated the XAP file into the WSP file.
Originally, the user control contained the following code:
<object data="data:application/x-silverlight-2," type="application/x-silverlight-2"
width="100%" height="100%">
<param name="source" value="_Layouts/Fabrikam/Wheel.xap" />
<param name="onError" value="onSilverlightError" />
<param name="background" value="white" />
<param name="minRuntimeVersion" value="3.0.40624.0" />
<param name="autoUpgrade" value="true" />
<a href="https://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0" style="text-decoration: none">
<img src="https://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight"
style="border-style: none" />
</a>
</object>
Notice that the XAP file is deployed to the _layouts folder -- and in accordance with best practices -- placed under a new folder corresponding to the company name (to isolate it from the Microsoft files installed by SharePoint). So far, so good.
In order to make the XAP file available to the project that creates the WSP, the Silverlight developer simply changed the output path in the Fabrikam.Portal.Web.ServiceWheel project to be:
..\..\..\Portal\Web\12\TEMPLATE\LAYOUTS\Fabrikam\
The DDF file (wsp_structure.ddf) used to create the WSP was updated to include the XAP file...
.Set DestinationDir=Layouts\Fabrikam
...
..\..\12\TEMPLATE\Layouts\Fabrikam\Wheel.xap
...and the manifest.xml file was updated to deploy the XAP file from the WSP:
<TemplateFiles>
...
<TemplateFile Location="Layouts\Fabrikam\Wheel.xap" />
</TemplateFiles>
Note that the developer also had to manually configure the build order to ensure the Silverlight project gets compiled before the Fabrikam.Portal.Web project -- or else the build could potentially break (if the XAP file was not found in the expected location because it had never been built before) or, even worse, the WSP could include an old version of the XAP file.
While everything appeared to work as expected, there was a fundamental problem with this approach. The developer only changed the output path for the Debug configuration of the Fabrikam.Portal.Web.ServiceWheel project. In other words, when it came time to create our Release builds, the build would either break (if the Debug build hadn't copied the XAP file to the expected location yet) or the Release build would include the Debug build of the XAP file in the Release build of the WSP.
While it probably isn't the worst thing to include a Debug version of an assembly in a Release build, it's definitely not a best practice. However, more importantly, changing the output path in order to copy the XAP file into the expected location is not compatible with Team Foundation Build.
As I noted in a previous post:
The problem with relative paths is that Team Foundation Build uses a different folder structure when compiling your projects.
In other words, whenever you find yourself about to specify ".." in the output path inside Visual Studio for a project, stop what you're doing, think about my blog posts, take a deep breath, and find another solution to the problem you are trying to solve.
In order to resolve the issues around integrating the Silverlight XAP file into the WSP, I made the following changes:
- Reverted the output path for the Debug configuration of the Fabrikam.Portal.Web.ServiceWheel project to the default (i.e. Bin\Debug instead of ..\..\..\Portal\Web\12\TEMPLATE\LAYOUTS\Fabrikam\ ).
- Added a reference from the Fabrikam.Portal.Web project to the Fabrikam.Portal.Web.ServiceWheel project. Note that while this is not explicitly required by the code within the Fabrikam.Portal.Web project, there really is a dependency between the two projects. Establishing this reference ensures the projects are built in the correct order (without having to manually tweak the build order of the projects in the solution).
- Modified the Fabrikam.Portal.Web project (by unloading the project and then editing the MSBuild file directly) to include the following:
<PropertyGroup>
<!-- Add the file extension for Silverlight application packages (.xap) to
the list of extensions that reference resolution considers when looking for
files related to resolved references (i.e. project references). This ensures
that .xap files are copied to the output folder for this project and
subsequently added to the SharePoint Web solution package (WSP). -->
<AllowedReferenceRelatedFileExtensions>
$(AllowedReferenceRelatedFileExtensions);
.xap
</AllowedReferenceRelatedFileExtensions>
</PropertyGroup>
This -- along with the project reference mentioned above -- ensures that the WSP generated by the Fabrikam.Portal.Web project is recompiled, even if the only thing that changed since the last build is something the Silverlight project.
I can see that this post is already turning out to be much longer than I originally anticipated, so I'm going to append "Part 1" to the title and detail some of the other gotchas I've found with Silverlight and SharePoint in a subsequent post.
Note that in the time since I originally started working with Silverlight and SharePoint, I've found other resources in addition to the MSDN article, such as the Silverlight Blueprint for SharePoint. However, from what I've seen so far (which, honestly, is just a cursory examination) most of these still seem to lack much depth.
Stay tuned and I promise I'll cover some more of the "juicy stuff" ;-)