My consulting company has been working with one of our clients over the past three years or so on a large commercial software package aimed at local governments. In a nutshell, it uses a Windows Presentation Foundation (WPF) UI with a Windows Communication Foundation (WCF) service layer and a SQL Server database. There are other pieces in play, but that's not important to this discussion.
Back when we started on this adventure, Silverlight was in its infancy, so it was difficult to envision how our client would eventually want to leverage it in their software package. So, we didn't pay a whole lot of attention to it until Silverlight 4 came out and we saw that it had matured into something that our client was ready to use. Some of our client's customers use WebSphere, so Silverlight was an obvious choice for building widgets that could be plugged right in using simple <object> tags.
If you've ever tried to reference an external library from a Silverlight project, you know that you can't do so unless that library has been compiled as a Silverlight library. This is a safeguard enforced by Visual Studio 2010. There are ways to force your way around this limitation by de-compiling your assembly using ildasm, making some modifications to the IL, and re-compiling it using ilasm, but that's dangerous business as your application will fail spectacularly at runtime if you use any namespaces or types not included in the Silverlight runtime. Meticulous maintenance is necessary to implement this approach, and you won't get any support from Microsoft, so as a general rule, I don't recommend it.
A better way to go is to create an empty Silverlight library project and add links to the same code files as your standard .NET project. That way, you can compile the project for either platform and you avoid the maintenance woes of duplicated code (see approach #2 in the blog post "Reusing .NET Assemblies in Silverlight."). Of course, if you were unwilling or unable to plan for Silverlight from the very beginning, the move to Silverlight can be far from easy. There are numerous pitfalls that you may encounter, and I'll discuss a few here that we ran into.
First, our client elected not to use POCO (Plain Old CLR Objects) in their project. Their objects contain other objects as well as some methods and other functionality. That meant that we couldn't rely on using the object stubs that get created for you using the "Add Service Reference" feature in Visual Studio 2010. As a general rule, it's best to avoid using tools that generate a lot of extra (some would refer to as bloated) code, so that in and of itself was not a deal breaker.
The thing that proved much more problematic is the fact that many of the types that we used in our client's project were not part of the Silverlight runtime—for example, System.Xml.XmlDocument. Of course, there is a viable alternative in the System.Xml.Linq.XDocument class, but that still meant that we needed to re-tool some of our code. It is all just speculation on my part, but I think that Microsoft used the fact that Silverlight required a small footprint for its runtime as a convenient excuse to eliminate some of the namespaces and types that it had either deprecated or felt were not in line with the direction that it wanted developers to go.
We also implemented some extension methods that converted our objects to System.Data.DataTable objects for data export purposes. Well, the entire System.Data namespace was not included in the Silverlight runtime. We determined, however, that these extension methods were unnecessary on the Silverlight end, so we moved them to a separate code file and excluded them from the Silverlight project. There were some cases where methods were implemented directly on the entities that used incompatible types. In those cases, we used partial classes to separate that functionality out of the Silverlight versions of the entities.
Changing data types and separating out incompatible methods are one thing, but the issues run even deeper. Even if the namespaces and types that you need are included in Silverlight, they often don't have the method overloads that you need. One example is in the case of the System.Math class. In .NET 4, the Math.Ceiling() and Math.Floor() methods have overrides that accept either a double or a decimal type. In Silverlight, your only option is a double. Of course, our code relied on the decimal override, so we had to make changes to our logic to avoid behavior inconsistencies between the .NET 4 and Silverlight versions of our assemblies.
Next, we had to deal with third-party components. Some of the vendors that we used had a Silverlight version of their components. Some didn't. Others (such as the awesome CSLA Framework by Rocky Lhotka) had a Silverlight version, but it lacked key features that we needed (in this particular case, validation). A lot of work was required to separate things out and retool our code so that it could both compile to Silverlight assemblies and provide the requisite functionality that we needed.
WCF proved to be another stumbling block for us. All WCF calls in Silverlight are asynchronous, so ideally your endpoints should be designed with this pattern. Of course, ours weren't, and our client was not all that interested in maintaining multiple endpoints. What we ended up doing is using the Add Service Reference wizard along with the Reuse types in all referenced assemblies advanced option. This created service clients that adapted the connection to our endpoints for asynchronous use. Yes, it created a bunch of other superfluous code and files, but we extracted just the service client code and placed it in our own class library and excluded all the extra stuff that drives most developers crazy and gives the wizard a bad name. It was certainly not the ideal solution, but performance was acceptable and it saved us a ton of development time.
We also had to work though some issues with how we pass security credentials, since client-side WCF configuration is almost completely different in Silverlight than in WPF. For example, in Silverlight, the only two binding types you get are basicHttpBinding and customBinding. If you want to do anything with netTcp, you need to use a customBinding, which is not very straightforward if you have a security credential mechanism.
You may hit an interesting problem if you're hosting your WCF endpoints in a Windows service instead of using IIS (as was the case with our client's project). Silverlight is only able to consume services from the same domain that hosts it unless you have a clientaccesspolicy.xml file hosted on the root of the website that your WCF service is also hosted on. Of course, if you're not using IIS, there's nowhere to stick that clientaccesspolicy.xml file. Fortunately, you can host a custom WCF endpoint that uses the WebGet attribute to stream the contents of a policy file to the Silverlight client as if it were hosted in IIS (see the blog post "Enabling cross-domain calls for SL apps on self-hosted TCP services").
These are just a few of the issues that we encountered while building a Silverlight application using existing WPF and .NET 4 code assets and WCF service endpoints. In closing, I would like to make two points in this month's column. First, if you have an existing .NET application, don't assume that it will be super easy to repurpose your business entities and WCF endpoints for use in a Silverlight application. Depending on your circumstances, a significant amount of retooling may be necessary.
Second, if you're starting any .NET application now, I strongly advise you to create mirror Silverlight projects using linked files and do frequent compiles to gauge how Silverlight compatible (or not so compatible) your code is. Even if Silverlight is not one of your intended deployment platforms now, you never know what the future holds. You can save yourself a lot of heartache down the road if you make more informed decisions now and fix Silverlight incompatibilities as they occur, instead of trying to shoehorn it all at once later.
Jonathan Goodyear ([email protected]) is president of ASPSOFT, an Internet consulting firm in Orlando. He is Microsoft regional director for Florida, an ASP.NET MVP, a Microsoft Certified Solution Developer, and a contributing editor for DevProConnections.