Thursday 2 August 2018

Streaming audio with WCF

This article will present a way to stream audio with WCF. In my example, I will play a Waveform Audio File Format file (.wav), but my sample should support any well-known audio formats. Note that WCF is not automatically the best streaming service to provide to your clients. Other protocols such as RTSP have better handling of graceful degradation, random seek and so on. This article will only show how you can provide a simple one-direction playback of audio files by using WCF. You can start by cloning the repository I have made public from here:

git@bitbucket.org:toreaurstad/wcfaudiostreamdemo.git

First off, we need to specify a content type of the method that returns a Stream with the audio. This can be achieved by implementing a class that inherits from System.Attribute and implementing IOperationBehavior. The class below sets a content type in its constructor and sets a Formatter of the DispatcherOperation in ApplyDispatchBehavior to the class ContentTypeMessageFormatter.


using System;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;


namespace WcfStreamAudioDemo.Common
{
    public class ContentTypeAttribute : Attribute, IOperationBehavior
    {
        public ContentTypeAttribute(string contentType)
        {
            ContentType = contentType;
        }

        public string ContentType { get; } //readonly auto property

        public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
        {
          
        }

        public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
        {
            
        }

        public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
        {
            // ReSharper disable once ArrangeThisQualifier
            dispatchOperation.Formatter = new ContentTypeMessageFormatter(dispatchOperation.Formatter, this.ContentType);
        }

        public void Validate(OperationDescription operationDescription)
        {
            
        }

      }
}

The ContentTypeMessageFormatter class is shown below:

using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
namespace WcfStreamAudioDemo.Common
{
    public class ContentTypeMessageFormatter : IDispatchMessageFormatter
    {

        private readonly IDispatchMessageFormatter _formatter;
        private readonly string _contentType;

        public ContentTypeMessageFormatter(IDispatchMessageFormatter formatter, string contentType)
        {
            _formatter = formatter;
            _contentType = contentType; 
        }

        public void DeserializeRequest(Message message, object[] parameters)
        {
            _formatter.DeserializeRequest(message, parameters);
        }

        public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
        {
            if (!string.IsNullOrEmpty(_contentType))
                WebOperationContext.Current.OutgoingResponse.ContentType = _contentType;
            return _formatter.SerializeReply(messageVersion, parameters, result);
        }

    }
}

Next step is to define a Service Contract in WCF to test this out:

using System.IO;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace WcfStreamAudioDemo.Common
{

    [ServiceContract]
    public interface IAudioServiceContract
    {

        [OperationContract]
        [ContentType("audio/wav")]
        [WebGet(UriTemplate = "media/{trackName}")]
        Stream GetAudio(string trackName);
    }
}

The configuration of the WCF service in web.config sets up streaming by using transferMode set to Streamed on a webHttpBinding.

  <system.serviceModel>

    <services>
      
      <service name="WcfStreamAudioDemo.Host.AudioService">
        <endpoint behaviorConfiguration="RestBehaviorConfig" binding="webHttpBinding" bindingConfiguration="HttpStreaming" contract="WcfStreamAudioDemo.Common.IAudioServiceContract" />
      </service>
    </services>

    <bindings>
      <webHttpBinding>
        <binding name="HttpStreaming" transferMode="Streamed" maxReceivedMessageSize="1000000000" />
      </webHttpBinding>
    </bindings>
    
    <behaviors>

      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
     
      <endpointBehaviors>
        <behavior name="RestBehaviorConfig">
          <webHttp />
        </behavior>
      </endpointBehaviors>

    </behaviors>
    <protocolMapping>
        <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>


Finally, to test out this Audio Service you can either enter the url of an audio file the WCF service recognizes into a browser to download the .WAV file or for example use an HTML 5 <audio> element. Here is a sample page I created to test out the Audio service (a simple ASP.NET Web form)!

<%@ Page Language="C#" %>

