WCF Guidance For Mobile Developers
WCF Guidance For Mobile Developers
WCF Guidance For Mobile Developers
Contents
The State of Windows Mobile Today ............................................................................................................ 4 Windows Mobile Devices .......................................................................................................................... 5 Pocket PCs (Windows Mobile 6 Professional/Classic) .......................................................................... 5 Smartphones ......................................................................................................................................... 6 .NET Compact Framework ........................................................................................................................ 6 Remote Communications with Mobile Devices ........................................................................................ 7 WCF for the Mobile Developer ..................................................................................................................... 9 Setting Up Your Mobile Development Environment .................................................................................. 11 Visual Studio 2008 Professional (and higher) ......................................................................................... 11 Windows Mobile 6 Professional and Standard Software Development Kits Refresh ............................ 12 Microsoft Windows Mobile Device Center 6.1 for Windows Vista ........................................................ 12 Power Toys for .NET Compact Framework 3.5 ....................................................................................... 14 Final Steps ............................................................................................................................................... 14 Getting Started: Your First Mobile + WCF Application ............................................................................... 14 Creating and Hosting the WCF Service ................................................................................................... 15 Creating a Simple Mobile Application..................................................................................................... 17 Proxy Generation for Mobile Applications ............................................................................................. 19 Testing the Solution ................................................................................................................................ 21 Contract Design ........................................................................................................................................... 22 Service Contracts .................................................................................................................................... 23 Streaming ............................................................................................................................................ 24 Sessions ............................................................................................................................................... 25 Duplex ................................................................................................................................................. 25 Transactions ........................................................................................................................................ 25 Complex Types and Serialization ............................................................................................................ 26 Serialization Architecture.................................................................................................................... 26 Complex Types and Wire Compatibility .............................................................................................. 27 Fault Contracts ........................................................................................................................................ 32 Message Contracts .................................................................................................................................. 32
wcfguidanceformobile.codeplex.com
Page 1
wcfguidanceformobile.codeplex.com
Page 2
Mobile devices have become a staple of everyday life, not only for professional knowledge workers but also for average consumers and end-users who have grown accustomed to a connected lifestyle. Mobile devices are routinely used to make phone calls, send and receive emails, exchange text messages and manage busy schedules with calendars and reminders. Most devices can perform much more than typical electronic organizer and phone features. They literally are small computers that can fit in a pocket, lab coat or purse. Just like any computer, to get the most value out of devices, you need additional software. When it comes to writing mobile software, there are two main types of applications you can build to run on devices: standalone applications, and connected smart clients. Standalone applications have to deal with multiple constraints introduced by the device form factor - such as limited screen real estate, less memory, CPU power and available storage, limited input methods such as tactile screens and hardware keys on the device, and dependency on battery life. Smart clients on the other hand have to deal with the same constraints as standalone mobile applications with the addition of concerns related to communications with remote server resources such as sporadic connectivity, slower connection speeds, high latency networks, wireless security threats and much more. The ability to reach application servers and backend systems and databases is what finally enabled mobile devices as full class enterprise citizens. Enterprise applications have long been accessible to office users and knowledge workers in various ways. Whether they are deployed locally as rich clients or accessed centrally as Web clients running within a browser, enterprise applications connect people, systems, processes and data in order to achieve enterprise agility. This is done by relying on systems that enhance productivity, lower operational costs, and enable new ways to work with data, resulting in a competitive advantage. Great technological advances in the field of mobility have allowed us to reach even farther out, beyond the physical boundaries of the enterprise. Faster wireless networks, broader coverage, more powerful devices, standardized positioning and location-based services, different form factors and state of the art development tools have all contributed to the mobile revolution currently in progress. Most of the users that have been long forgotten by the world of information technology are now able to participate in automated business processes once again. Both internal and external mobile and field workers can access corporate data from the road, alter it or collect new data, and submit it back to the central office. Spurred by technologies like the Windows Mobile platform and the Microsoft .NET Compact Framework which brings managed code to mobile devices, a new breed of mobile smart client applications is emerging and changing the way we think about software development. These new smart clients are based on best practices learned by merging rich clients and Web development, bringing the best of both worlds to the realm of mobile devices.
wcfguidanceformobile.codeplex.com
Page 3
wcfguidanceformobile.codeplex.com
Page 4
The core components necessary for the OS A set of shell-extensions which provide a uniform look and feel and uniform data entry mechanism for each group of device form factors.
In other words, each device must support a common, minimum set of components. Additional components are grouped into specific extensions. In order to unify the Windows CE versions to be used on generic consumer-grade Pocket PCs and Smartphones, Microsoft created Windows Mobile. Windows mobile is therefore a platform that meets a standard set of specifications for Windows CE. Windows Mobile 6 is powered by Windows CE 5.0 and includes a number of mobile productivity features including an SDK with managed API's for working with Outlook Mobile and Telephony, a lightweight version of Office called Office Mobile (including Word Mobile, Excel Mobile and PowerPoint Mobile) and Media Player Mobile. Windows Mobile 6.0 added many security enhancement such as storage card security and better support for certificates, and a number of other productivity and networking features are also included to provide control for device specific capabilities, such as Caller Photo ID, GPS and Bluetooth. Windows Mobile 6.1 added a more streamlined Today screen and support for Microsoft App Center Mobile Device Manager, and the new version 6.5 introduces new user interface options as well as updated productivity applications.
wcfguidanceformobile.codeplex.com
Page 5
wcfguidanceformobile.codeplex.com
Page 6
Partial support for Windows Communication Foundation (WCF), which is the primary topic discussed in this paper Partial support for the Language Integrated Query (LINQ), including LINQ to Objects, XML and DataSets New Media classes to play sounds from managed code Various Windows Forms enhancements Compression support to provide basic compression and decompression services for streams via the new System.IO.Compression namespace Support for Client-side certificates Managed debugger fixes and a new CLR Profiler tool
WCF is not supported on earlier versions of the .NET Compact Framework, thus all the code samples for this whitepaper require the .NET Compact Framework 3.5.
Sockets
Live connection to SQL Server on the backend over TCP/IP or HTTP, using ADO.NET for data exchange Synchronization of data between the source SQL Server database and clientside SQL Server CE database using RDA, Merge Replication or ADO.NET Sync Services Asynchronous exchange of data messages with Message Queuing Services (MSMQ) over TCP/IP
Synchronization
Message Queuing
Email/SMS Messages
HTTP
wcfguidanceformobile.codeplex.com
WCF was initially introduced with the .NET Framework 3.0 which released with Windows Vista in January 2007 along with Windows Workflow Foundation (WF) and Windows Presentation Foundation (WPF). When the .NET Framework 3.5 released with Visual Studio 2008 in November 2007 additional WCF features were introduced including improvements to the web programming model and support for the latest WS* protocols. As mentioned earlier, as of the .NET Compact Framework 3.5 WCF is supported on mobile devices. To that end, mobile devices will typically consume WCF services as interoperable web services using a restricted set of protocols, or as POX and REST-based services although nothing precludes you from calling services using JSON serialization, or syndication services all it takes is a little elbow grease. It is impossible to sum up a platform as vast and feature-rich as WCF in a short section however, it is possible to explain some fundamental concepts that will provide you with a foundation for subsequent sections in this paper. In this section well keep the discussion conceptual, since in later sections we will
wcfguidanceformobile.codeplex.com
Page 9
In order for a client to call operations exposed by a hosted service it must have access to the service metadata (the contract) so that it can send messages with the required parameters, and process return values accordingly. Clients typically rely on proxy generation to improve their productivity for writing code to call remote services. Services enable metadata exchange in order to support proxy generation. The proxy generation tool (NetCFSvcUtil.exe for mobile clients) can look at the Web Service Description
wcfguidanceformobile.codeplex.com
Page 10
These features, along with other messaging protocols and behaviors all contribute to how messaging works between client and service. Since the .NET Compact Framework implements a subset of WCF features, services must also be careful not to require features that a mobile client is unable to communicate with. Of course this is a subject that we will address throughout this paper.
Visual Studio 2008 Professional (and higher) Windows Mobile 6 Professional and Standard Software Development Kits Refresh Microsoft Windows Mobile Device Center 6.1 for Windows Vista Power Toys for .NET Compact Framework 3.5
These tools, where to get them and how to install them are covered next.
wcfguidanceformobile.codeplex.com
Page 12
Allow USB connections is checked For Allow connections to one of the following, select DMA in the dropdown list. Direct memory Access (DMA) is the fast native protocol used by the Visual Studio for Devices Debugger to establish connections for deployment and remote debugging with devices and emulators. For This computer is connected to, select Automatic to make sure pass-through connectivity is automatically enabled between your device or emulator and your development workstation. This pass-through connectivity allows the connected device or emulator to share your workstations network and Internet connectivity, which is essential for Mobile WCF development. Allow data connections on device when connected to PC must be unchecked since this feature is only used when a Mobile PC notebook needs to borrow the wireless cellular connectivity of the device, thus acting as a wireless modem.
All the code examples in this document require connectivity between the device emulator and the development workstation. It is therefore essential that Windows Mobile Device Center be installed and functional prior to getting started with any of the samples presented here. wcfguidanceformobile.codeplex.com Page 13
Final Steps
After installing the tools just discussed you are now ready to start building Windows Mobile applications with .NET Compact Framework and Visual Studio 2008. The following are additional tips and considerations to keep in mind as you build mobile applications:
It will be easier for you to develop mobile applications communicating with WCF services if you disable the User Account Control (UAC) feature in Windows Vista which is used to request an elevation of privilege for certain operations using an Allow/Deny dialog option. However do this at your own risk and make sure that your computer has all the latest Windows security patches and that you have an up-to-date security suite installed and running to prevent intrusions from viruses and other malware. To disable UAC, open the Control Panel, select User Accounts, and then Turn user Account Control on or off. Clear the checkbox and select Ok. A reboot will be required. Remember to turn UAC back on when you are done with your development session as a precaution.
When running and testing your mobile applications on the device emulator, remember that when closing the emulator you should select the option to save the state when prompted. This prevents the long deployment and installation of runtime packages, such as .NET Compact Framework 3.5 or SQL Server Compact Edition, the next time you use that emulator for development. It also ensures that you save any files copied to the device for testing, such as certificates we ask you to install for security scenarios.
wcfguidanceformobile.codeplex.com
Page 14
Design the service contract Implement the service contract on a service type Supply one or more service endpoints, custom binding configurations if applicable, and configure runtime behaviors Initialize the ServiceHost for that service type
Consider the simple example of a service contract and service type implementation shown in Figure 3. The service contract is a CLR interface (IGreetingService) decorated with the ServiceContractAttribute, each method decorated with the OperationContractAttribute to include it in the service metadata. You typically supply a Namespace to the ServiceContractAttribute to disambiguate messages on the wire. So far, this is typical for any WCF service. The service type (GreetingService) implements the service contract and in theory would coordinate calls to the business and data tier. In this case, it is a simple service and the implementation is embedded directly into the service type. The ServiceBehaviorAttribute is used to apply service behaviors that are tightly coupled to the implementation. Figure 3: A simple service contract and implementation for mobile clients
[ServiceContract(Namespace = "urn:mobilewcf/samples/2009/04")] public interface IGreetingService { [OperationContract] string Greet(string firstname); } public class GreetingService : IGreetingService { public string Greet(string firstname) { if (string.IsNullOrEmpty(firstname)) { Console.WriteLine("ERROR: You must provide a valid name to receive a greeting."); throw new InvalidOperationException("You must provide a valid name to receive a greeting."); } string s = string.Format("Greetings {0}.", firstname); Console.WriteLine(s); return s; }
wcfguidanceformobile.codeplex.com
Page 15
Once you have designed and implemented the service keeping in mind mobile limitation you can host the service and expose one or more endpoints for client applications. Assuming you are hosting on Windows Server 2003 or Windows Server 2008 machines you will typically host services in Internet Information Services (IIS), Windows Process Activation Service (WAS) or self-host in a Windows Service. The only requirement is that you expose one or more HTTP-based endpoints compatible with mobile clients. For testing purposes, in this example we will host the service in a simple Console application. Figure 4 shows the code to initialize the ServiceHost. Figure 4: Initializing the ServiceHost for the GreetingService
ServiceHost host = new ServiceHost(typeof(GreetingService)); try { host.Open(); Console.ReadLine(); } finally { if (host.State == CommunicationState.Faulted) host.Abort(); else host.Close(); }
Instead of hard-coding endpoints, this code assumes that endpoints will be configured in the app.config (or web.config for IIS and WAS hosting). Figure 5 shows the <system.serviceModel> section for the GreetingService described in Figure 3. A single endpoint is exposed over BasicHttpBinding which means it is a simple SOAP 1.1 endpoint without security features. This is a good way to test that your mobile application can reach the service before you begin adding necessary security features which is covered later. Additionally, youll want to expose a metadata exchange endpoint, using MexHttpBinding (a variation of WSHttpBinding without security) so that you can generate a proxy for the mobile application. The associated service behavior (see the <serviceBehaviors> section) enables the ServiceMetadataBehavior to support the metadata exchange endpoint, and to enable HTTP browsing (so that you can view the WSDL document in the browser). The debug setting to includeExceptionDetailsInFaults should be set to false in production deployments and, frankly, this setting is not very useful for debugging mobile application since the mobile implementation of WCF in the .NET Compact Framework leaves much to be desired in the way of handling faults thrown by the service channel. Figure 5: A simple example of a mobile-compatible service model configuration wcfguidanceformobile.codeplex.com Page 16
With this you now have a mobile-compatible service configuration and can begin to look at creating your first mobile application to consume this simple WCF service.
wcfguidanceformobile.codeplex.com
Page 17
In this case, you will use the Device Application template to create a mobile client, which is the mobile equivalent of a standard Windows Forms project. This generates a properly-sized form that presents a device image where you can drop controls as you would any Windows Forms or Windows Presentation Foundation (WPF) application to design the UI. Figure 7 shows the UI for the GreetingService sample. Figure 7: A mobile client application for the GreetingService
wcfguidanceformobile.codeplex.com
Page 18
The point of this section was to make sure you are aware of the requirement to use Smart Device templates for mobile clients. Next well explain how to generate a proxy to call a WCF service from a mobile application.
wcfguidanceformobile.codeplex.com
Page 19
In addition, a static method called CreateDefaultBinding() returns a runtime representation of the binding. In this case, a CustomBinding equivalent for BasicHttpBinding defaults is returned:
public static Binding CreateDefaultBinding() { CustomBinding binding = new CustomBinding(); binding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8)); binding.Elements.Add(new HttpTransportBindingElement()); return binding; }
NOTE: One apparent limitation seems to be that NetCFSvcUtil only generates code for the first endpoint it finds that is compatible with mobile devices during proxy generation. Once you have generated the proxy using NetCFSvcUtil you can write code to call the service from your mobile application. The following code illustrates using the static binding and endpoint address exposed by the proxy, GreetingServiceClient, to initialize the proxy before calling the Greet() operation:
Binding binding = GreetingServiceClient.CreateDefaultBinding(); string address = GreetingServiceClient.EndpointAddress.Uri.ToString(); GreetingServiceClient m_proxy = new GreetingServiceClient(binding, new EndpointAddress(address)); string result = m_proxy.Greet(txtName.Text).ToString();
NOTE: Mobile devices are unable to resolve localhost to the correct IP address or machine name where the service is hosted. This is due to the fact that the device or emulator are considered to be wcfguidanceformobile.codeplex.com Page 20
Launch Windows Mobile Device Center via the Windows Vista Start menu or from the Control Panel. From Visual Studio 2008, select Connect to Device under the Tools menu. This lists all the available emulators compatible with your mobile project. Select the Windows Mobile 6 Professional Emulator and click Connect. Wait as the emulator is loaded and initialized. From Visual Studio 2008, launch the Device Emulator Manager (DEM) under the Tools menu. Locate the emulator you just launched in the list (it has a small Play icon next to it as shown in Figure 8). Select the active emulator you located, right-click it and pick Cradle (i.e., the equivalent of connecting a device to your computer via a USB cable) to establish a connection between the emulator and WMDC, which will show the device status as Connected, and the Play icon next to your emulator in DEM will change to a cradle icon. The final step is to select the option to connect to the device without setting it up, from WMDC. Your emulator is now ready for deployment and has network and Internet access. At this point, you can use WMDC to interact with the device, such as copying files to and from the device for testing. This is useful for copying certificates for security scenarios, for example.
Figure 8: The Device Emulator Manager and Windows Mobile Device Center
wcfguidanceformobile.codeplex.com
Page 21
Once you have executed these steps, the Device Emulator Manager and will continue to run in the background, and the selected emulator will also remain open in a separate window. Even if you close down all Visual Studio instances, these continue to run which saves you time if you reload your project and wish to continue testing with the same emulator. You will only have to repeat these steps if you close the emulator window (which can be a force of habit while debugging) or if you shut down the DEM with brute force. Now, with the emulator running you can test your code. It is helpful to test the mobile client from a separate Visual Studio instance. Run the service host first, and then run the mobile client application in debug mode. This will bring up a deployment dialog asking where you want to deploy your mobile application. Select Windows Mobile Professional Device. By selecting the device instead of the emulator you are deploying to whatever device the WMDC is connected to which will be helpful when you are testing deployment to a physical cradled device. This dialog will be presented each time you debug the client.
Contract Design
Contract design is the first step in implementing a WCF service. Service contracts define the operations that will be exposed to clients, and in some cases place requirements on communications between clients and services. Service operation parameters and return values define the messaging requirements for each operation and are usually serializable complex types such as data contracts. Service operations can also use message contracts to support advanced SOAP messaging requirements such as custom message headers. Fault contracts provide information to clients about the type of SOAP fault details beyond a simple fault with an error message that can be returned by service operations. You can define contracts that are compatible with both non-mobile and mobile clients. This may not make sense in all cases because of mobile client considerations such as: Mobile clients prefer chunky over chatty interfaces. This optimizes transfer overhead and reduces the number of calls to the service given the latency of mobile communications. Page 22
wcfguidanceformobile.codeplex.com
If contracts will be shared by non-mobile and mobile clients you must avoid requiring features not supported by the .NET Compact Framework. A short summary of features related to contract design is shown in Figure 9. Figure 9: Contract-related features and support for mobile clients Feature Streaming Comments Streaming not supported. Service contract can use Stream parameters but they will be buffered, not streamed. Cannot enable streaming on the binding. Transport sessions not supported. Service contract can use SessionMode.Allowed. Service should use InstanceContextMode.PerCall behavior. Services designed for duplex communications with callback contracts cannot be called by mobile clients. Duplex requires a transport session. Service contract cannot require transactions for any service operations. Mobile clients cannot flow transactions. Can freely employ data contracts or any other serializable types in the service contract definition. The mobile client will use XmlSerializer types that are wire compatible. Can freely employ message contracts in the service contract. If the message contract includes custom headers proxy generation will not work. The mobile client can work with headers with additional custom code. Can freely employ fault contracts in the service contract. Proxy generation will not include fault contracts and does not handle faults well. Can write custom code for mobile client to work with faults.
Sessions
Duplex
Fault Contracts
The following sections will discuss these features and limitations with additional detail.
Service Contracts
Generally speaking you will approach service contract design for mobile clients as you would for any WCF service. As discussed earlier a service contract is typically implemented as a CLR interface with the ServiceContractAttribute. The OperationContractAttribute is also applied to all operations you want to expose as part of the service implementation. Figure 10 illustrates the service contract for the TodoListService.
wcfguidanceformobile.codeplex.com
Page 23
The equivalent implementation at the client is an interface including a method for each service operation minus the attributes. The proxy, TodoListServiceClient in this case, implements this interface and inherits the base proxy, CFClientBase, which handles the serialization dirty work. Figure 11 illustrates the client-side implementation (without the proxy implementation details). Figure 11: Client-side equivalent for the ITodoListService service contract, also implemented on the proxy type TodoListServiceClient
public interface ITodoListService { TodoItem[] GetItems(); string CreateItem(TodoItem item); void UpdateItem(TodoItem item); void DeleteItem(string id); } public partial class TodoListServiceClient : Microsoft.Tools.ServiceModel.CFClientBase<ITodoListService>, ITodoListService
{} There are some WCF features that are not supported in mobile scenarios which impacts the usage of these fundamental service contract attributes, in addition to other attributes that you might apply to a service contract or its operations. They are streaming, sessions, duplex and transactions. Streaming Only buffered messages are supported by the .NET Compact Framework. It is perfectly legal to use Stream parameters and return types in your service contracts, however the data will be buffered, not streamed. Thats because Stream parameters are only streamed when exposed by an endpoint that supports streaming in the binding configuration which is not supported. Since the behavior for a mobile client would be very different from that of a non-mobile client that supports streaming you wcfguidanceformobile.codeplex.com Page 24
Using SessionMode.NotAllowed is also acceptable, except that this precludes exposing endpoints over any binding that supports session which is like saying you can only expose these service operations over simple HTTP. Since sessions are not supported by mobile clients, it doesnt make sense to call a service that maintains session state between calls for clients. The default behavior for a service type is InstanceContextMode.PerSession, so to avoid unexpected results you should explicitly apply the instancing behavior InstanceContextMode.PerCall.
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)] public class GreetingService : IGreetingService
If the service is a singleton (not useful in most distributed environments) this is also acceptable, so long as session is not maintained as defined by the contract setting. Duplex Duplex communications require a transport session (Named Pipes, TCP or HTTP Dual) and this is not supported by NetCF. Service contracts designed for duplex communications cannot be called by mobile clients. Transactions Distributed transactions are not supported by the .NET Compact Framework Contract so the mobile client can never flow transactions to the service. The implication of this is that the service contract cannot require transactions from the client. This means that service operations decorated with the TransactionFlowAttribute cannot use TransactionFlowOption.Mandatory. It is, however, acceptable for the service to support transactions if the client wants to send one. Thus, the same contract can be called by mobile and non-mobile clients if it uses TransactionFlowOption.Allowed.
wcfguidanceformobile.codeplex.com
Page 25
The default behavior is TransactionFlowOption.NotAllowed which means you likely do not use the TransactionFlowAttribute unless you want to opt-in support for transactions at the service.
wcfguidanceformobile.codeplex.com
Page 26
At the mobile client, the XmlSerializer is always used for message serialization, and complex types are always XmlSerializer types. Proxy generation using NetCFSvcUtil produces the code to handle serialization through the XmlSerializer, and produces wire-compatible versions of complex types defined at the service. The messaging layer of WCF requires you to supply a type that derives from XmlObjectSerializer for message serialization. The DataContractSerializer is an implementation of the XmlObjectSerializer, but the XmlSerializer is not. At the service, if you apply the XmlSerializerFormatAttribute to the service contract (or, service operation) the XmlSerializer is used through an internal wrapper class that inherits XmlObjectSerializer XmlSerializerObjectSerializer. Figure 11 also illustrates the relationship between these types at the service. This detail is important to mobile clients since they do not use traditional service contracts for serialization everything is done directly at the messaging layer of WCF inside the proxy. NetCFSvcUtil generates an implementation of the XmlObjectSerializer called CFContractSerializer. As illustrated in Figure x this type wraps the use of the XmlSerializer to handle message serialization, much like the equivalent at the service, the XmlSerializerObjectSerializer. When a message is created to send to the service, an instance of this serializer is supplied to handle serialization. Likewise, when a response is received an instance of this serializer is created to handle deserialization. Since the proxy base type CFClientBase handles this for you, the details of message and complex type serialization are not apparent to your mobile client code. You work with instances of the XmlSerializable complex types, and a proxy that looks much like the service type. Complex Types and Wire Compatibility As illustrated in Figure 11, at the service any number of complex type formats can be used to describe the messaging requirements for each service operation although the preferred route is to use data contracts. Figure 12 lists each of the supported formats and reasons to use them. Figure 12: Summary of complex type formats at the service Serialization Format POCO Usage Plain-Old-CLR-Type (POCO) aptly refers to CLR types not decorated with any serialization attributes. Only public properties and fields are included in serialization and the type must not be part of a type hierarchy that includes non-POCO types. This is useful for simple scenarios that do not require a long term strategy for contract versioning, nor for supporting other serializable types in the type hierarchy. Data contracts are the preferred format for WCF services. Types are opted-in as data contracts using the DataContractAttribute and DataMemberAttribute (to be discussed). Data contracts provide control over the serialization namespace for types, serialization order, and required members. Serializable types are types decorated with the SerializableAttribute. Only fields are serialized (wire-level serialization) and there is no control over namespaces, Page 27
Data Contract
Serializable
wcfguidanceformobile.codeplex.com
The complex type format used at the service is irrelevant to the mobile client. The service metadata and WSDL include XSD schema definitions for types and proxy generation is based on the schema. As already mentioned, complex types are always represented as XmlSerializer types for the mobile client but the result is wire-compatible with the service. To provide some context, lets take a look at a few examples. Consider the service contract in Figure x which exposes operations to interact with a collection of todo items described by the complex type TodoItem. If the TodoItem type is implemented as the POCO type shown in Figure 13, the following are characteristics of the serialized type: All public fields and properties are serialized as XML elements Elements are ordered alphabetically No elements are considered required
When NetCFSvcUtil generates a proxy for the TodoListService it also generates the XmlSerializer type shown in Figure 14 to represent the POCO type from Figure 13. Here are a few relevant points about the client-side type: It relies on XmlSerializer attributes such as XmlTypeAttribute and XmlElementAttribute to provide instructions for serialization. Page 28
wcfguidanceformobile.codeplex.com
The result is a wire-compatible type with the POCO type at the service. Figure 14: XmlSerializer type definition, wire-compatible with the POCO type in Figure 13
[System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace= "http://schemas.datacontract.org/2004/07/Entities")] public partial class TodoItem { private string descriptionField; private System.DateTime dueDateField; private bool dueDateFieldSpecified; private string idField; private string tagsField; private string titleField; [System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Order=0)] public string Description { get { return this.descriptionField; } set { this.descriptionField = value; } } [System.Xml.Serialization.XmlElementAttribute(Order=1)] public System.DateTime DueDate { get { return this.dueDateField; } set {
wcfguidanceformobile.codeplex.com
Page 29
wcfguidanceformobile.codeplex.com
Page 30
The limitation of POCO types includes lack of control over namespace and order, in addition to the limitation that all types in the inheritance tree of a POCO type must also be POCO (not data contracts or otherwise serializable types). Generally data contracts are preferred at the service. Consider the data contract implementation of the TodoItem type shown in Figure 15. Recommended practices for a data contract include providing a namespace to disambiguate serialized types on the wire and to support a versioning strategy, explicitly indicating which properties are required and not, and explicitly enforcing order rather than accepting the default alphabetical order. NetCFSvcUtil generates a wire-compatible type for this data contract as shown in Figure 16. This time, the following characteristics are worth noting: The XmlTypeAttribute uses the same namespace as the data contract. The XmlElementAttribute for each property enforces order consistent with the data contract. Since DueDate is required there is no longer a DueDateSpecified field to support omitting serialization of the element.
Figure 16: XmlSerializer type definition, wire-compatible with the data contract in Figure x
[System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace= "urn:mobilewcf/samples/2009/04/schemas")] public partial class TodoItem { private string idField; private string titleField; private string descriptionField;
wcfguidanceformobile.codeplex.com
Page 31
Fault Contracts
Service operations can optionally include fault contract declarations in order to produce metadata that indicates the type of exception details that may be reported by each operation. This is done by applying one or more FaultContractAttribute to the operation definition as follows:
[OperationContract] [FaultContract(typeof(FaultDetail))] List<TodoItem> GetItems();
The fault contract type can be any serializable type but is usually a custom data contract that exposes properties to collect useful information to report to clients beyond a simple error message. The .NET Compact Framework does not support the FaultContractAttribute and so when NetCFSvcUtil is used to generate a proxy it ignores the presence of this metadata. In the Exception Handling section we will discuss strategies for working with faults at the mobile client.
Message Contracts
Message contracts are types that represent the SOAP message including message headers and the message body. They can be used in place of complex types in the service contract enabling the following scenarios: Adding custom message headers to message requirements for an operation and publishing the custom header requirement with the service metadata Returning multiple complex types in an operation response. Serializing unwrapped messages, usually for compatibility with other platforms. Controlling which message header or message body elements should be signed or encrypted. Page 32
wcfguidanceformobile.codeplex.com
Most of these are advanced scenarios, the most popular of which is supporting custom headers. Consider the following service operation that uses two message contracts for its parameter and return type GetItemsRequest and GetItemsResponse, respectively:
[OperationContract] GetItemsResponse GetItems(GetItemsRequest requestMessage);
The message contract for the request should include one or more message body member, one for each parameter that would have been passed to the operation. The response message contract should include at least a message body member for the return type. Message body members are properties decorated with the MessageBodyMemberAttribute. For each custom header to be received by a request, or to be returned with a response (less common) a property should be added and decorated with the MessageHeaderAttribute. All header and body members should be either POCO types or serializable types usually data contracts. Figure 17 shows the request and reply message contracts for the GetItems() operation shown previously. GetItems() returns the collection of TodoItem elements, thus GetItemsRequest does not have a body member. The response, GetItemsResponse, includes a single message body member which is the collection of TodoItem types to return. Figure 17: Message contracts for the request and reply of GetItems()
[MessageContract] public class GetItemsRequest { } [MessageContract] public class GetItemsResponse { [MessageBodyMember] public List<TodoItem> Items { get; set; } }
The service operation implementation can access body members through the message contract parameter or return type. In Figure 18, the request message contract has no body members, however to return the response an instance of GetItemsResponse must be created and its body member (the Items property) initialized. Figure 18: Message contracts in the GetItems() implementation
public GetItemsResponse GetItems(GetItemsRequest requestMessage) { return new GetItemsResponse{ Items=m_globalTodoList }; }
wcfguidanceformobile.codeplex.com
Page 33
If a custom header were to be included in the message contract at the service, a header will also be added to the pseudo message contract generated for the client. However, a warning will be issued by during proxy generation and the header will not properly serialize. The details for working with custom headers will be discussed in a later section.
Proxy Generation
Earlier we walked you through the process of generating a proxy with NetCFSvcUtil for the GreetingService. Since then, we have discussed how service contracts, complex types and message wcfguidanceformobile.codeplex.com Page 34
The purpose of this section is not to regurgitate the long list of options, but to discuss the most common ways to use the tool. Figure 20 summarizes the most useful options available to NetCFSvcUtil. Figure 20: Useful NetCFSvcUtil options for proxy generation Option (Short Form) /language (/l) /out (/o) Usage By default the tool generates C# code. To generate Visual Basic (VB) code, use this option. Controls the filename of the generated proxy. One useful pattern for this is to name this for the service type with the Client suffix. For example, if the service type is TodoListService, this option would use TodoListServiceClient. This option lets you rename the file that contains the proxy base type, CFClientBase, however this does not change the type name inside the file. Usually it is better to name file with the same name as the type for clarity so this option is not typically useful. By default custom proxy types generated by the tool are not wrapped in a namespace. This option wraps types in a CLR namespace of your choosing. Typically you will use a namespace that matches that of the assembly where you will include these files. You can use this option to have the proxy reference types from a preexisting assembly instead of generating those types. This can be useful for sharing libraries between multiple projects. You should be able to use this option to control which array and dictionary types the proxy uses. For example, to use the generic type List<T> for arrays. Unfortunately this option does not appear to work so you would have to manually edit the generated proxy to modify arrays to use List<T>. This option adds an implementation of the IPropertyNotifyChanged interface on complex types imported through proxy generation. This supports oneway or two-way data binding with UI components. This option should instruct the tool to generate message contracts (wrapper types for parameters and return values) for each service operation. It appears that the tool does this by default thus this option is not necessary.
/cfClientBase (/cb)
/namespace (/n)
/reference (/r)
/collectionType (/ct)
First and foremost, you should always use the /namespace and /out options perhaps also /language if you want to generate VB output. The following illustrates how to supply these options: wcfguidanceformobile.codeplex.com Page 35
In this case, VB code will be generated and the proxy type will be named TodoListServiceClient. The proxy and all supporting types (excluding the base functionality of CFClientBase) will be generated within the namespace MobileClient which purposely matches the namespace of the client application. You can optionally map each XSD namespace from the service metadata to a different CLR namespace, but this is usually not necessary therefore by supplying * all types share the same namespace. If you have a library that includes XmlSerializer types that can be used in place of generated types, you can supply the /reference options as shown here:
netcfsvcutil /namespace:*,MobileClient /out:TodoListServiceClient /reference:sharedtypes.dll http://mobilewcf.com:8000
The previous two examples illustrate generating a proxy for a self-hosted service at the base address http://mobilewcf.com:8000. Thats because most of the code samples for this paper use a host header to provide an alternate name for the service hostname (this will be discussed further). You could also generate proxies for http://localhost:8000, or, use the IP address in place of localhost. If the service is hosted in IIS, your based address is the .svc endpoint as follows:
netcfsvcutil /namespace:*,MobileClient /out:TodoListServiceClient http://mobilewcf.com/TodoListServiceWebHost/TodoListService.svc
Once again, localhost could be used in place of mobilewcf.com if you do not configure the IIS application to use an alternate hostname. We think for mobile development the custom hostname is the better approach so well describe how to achieve that later. Since there is not the equivalent of Add Service Reference for NetCFSvcUtil, the most productive thing to do is create a command file that includes the instructions to generate a proxy for your mobile clients, and place it in the solution root so you can easily find it, generate the files, and copy them to the client app once you are sure they were correctly generated. The samples with this whitepaper include command files for this purpose.
Proxy Architecture
NetCFSvcUtil generates core base functionality and a typed proxy with associated XmlSerializer types based on the service metadata. The result is that developers can work with a friendly object model to communicate with services unless customizations are necessary. Unfortunately, there are times that you will need to modify the resulting files in order to support exception handling, custom headers, binding configurations not supported by NetCFSvcUtil and potentially other requirements specific to your implementation. In this section well discuss the architecture of the proxy so that later discussions that customize the proxy will be clear. Figure 21 provides a high level view of the types generated by NetCFSvcUtil for the TodoListService described in earlier sections. The core, reusable functionality is provided by these types: CFClientBase, wcfguidanceformobile.codeplex.com Page 36
CFClientBase wraps the channel layer and the functionality necessary to serialize and deserialize messages. It exposes two Invoke() methods: one for calls that return a value, another for calls that do not return a value. Invoke() is the core function of the proxy base type which drives communications with the channel layer. CFContractSerializer is the XmlObjectSerializer implementation that wraps the XmlSerializer as discussed earlier. An instance of this type is provided to the channel layer to serialize and deserialize messages. CFContractSerializerInfo is a type that collects settings to initialize the CFContractSerializer, and CFInvokeInfo is a type that collects settings to build the message. CFFaultException is a wrapper class that references the XML returned when a fault is returned by an operation. wcfguidanceformobile.codeplex.com Page 37
In later sections some scenarios will be discussed where proxy generation fails, generates a warning, or otherwise does not generate the code necessary to work with a particular service endpoint. These high level details will come in handy to understanding the required changes to satisfy these scenarios.
Bindings
When you configure endpoints for a service the binding controls which protocols are supported between clients and services. In order to support mobile clients you must expose at least one endpoint that employs protocols supported by the channel layer in the .NET Compact Framework. In this section well discuss which bindings and configurations are supported by the .NET Compact Framework, discuss how bindings are initialized for the generated proxy, and review some scenarios where you might modify the generated code.
Text Encoding
SOAP 1.1
Set security mode to Transport and configure transport security client credential type as Basic. Set security mode to Message and configure message security client credential type as Certificate.
There are some features that you may have expected to see in this list that are not supported by the .NET Compact Framework 3.5. Here is a summary of the most notable features that are missing: WS-Security UserName Token credentials are not supported. In theory you could roll your own implementation and pass it as a custom header over SSL. Custom headers cannot be encrypted using message security. Page 39
wcfguidanceformobile.codeplex.com
Since the .NET Compact Framework does not provide support for declarative binding configuration, the NetCFSvcUtil generates a function called CreateDefaultBinding() for every proxy. This function returns an instance of a Binding type compatible with the mobile-compatible service endpoint selected during proxy generation. This code always creates a CustomBinding with the required features enabled. For example, if the service exposed an endpoint using the defaults for BasicHttpBInding:
<endpoint address="GreetingService" binding="basicHttpBinding" contract="Greetings.Services.IGreetingService" />
CreateDefaultBInding() will include code to create a CustomBInding instance with HttpTransportBindingElement and TextMessageEncodingBIndingElement as follows:
public static Binding CreateDefaultBinding() { CustomBinding binding = new CustomBinding(); binding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8)); binding.Elements.Add(new HttpTransportBindingElement()); return binding; }
To support SOAP 1.2 the .NET Compact Framework requires that you also use WS-Addressing. To configure a SOAP 1.2 endpoint at the service the endpoint and custom binding configuration look like this:
<endpoint address="GreetingService" binding="customBinding" bindingConfiguration="Soap12WSAddressing10" contract="Greetings.Services.IGreetingService" />
wcfguidanceformobile.codeplex.com
Page 40
Unfortunately NetCFSvcUtil cannot generate a proxy for SOAP 1.2 endpoints, so an exception will be thrown by the tool. At the client you would have to pass MessageVersion.Soap11WSAddressing10 to the TextMessageEncodingBIndingElement initialization:
public static Binding CreateDefaultBinding() { CustomBinding binding = new CustomBinding(); binding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11WSAddressing10, Encoding.UTF8)); binding.Elements.Add(new HttpTransportBindingElement()); return binding; }
Security scenarios become a little more intricate but those will be discussed in a later section.
Once you have decided on a host name to use for your development environment (since, this will inevitably change as you move source through staging, test and production) you can add an IPv4 and IPv6 entry for that host name. For example, in the samples for this whitepaper we use mobilewcf.com as the host name. For this to work on your machine, add the following to your hosts file:
192.168.0.1 ::1 mobilewcf.com mobilewcf.com
You must replace 192.168.0.1 with your actual IPv4 address. This must be the desktops actual IP address on the network dont use 127.0.0.1. Also, for the device to communicate with your desktop you must have network connectivity.
You can also do this from the Visual Studio command line as follows:
cscript //nologo %systemdrive%\inetpub\adminscripts\adsutil.vbs set W3SVC/1/ServerBindings ":80:mobilewcf.com"
To confirm that it worked, use this command line instruction: wcfguidanceformobile.codeplex.com Page 42
NOTE: This command line instruction modifies the host name for the Default Web Site in IIS as indicated by the number 1 in W3SVC/1/ServerBindings. You should probably create a new web site for the samples so that your Default Web Site can remain as localhost for other development efforts. In that case, assuming that you only have two web sites, change the command line to: W3SVC/2/ServerBindings. To modify the host name for HTTPS requests you must use a command line instruction. First, create a certificate using makecert.exe that matches the host name you will be using and install this certificate to your local machine Personal certificate store (see code download instructions for details on how to do this). For convenience we have included a certificate for our samples named mobilewcf.com. Add this cert to the Server Certificates for the machine using IIS Manager. Select the machine node and the Server Certificates feature as shown in Figure 25. Import mobilewcf.com.pfx (the password is mobilewcf.com). Figure 25: Server Certificates feature in IIS Manager
wcfguidanceformobile.codeplex.com
Page 43
Figure 27: Site Bindings for HTTP and HTTPS using the designated certificate
Next, execute the following command line to modify the host name:
cscript //nologo %systemdrive%\inetpub\adminscripts\adsutil.vbs set W3SVC/1/SecureBindings ":443:mobilewcf.com"
To verify the host name was changed, execute this command line:
cscript //nologo %systemdrive%\inetpub\adminscripts\adsutil.vbs get W3SVC/1/SecureBindings
NOTE: As mentioned for the HTTP binding, if you create a new web site, modify the number in W3SVC/1/SecureBIndings to match the ordinal number of the web site. Verify from the site bindings dialog once more that the HTTPS binding shows the correct host name, and that the certificate is indeed still set to mobilewcf.com. Reset IIS after making these changes. From the Visual Studio command line type iisreset. NOTE: The code download includes a detailed set of instructions for working with certificates including generating certificates with makecert.exe, installing them into the various certificate stores, and working with IIS Manager for web site certificates.
Exception Handling
In this section well explore how WCF services handle exceptions and faults, and more importantly, how to update the code generated by the NetCFSvcUtil so that mobile clients can work with faults and report useful information to the user.
You can enable the debugging behavior to include exception details in faults reported by the service as follows: wcfguidanceformobile.codeplex.com Page 45
This is not a solution, however, to reporting useful information to the client since it includes the stack trace and other information that may not be appropriate for clients to view. Instead, services should throw faults. To throw a fault, the service can use the FaultException type. At a minimum an error message should be reported, but you can also include a fault code which provides additional details to the client. The following illustrates how to throw a fault without the fault code, and one that includes the fault code:
throw new FaultException("You must provide a valid name to receive a greeting."); throw new FaultException("You must provide a valid name to receive a greeting.", FaultCode.CreateSenderFaultCode("SenderFault", "urn:mobilewcf.com/samples/2009/04"));
A simple fault will result in a SOAP fault similar to the following (based on SOAP 1.1):
<s:Fault xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <faultcode>s:Client</faultcode> <faultstring xml:lang="en-US"> You must provide a valid name to receive a greeting. </faultstring> </s:Fault>
Typically, the service can get away with throwing a simple FaultException without a code because most clients require only a string message to present to the user. At times, however, the service implements a rich error handling strategy that returns additional details to the client, beyond a simple error message and error code. In this case, the service can use declared faults.
Declared Faults
When the service wants to share additional details with the client in the event of an exception, they throw faults using FaultException<T>. The generic type parameter, T, can be any serializable type that includes details to share with the client. Typically, this is a custom type, not a CLR exception since the former is interoperable and the latter is not. Figure 28 shows an example of a data contract named FaultDetail that exposes properties for information about the original CLR exception. This is just one example of the type of additional information the service might want to provide with the fault. The code wcfguidanceformobile.codeplex.com Page 46
If the service throws a fault with FaultException<FaultDetail>, a SOAP fault is returned including an additional detail element containing the serialized version of the FaultDetail. In order to produce metadata for clients so that they can process this detail services usually declare faults in the service contract by applying the FaultContractAttribute to operations that can throw this type of fault:
[OperationContract] [FaultContract(typeof(FaultDetail))] string Greet(string firstname);
wcfguidanceformobile.codeplex.com
Page 47
The problem with this is that the based type, CommunicationException, is not provided with a friendly value for its Message property. Thus, the user will always see CFFaultException as the error message with the proxy-generated implementation. In order to provide even the most basic error handling for mobile clients changes must be made to CFClientBase and CFFaultException. In addition, a few new supporting types must be created to compliment the new version of CFFaultException.
wcfguidanceformobile.codeplex.com
Changes to CFClientBase
A message can only be read once, so in order to provide an unread copy of the message to CFMessageFault we must first buffer the message prior to attempting to read the reply. The MessaegBuffer instance is used to create a new copy of the message to process the reply, and again (if necessary) to create the CFMessageFault. The CFMessageFault instance is used to initialize an instance of CFFaultExceptionEx, instead of CFFaultException. Changes to CFClientBase are highlighted in Figure 32.
wcfguidanceformobile.codeplex.com
Page 49
CFMessageFault exposes a GetMessagFault() method which does the work of deserializing the SOAP fault. The code must handle the subtle differences between SOAP 1.1 and SOAP 1.2 faults and initialize a set of properties that represent the fault code, reason, and any detail element if present. Figure 33 shows the implementation details of CFMessageFault (omitting some details for brevity). Figure 33: CFMessageFault implementation
public class CFMessageFault { public string Reason { get; private set; } public CFFaultCode FaultCode { get; private set; } public bool HasDetail { get; private set; } protected MessageBuffer MessageBuffer { get; private set; } // other constructors public CFMessageFault(string reason, CFFaultCode faultCode, Message faultMessage) { this.Reason = reason; this.FaultCode = faultCode; if (faultMessage != null) { this.MessageBuffer = faultMessage.CreateBufferedCopy(int.MaxValue); this.HasDetail = true;
wcfguidanceformobile.codeplex.com
Page 50
wcfguidanceformobile.codeplex.com
Page 51
The reason for creating the CFMessageFault type is so that the message can be processed and the fault reason (the error message) extracted before constructing an instance of CFFaultExceptionEx. This is the only way to provide a reasonable error message to the base type, CommunicationException. This value must be provided to the constructor as it is read-only, thus both CFFaultExceptionEx constructors call down to the base constructor. Figure 34 shows the listing of both CFFaultExceptionEx and CFFaultCode. Figure 34: CFFaultExceptionEx and CFFaultCode implementation
public class CFFaultExceptionEx : CommunicationException { public string Reason { get; private set; } public CFFaultCode FaultCode { get; private set; } public bool HasDetail { get; private set; } public CFMessageFault Fault { get; private set; } public CFFaultExceptionEx(string reason) : base(reason) { this.FaultCode = CFFaultCode.DefaultFaultCode; this.Reason = reason; this.HasDetail = false; } public CFFaultExceptionEx(CFMessageFault fault) : base(fault.Reason) { this.FaultCode = fault.FaultCode; this.Reason = fault.Reason; this.HasDetail = fault.HasDetail; this.Fault = fault; } } public class CFFaultCode { public string Name { get; private set; } public string Namespace { get; private set; } public FaultCode SubCode { get; private set; } // other constructors public CFFaultCode(string name, string ns, FaultCode subCode) { if (String.IsNullOrEmpty(name)) throw new InvalidOperationException("The parameter 'name' cannot be null or empty"); this.Name = name; this.Namespace = ns; this.SubCode = subCode;
wcfguidanceformobile.codeplex.com
Page 52
The end result of these modifications is that your client code can catch CFFaultExceptionEx and show the fault code and reason, or optionally catch Exception and show the error message as follows:
catch (CFFaultExceptionEx faultEx) { MessageBox.Show(string.Format("FaultCode: {0}\r\nFaultReason: {1}", faultEx.FaultCode, faultEx.Reason)); } catch (Exception ex) { MessageBox.Show(ex.Message); }
To use these types you will add a reference to our custom library, Mobile.ServiceModelEx, which contains these and other enhanced files to support mobile clients. You will then modify your proxy to inherit CFClientBaseEx instead of CFClientBase. The rest is done for you! NOTE: You can find Mobile.ServiceModelEx in the code download for this whitepaper.
wcfguidanceformobile.codeplex.com
Page 53
Message Contracts
Before discussing the implications on your mobile applications, lets review how message contracts are used by a WCF service implementation to require custom headers. Message contracts are an easy way to implement operations that require custom headers at the service. A message contract is a type that represents the SOAP message the message body and any custom message headers. Typically a message contract is used for both the request and reply in an operation definition as shown in the following service contract for the GetItems() operation:
[ServiceContract(Namespace="urn:mobilewcf/samples/2009/04")] public interface ITodoListService { [OperationContract] GetItemsResponse GetItems(GetItemsRequest requestMessage); // other operations }
GetItemsRequest and GetItemsResponse are each types decorated with the MessageContractAttribute. GetItemsRequest includes a single message header named LicenseKey, and no message body members since the GetItems() operation requires no parameters:
[MessageContract] public class GetItemsRequest { [MessageHeader] public string LicenseKey { get; set; } }
GetItemsResponse includes a single message body member named header named Items, but no message headers:
[MessageContract] public class GetItemsResponse { [MessageBodyMember] public List<TodoItem> Items { get; set; } }
In the service contract implementation the LicenseKey header is accessed as a property of the deserialized GetItemsRequest instance. If the LicenseKey property is valid, an instance of the GetItemsResponse type is constructed to return the item collection:
public GetItemsResponse GetItems(GetItemsRequest requestMessage) { if (requestMessage.LicenseKey != "XXX") {
wcfguidanceformobile.codeplex.com
Page 54
The metadata for this service describes the requirement to pass the LicenseKey header to aide in proxy generation.
Despite this message, at first blush it may appear that the proxy supports passing the LicenseKey header to the GetItems() operation. The proxy indeed exposes a GetItems() method that takes a single parameter, the LicenseKey string value:
TodoItem[] todoList = proxy.GetItems("XXX");
The implementation in the proxy also appears to pass this value to an internal GetItems() method that relies on similar message contracts: GetItemsRequest and GetItemsResponse:
public TodoItem[] GetItems(string LicenseKey) { GetItemsRequest request = new GetItemsRequest(LicenseKey); GetItemsResponse response = this.GetItems(request); return response.Items; }
This pattern is consistent with the service contract implementation using message contracts, except that the outgoing and incoming message types are defined as XML serializable types. As the NetCFSvcUtil warning stated, however, the proxy only supports a reduced set of functionality for calling the service. Specifically, the internals of the generated proxy does not include code to handle message headers. Indeed the GetItemsRequest type is initialized with the LicenseKey value, however since message contracts are not supported, there is no mechanism for indicating that this value is to be serialized as a message header. To prevent serializing the value as part of the message body, the LicenseKey property is decorated with the XmlIgnoreAttribute:
[System.Xml.Serialization.XmlRootAttribute(ElementName="GetItemsRequest", Namespace="urn:mobilewcf/samples/2009/04")] public partial class GetItemsRequest {
wcfguidanceformobile.codeplex.com
Page 55
Removing this attribute would not achieve the goal of serializing the value as a message header. It would merely add another element to the SOAP body serialization which would be ignored by the service. Additional code is required to include this property as a message header for the outgoing message. Ultimately, one of the two Invoke() methods exposed by CFClientBase (one is for two-way operations, the other for one-way operations) handles building the message to be sent to the service operation. Invoke() is a generic method that receives two parameters: A CFInvokeInfo type which provides information to the serializer such as the type to serialize A request parameter typed for the message contract type for this particular operation which describes the message body
What you ultimately want is to add message headers to the Message type that is created during Invoke(). If the LicenseKey header were to be the same for all calls, you could add code to write the
wcfguidanceformobile.codeplex.com
Page 56
Note that this assumes that the LicenseKey header name, namespace and value are all known to the proxy base type, CFClientBase. To achieve reuse there must be a way to pass the header to the Invoke() method in a way that it can generalize creating the header.
In the Mobile.ServiceModelEx implementation (introduced earlier) these changes have been applied to the new type CFClientBaseEx, and enhancements described to the other types are included in this library as well. Figure 35 highlights these changes. Figure 35: Changes to the generated CFClientBase proxy and related types to accommodate outgoing header serialization
public partial class CFClientBaseEx<TChannel> where TChannel : class {
wcfguidanceformobile.codeplex.com
Page 57
wcfguidanceformobile.codeplex.com
Page 58
wcfguidanceformobile.codeplex.com
Page 59
The result is that the proxy implementation can, for each operation that requires a header, initialize the CFInvokeInfo type with that information. Figure 36 illustrates the changes made to the customized version of the GetItems() method to pass the LicenseKey header. Figure 36: Adding headers to the CFInvokeInfo type for serialization
private MobileClient.GetItemsResponse GetItems(MobileClient.GetItemsRequest request) { CFInvokeInfo info = new CFInvokeInfo(); info.Action = "urn:mobilewcf/samples/2009/04/ITodoListService/GetItems"; info.RequestIsWrapped = true; info.ReplyAction = "urn:mobilewcf/samples/2009/04/ITodoListService/GetItemsResponse"; info.ResponseIsWrapped = true; CFContractSerializerInfo headerSerializationInfo = new CFContractSerializerInfo(); headerSerializationInfo.MessageContractType = typeof(string); headerSerializationInfo.IsWrapped = info.RequestIsWrapped; headerSerializationInfo.ExtraTypes = info.ExtraTypes; headerSerializationInfo.UseEncoded = info.UseEncoded; CFContractSerializer headerSerializer = new CFContractSerializer(headerSerializationInfo); info.Headers = new MessageHeaders(base.MessageVersion); info.Headers.Add(MessageHeader.CreateHeader("LicenseKey", "urn:mobilewcf/samples/2009/04", request.LicenseKey, headerSerializer));
wcfguidanceformobile.codeplex.com
Page 60
Unfortunately there is no way to avoid modifying the generated proxy to support custom headers but you can minimize impact by using the CFClientBaseEx. NOTE: You can find Mobile.ServiceModelEx in the code download for this whitepaper.
Figure 36 show the listing for the ITodoListService contract using WebGetAttribute and WebInvokeAttribute to describe REST-based requirements. Operations that return a value use the WebGetAttribute which means an HTTP GET will be used to call the operation. Other operations use the WebInvokeAttribute specifying the HTTP verb POST (to create items), PUT (to update items), or DELETE (to delete items). In this case both attributes are instructed to use XML format for communications instead of JSON since support for the latter is not build-in to the .NET Compact Framework. Figure 36: REST-based service contract definition for ITodoListService
[ServiceContract(Namespace="urn:mobilewcf/samples/2009/04")] public interface ITodoListService
wcfguidanceformobile.codeplex.com
Page 61
The .svc endpoint uses the WebServiceHostFactory to activate the service using the WebServiceHost which installs behaviors that bypass traditional SOAP filtering.
<%@ ServiceHost Service="TodoList.TodoListService" Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>
As for the binding configuration for the REST-based endpoint, the defaults for WebHttpBinding are used in this simple example.
<system.serviceModel> <services> <service name="TodoList.TodoListService"> <endpoint address="" binding="webHttpBinding" contract="Contracts.ITodoListService" /> </service> </services> </system.serviceModel>
Using HttpWebRequest
There is no concept of proxy generation for REST-based services and so the HttpWebRequest object is used. That means you need to know the Url for the service endpoint, the Uri to invoke each operation, the required XML to pass to operations, and the expected XML to receive back from operations. Figure 37 shows how to use HttpWebRequest to call the GetItems() operation exposed by the TodoListService. Here are some points worth noting: You provide the Url to the service when you create the HttpWebRequest instance. The suffix /Items matches the template Uri indicated in the WebGetAttribute from Figure x. Since this is an HTTP GET request, the Content-Length header is set to 0. For a POST or PUT request this header should be set to the size of the data being passed. Page 62
wcfguidanceformobile.codeplex.com
Using HttpWebRequest is very simple, but it lacks the convenience of automatic serialization and deserialization that would be provided by a WCF proxy.
Since the client isnt passing credentials in this case, no additional initialization is required for the proxy to call the service endpoint.
Basic Authentication
With basic authentication a username and password are passed in HTTP headers, in the clear. For this reason you will usually rely on an SSL connection to protect the credentials. To support basic authentication without SSL encryption at the WCF service, you can configure BasicHttpBinding as follows:
<basicHttpBinding> <binding name ="basicAuth">
wcfguidanceformobile.codeplex.com
Page 64
To support basic authentication the preferred way, with SSL encryption, use the following configuration:
<basicHttpBinding> <binding name ="basicAuth"> <security mode="Transport"> <transport clientCredentialType="Basic" /> </security> </binding> </basicHttpBinding>
In addition, your IIS application must support Basic Authentication. Unfortunately the .NET Compact Framework does not support basic authentication scenarios using the channel layer. You can work around this by using HttpWebRequest to communicate with the SOAPbased service, with some modifications to how we discussed using this for REST-based services earlier including: All requests use the HTTP POST verb The Content-Type header is set to text/xml instead of application/xml For SOAP 1.1 requests you must add a SOAPAction HTTP header indicating the operation to invoke You must manually build the XML to POST for each request including the SOAP envelope and namespaces according to the SOAP version to be used
Figure 39 illustrates using the HttpWebRequest to call the GetItems() operation exposed by the TodoListService over the binding illustrated previously. In order to pass credentials using basic authentication a new NetworkCredentials instance is created and assigned to the Credentials property of the HttpWebRequest instance. To pass credentials you will also set PreAuthenticate to true to prepopulate authentication headers on the request and avoid redirects, and set AllowWriteStreamBuffering to true as this is a requirement after setting the Credentials property. A SOAP message is generated according to the requirements of the operation. In this case, an empty element <GetItems> is passed within the SOAP body. The message is written to the request stream first, and then GetResponse() leads you to the response stream that contains the result of the call. This response will contain a SOAP fault if the call failed, otherwise it will contain XML according to the operation result. Figure 39: HttpWebRequest invoking a SOAP-based service using basic authentication
HttpWebRequest webRequest = HttpWebRequest.Create(
wcfguidanceformobile.codeplex.com
Page 65
Digest Authentication
With digest authentication is implemented with a challenge/response protocol whereby the service sends material to the client to be used to hash the password before sending a username and password to the server. The server then compares the hashed password with the hashed password at the server to authenticate the caller. To support digest authentication without SSL encryption at the WCF service, you can configure BasicHttpBinding as follows:
<basicHttpBinding> <binding name ="basicAuth"> <security mode="TransportCredentialOnly"> <transport clientCredentialType="Digest" /> </security> </binding>
wcfguidanceformobile.codeplex.com
Page 66
To support digest authentication with SSL encryption, use the following configuration:
<basicHttpBinding> <binding name ="basicAuth"> <security mode="Transport"> <transport clientCredentialType="Digest" /> </security> </binding> </basicHttpBinding>
In addition, your IIS application must support Digest Authentication. You should follow the instructions discussed in the previous section for basic authentication, to use the HttpWebRequest for digest authentication.
You will initialize the security features of HttpWebRequest as you did to secure calls to SOAP-based services, and handle messaging payload for GET, POST, PUT and DELETE calls to service operations according to the earlier discussion about REST-based services and HttpWebRequest.
The service can also use a CustomBInding to describe the same configuration. This is sometimes necessary when features not exposed by BasicHttpBinding must also be configured. For example, to expose an endpoint that supports SOAP 1.2 with WS-Addressing 1.0 the configuration in Figure 41 can be employed. The <security> element illustrates the only allowed settings for defaultAlgorithmSuite, authenticationMode and messageSecurityVersion for mobile clients. Figure 41: Custom binding equivalent for mutual certificate security
<customBinding> <binding name="MutualCertCustom"> <security defaultAlgorithmSuite="Basic256Rsa15" authenticationMode="MutualCertificate" messageSecurityVersion=
wcfguidanceformobile.codeplex.com
Page 68
Through proxy generation, the binding equivalent of the configuration in Figure 41 is shown in Figure 42. To initialize the proxy you must provide a client certificate to authenticate to the service, and a service certificate to use for encrypting messages. That means installing the certificates to the device ahead of time (to be discussed) and initializing the proxys ClientCredentials the ClientCertificate and ServiceCertificate properties. This is illustrated in Figure 43. The client certificate is initialized from the My store, the service certificate from the Root store. Figure 42: Programmatic configuration for mutual certificate security at the mobile client
CustomBinding binding = new CustomBinding(); binding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8)); AsymmetricSecurityBindingElement asbe = new AsymmetricSecurityBindingElement(new X509SecurityTokenParameters(X509KeyIdentifierClauseType.Any, SecurityTokenInclusionMode.Never), new X509SecurityTokenParameters(X509KeyIdentifierClauseType.Any, SecurityTokenInclusionMode.AlwaysToRecipient)); asbe.MessageSecurityVersion = MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFeb ruary2005WSSecurityPolicy11BasicSecurityProfile10; asbe.LocalClientSettings.DetectReplays = false; asbe.LocalServiceSettings.DetectReplays = false; asbe.DefaultAlgorithmSuite = SecurityAlgorithmSuite.Basic256Rsa15; asbe.SetKeyDerivation(false); binding.Elements.Add(asbe); binding.Elements.Add(new HttpTransportBindingElement()); return binding;
Figure 43: Initializing CFClientBase with certificates for mutual certificate authentication
Binding binding = GreetingServiceClient.CreateDefaultBinding(); EndpointAddress ep = new EndpointAddress( GreetingServiceClient.EndpointAddress.Uri); GreetingServiceClient m_proxy = new GreetingServiceClient(binding, ep); m_proxy.ClientCredentials.ClientCertificate.SetCertificate( StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "mobileclient"); m_proxy.ClientCredentials.ServiceCertificate.SetDefaultCertificate( StoreLocation.CurrentUser, StoreName.Root, X509FindType.FindBySubjectName, "mobilewcf.com");
wcfguidanceformobile.codeplex.com
Page 69
From the device emulator Start menu, select the File Explorer to browse for the certificates. Figure 45 shows the files copied to the Application Data directory. From here, simply click on each file to install them into the certificate store. In the case of mobileclient.pfx, a password will be requested (in this case the password is mobileclient). You will be presented with a message indicating that the certificate has been successfully installed. Private keys are installed to the My certificate store, and certificates are installed to the Root certificate store. Figure 45: Installing certificates copied to the device
wcfguidanceformobile.codeplex.com
Page 70
Deployment Considerations
Pushing new applications and updates to rich clients on the desktop has always been a challenge in itself, but what do you do when the physical deployment device is not even connected to the network and out of the office most of the time? Web-based applications gained a lot of popularity thanks to centralized management and deployment, but when connectivity cannot be taken for granted in the field the majority of mobile scenarios have to adopt a smart client deployment model. One such smart feature is the ability of a smart client to automatically deploy itself without requiring setup kits and manual or scripted deployments. Web clients have always been popular thanks to the centralized nature of the application on the Web server where no client deployment is needed. Smart clients reuse this model by living inside a Web server as well, ready to be initiated. When first accessed by a new user/client, the smart client downloads itself to the client machine or device along with all its dependencies, therefore eliminating the need for a local installation before the application can be used. Technologies like ClickOnce in .NET Framework 2.0 and higher can provide such functionality for desktop scenarios, but unfortunately not for mobile device applications. Mobile smart clients typically need to be deployed to the device first, and then a bootstrapper can manage updates as they become available on the deployment server. wcfguidanceformobile.codeplex.com Page 71
Acknowledgements
Very few resources exist for mobile applications communicating with WCF services. We would like to recognize the few people who have online resources on the subject of mobile development with WCF including Andrew Arnott and Chris Tacke. In addition, there are several people at Microsoft who were a great help to us as we pulled together this information including Mahathi Mahabhashyam, Amit Chopra, and Kirill Gavrylyuk.
wcfguidanceformobile.codeplex.com
Page 72