<script runat="server">

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Demo Audio Stream wcf</title>
</head>
<body>
<form id="HtmlForm" runat="server">
    <div>
        
        <h3>WCF Sample Audio streaming example</h3>
        
        <p>Playing an audio file (.wav) using a custom WCF IDispatchMessageFormatter with REST (webHttpBinding).</p>
        
        <audio controls>
            <source src="http://localhost/WcfStreamAudioDemo.Host/AudioService.svc/media/Sample" type="audio/wav" />
            <p>Your browser doesn't support HTML5 audio. Here is a <a href="http://localhost/WcfStreamAudioDemo.Host/AudioService.svc/media/Sample.wav">link to the audio</a> instead.</p> 
        </audio>

    </div>
</form>
</body>
</html>


Wednesday 1 August 2018

Controlling WCF serialization and deserialization with IXmlSerializable

It is possible to customize how WCF serializes and deserializes objects to be sent over the wire. The default way to serialize and deserialize objects with WCF is using DataContractSerializer. There are many scenarios where you instead want to control this more in your WCF services. For interoperability scenarios with other platforms or perhaps you want to change the way WCF serializes objects. Perhaps using attributes more than child elements, so the XML can be condensed more. Either way, you can end up in situations where you must handle the serialization and deserialization in WCF in more detail, then IXmlSerializable is one option. Read on for a simple demo. Another, more fine-grained way of actually customizing the way WCF serializes objects and deserialize them is using IXmlSerializable interface. I have added a solution on Bitbucket, which allows you to see how this is done here:

git clone https://toreaurstad@bitbucket.org/toreaurstad/wcfixmlserializabledemo.git The source code is here: WcfIXmlSerializableDemo
The core of the serialization is done implementing the interface IXmlSerializable. The following class serves as a demonstration:
 using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace CustomWcfSerialization.Common
{
  
    // Use a data contract as illustrated in the sample below to add composite types to service operations.
    [XmlRoot("Animal", Namespace ="http://schemas.toreaurstad.no/2018/08")]
    public class Animal : IXmlSerializable
    {
       
        public Animal()
        {

        }

        bool _isBipedal;
        public bool IsBipedal
        {
            get { return _isBipedal; }
            set { _isBipedal = value; }
        }

        string _name;
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(XmlReader reader)
        {
            reader.MoveToContent();
   
            Name = reader.GetAttribute("Name");
            reader.ReadStartElement(); 

            IsBipedal = bool.Parse(reader.ReadElementString("IsBipedal") == "Yes" ? "true" : "false");
            reader.ReadEndElement(); 
        }

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteAttributeString("Name", Name);
            writer.WriteElementString("IsBipedal", IsBipedal ? "Yes" : "No");
        }

    }
}

The serialized request and response from WCF now is changed from the default serialization of DataContractSerializer, to not only support XML attributes - but also represent booleans as a custom boolean where true and false is exchanged with "Yes" and "No".

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body>

<GetAnimalsResponse xmlns="http://tempuri.org/">
<GetAnimalsResult xmlns:a="http://schemas.datacontract.org/2004/07/CustomWcfSerialization.Common" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Animal Name="Rex"><IsBipedal>No</IsBipedal></a:Animal>
<a:Animal Name="Bubbles"><IsBipedal>Yes</IsBipedal>
</a:Animal>
</GetAnimalsResult> </GetAnimalsResponse></s:Body></s:Envelope>

Note though, that even we used special values for boolean values in our sample ("Yes" and "No"), it was possible to deserialize this by implementing the deserialization in the ReadXml method of our WCF service entity class Animal. This image shows that Visual Studio is able to deserialize the data into objects properly even that the XML presents data that is not directly compatible by .NET:




This is done by manually parsing the data retrieved from WCF like this:

IsBipedal = bool.Parse(reader.ReadElementString("IsBipedal") == "Yes" ? "true" : "false");

You can quickly end up with much code if there is much code you want to serialize in a specific manner. It is possible use .NET reflection, generics and the [KnownType] argument for this in case you want to support this is a more generic manner. I will look into this in a future article.

Tuesday 24 July 2018

Getting started with Transactions in WCF

Transactions is a powerful concept in many parts of the software industry, they ensure that two or more procedures are either all carried out and persisted, or not any of them. This ensures that data is consistent, that the operation that performs the procedures are atomic, it is isolated and durable (ACID-principle). But what about WCF? Is it possible to easily support transactions in WCF in a full-stack scenario? Can you try to carry out two or more WCF service calls and either have all the calls changes on data in for example a SQL Server database persisted or not any of them? Is it possible to make a transaction that spans two or more WCF service calls? Yes it is! I have created a sample solution here that you can clone with Git:

git clone https://toreaurstad@bitbucket.org/toreaurstad/wcfdemotransactions.git

The repository with the code sample is available as a public repository on Bitbucket where you can view the code here: https://bitbucket.org/toreaurstad/wcfdemotransactions/src/master/ The code sample is a full-stack WPF application with a backend implemented in WCF serviced under WAS / IIS and the data layer uses Entity Framework and Model first (EDMX). This scenario will display that we can implement transactions that span multiple WCF calls and be able to either commit the update of data in the database that all these WCF calls inflict, or abort them all, i.e. a transaction. The GUI looks like this:


Enabling transactions for the WCF service

First off, enable transactionflow on the binding of the service (web.config)

  <system.serviceModel>

    <bindings>
      <wsHttpBinding>
        <binding name="wsHttpBindingWithTransactionFlow"  transactionFlow="true" >
          <security>
            <transport clientCredentialType="None"></transport>
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>

    <services>
      <service name="WcfTransactionsDemo.ServiceImplementation.SampleServiceImplementation" behaviorConfiguration="SampleServiceBehavior">
        <endpoint bindingConfiguration="wsHttpBindingWithTransactionFlow" binding="wsHttpBinding" address="http://localhost/WcfTransactionsDemo.Host/sampleservice.svc" contract="WcfTransactionsDemo.Common.ServiceContract.ISampleServiceContract"></endpoint>
      </service>
    </services>

 
Next, define that the transactionflow from the client is mandatory in the WCF methods that will support this in the Service Contract of the WCF service: (this is done setting the TransactionFlow attribute to Mandatory on the WCF service methods (operations) that will join a transaction flowed downstream from the client.

 [ServiceContract(Namespace = Constants.ServiceContractsNamespace)]
    public interface ISampleServiceContract
    {

        [OperationContract]
        [FaultContract(typeof(FaultDataContract))]
        [TransactionFlow(TransactionFlowOption.NotAllowed)]
        List GetAllCustomers();

        [OperationContract]
        [FaultContract(typeof(FaultDataContract))]
        [TransactionFlow(TransactionFlowOption.NotAllowed)]
        List GetAllProducts();

        [OperationContract]
        [FaultContract(typeof(FaultDataContract))]
        [TransactionFlow(TransactionFlowOption.Mandatory)]
        string PlaceOrder(OrderDataContract order);

        [OperationContract]
        [FaultContract(typeof(FaultDataContract))]
        [TransactionFlow(TransactionFlowOption.Mandatory)]
        string AdjustInventory(int productId, int quantity);

        [OperationContract]
        [FaultContract(typeof(FaultDataContract))]
        [TransactionFlow(TransactionFlowOption.Mandatory)]
        string AdjustBalance(int customerId, decimal amount);

    }
Next, specify the transaction isolation level of the WCF service implementation, using a ServiceBehavior attribute.

    [ServiceBehavior(TransactionIsolationLevel = IsolationLevel.Serializable, TransactionTimeout = "00:00:30")]
    public class SampleServiceImplementation : ISampleServiceContract
    {
Serializable is default in .NET and provides the consistency, it is though not recommended in high traffic scenarios as it causes too much database locking. (ReadCommitted can then be used for instance instead) Each WCF method in the service implementation that will join a transaction flowing from the client now specifies this with an OperationBehavior attribute, for example as the sample solution's AdjustBalance method:
         [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] 
        public string AdjustBalance(int customerId, decimal amount)
        {

Setting up a transaction at the client side

The WCF service is now configured to support transactions in our sample demo. Moving on next to the app.config file, updating service reference should set the transaction flow attribute correct. Note that the servicePrincipalName in this demo must be adjusted to match your computer's name (or use localhost).

<system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="WSHttpBinding_ISampleServiceContract" transactionFlow="true"  />
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost/WcfTransactionsDemo.Host/sampleservice.svc"
                binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ISampleServiceContract"
                contract="SampleService.ISampleServiceContract" name="WSHttpBinding_ISampleServiceContract">
                <identity>
                    <servicePrincipalName value="host/AlienHivemind" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>

The demo of this article works in the following manner :
  • Client selects a row from the list of customers in the GUI
  • Client selects a row from the list of products in the GUI
  • Client enters a quantity of the selected product to order
  • Client clicks Place Order to place the order
If the client does this, three WCF service calls are invoked. If no transactions were done, the client could cause inconsistent data in the database. There are three WCF calls (this scenario is perhaps not so realistic, but it gives you the idea of WCF calls that are related and should all succeed or neither succeed. Ignore for now the semantics and just accept the fact that there are multiple WCF calls that must succeed or not any of them).

Now try to do the following: Do not select a customer but select a product. Also enter a quantity such as 10. Then click the button to place an order. What happens is that the WCF service expects the client to have selected a customer and a product. Since the client has not selected a customer, the AdjustBalance() methods throws a FaultException at the WCF service. The method AdjustInventory() however succeeds. If there was no transaction scope at the client side, you would see that the InStock / OnHand value is reduced, but there are no Balance reduction on either Customer, since the client forgot to select a Customer. Actually, using WCF transaction, it is possible to roll back data and get consistent data still - since the client defines a transaction. The client does it in the following manner:


    private void PlaceOrderCommandExecute(object obj)
        {
            using (var client = new SampleServiceContractClient())
            {

                using (var transactionScope = new TransactionScope())
                {
                    try
                    {


                        string orderPlacement = client.PlaceOrder(new OrderDataContract
                        {
                            CustomerId = SelectedCustomer != null ? SelectedCustomer.CustomerId : 0,
                            ProductId = SelectedProduct != null ? SelectedProduct.ProductId : 0,
                            Quantity = Quantity
                        });
                        MessageBox.Show("Placing order: " + orderPlacement);



                        string adjustedInventory = client.AdjustInventory(SelectedProduct != null ?
                            SelectedProduct.ProductId : 0, -1 * Quantity);

                        MessageBox.Show("Adjusting inventory: " + adjustedInventory);


                        string adjustedBalance = client.AdjustBalance(SelectedCustomer != null ?
                            SelectedCustomer.CustomerId : 0, -1 * (SelectedProduct != null && SelectedProduct.Price > 0 ? SelectedProduct.Price.Value : 0) * Quantity);

                        MessageBox.Show("Adjusting balance: " + adjustedBalance);

                        transactionScope.Complete(); 

                    }
                    catch (FaultException err)
                    {
                        MessageBox.Show("FaultException: " + err.Message);
                    }
                    catch (ProtocolException perr)
                    {
                        MessageBox.Show("ProtocolException: " + perr.Message);
                    }
                }

                try
                {
                    LoadCustomers();
                    LoadProducts();
                }
                catch (Exception err)
                {
                    Console.WriteLine(err.Message);
                }
            }
        }

As the client code shows, there are multiple WCF calls (three WCF calls) and the second call gave a FaultException when the client did not enter a Customer. The change inflicted was not persisted to the database and we managed to keep a consistent content of our two tables and the transaction rolled back the persistence of data data Entity Framework was about to inflict - accross multiple WCF calls. Transaction support is rather easy to add to your WCF services and at the client side there is little code that must be writted to ensure that multiple WCF calls inflict consistent change in data. Adding WCF transactions are a much more elegant way to add transaction support to your API accross multiple WCF methods than manually trying to undo WCF operations or refactor / rewrite much code to achieve what you always want with your API - to persist data supporting all four ACID principles. Note that you must use SQL Server database (I have used SqlExpress) and give access to the database I have added a SQL script for (Transactionsdemo.sql) to the app pool user so that the database TransactionsDemo can be accessed and updated (grant db_datareader and db_datawriter access in SQL Management Studio). Hope you found this sample interesting.