Wednesday, 18 August 2010

Update: Writing a windows phone 7 application with an Azure backend: part 3 (WCF services)

Update: apparently my first version of this post worked in the development fabric but did not work on Azure due to the load balancer. I updated this post so that it works both on the development fabric and on Azure.

The third post in this series will explain how to create a WCF service running on Azure that can be consumed by a WP7 client. It will focus on creating the WCF service and consuming the repository we created in the previous post. In the next post I will then have a closer look at how we can secure this service while making sure we can still use it from our WP7 application.

Creating the service

It all starts with adding a “Windows Cloud Service” to your solution.

  • Click right on the solution;
  • Choose “Add new item”;
  • Choose the “Windows Cloud Service” type

image

Click “OK”. This will give you the screen underneath.

image

  • Choose “WCF Service Web Role” in the left pane and move it to the right pane.
  • You can change the proposed name by clicking on the pencil. I find that a bit confusing but there is no other way.
  • Choose a name for your services project and click “ok”.

You can find more details on the things we just did here. You’ll notice that Visual Studio has created two new projects in your solution. The WCF Service Web Role project already contains a WCF service. Rename that service to for example “WineService”.

Now we need to make sure our service works well with the load balancer on Azure. My colleague Yves Goeleven explained me how to do that. The solution is based on one of the known issues that are posted on MSDN.

First you need to download the WCF REST starter kit from codeplex. Then you need to write a custom ServiceHostFactory.

public class VinoHostFactory : WebServiceHost2Factory
{             
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {            
        var host = new VinoHost(serviceType,true, baseAddresses);            
        host.Interceptors.Add(new BaseAddressFixer());            
        return host;
    }
}

Two things to notice in the above code snippet. The custom ServiceHostFactory first creates a new ServiceHost and then adds an interceptor to that host. The detailed explanation of what this does can again be found in the MSDN article. My host looks like this:

public class VinoHost :WebServiceHost2
{
    public VinoHost(object singletonInstance, params Uri[] baseAddresses) : base(singletonInstance, baseAddresses)
    {
    }
 
    public VinoHost(Type serviceType, bool dummy, params Uri[] baseAddresses) : base(serviceType, dummy, baseAddresses)
    {
    }
 
    protected override void OnOpening()
    {            
        base.OnOpening();         
        #if DEBUG
            Description.Endpoints.Find(typeof(IWineService)).Address = new EndpointAddress("http://127.0.0.1:84/WineService.svc");
        #endif
        #if (!DEBUG)
            Description.Endpoints.Find(typeof(IWineService)).Address = new EndpointAddress("http://vino.cloudapp.net/WineService.svc");
        #endif
        Description.Endpoints.Find(typeof(IWineService)).ListenUri = this.BaseAddresses[0];
    }
}
}

and the code of the interceptor, the BaseAddressFixer is entirely copied from MSDN.

Then you should change the markup of your service so that it looks like this:

<%@ ServiceHost Service="Vino.Services.WineService" 
Factory="Vino.Services.VinoHostFactory" %>

Now that we have all of this in place, we can write the following code in the service:

[CallContextBehavior(typeof(VinoCallContext))]
public class WineService : IWineService
{
    private readonly IWineRepository _wineRepository;
 
    public WineService()
    {
        _wineRepository = new WineRepository();
    }
 
    public IEnumerable<WineData> GetAllWinesByTenant(string tenantId)
    {
        Mapper.CreateMap<Wine,WineData>();
        return Mapper.Map<IEnumerable<Wine>, IEnumerable<WineData>>(_wineRepository.GetAllWinesByTenant(tenantId));            
    }
 
    public string HelloWorld()
    {
        return "Nothing to creative. Let's just return Hello World";
    }
}

I use Automapper to map my entities to datacontracts. Automapper relies on conventions to automatically do a mapping between your entities and the datacontracts. If you respect those conventions mapping is as easy as defining the mapping source and target (Mapper.CreateMap<Wine,WineData>();) and actually mapping (Mapper.Map<Wine,WineData>(wine);) the source to the target. By using Automapper you don’t have to write that tedious mapping code anymore.

You also notice the “CallContextBehavior” attribute on top of the class. You can read about the implementation in a post from my colleague Gino Heyman. The only thing I still need to do in my application is implement my own call context to open a NHibernate session per WCF call:

public class VinoCallContext :CallContext
{
    public static readonly ISessionFactory sessionFactory = SessionFactoryBuilder.BuildSessionFactory();
 
    protected override void Initialize()
    {
        SessionScope.CurrentSession = sessionFactory.OpenSession();
    }
 
    protected override void Dispose()
    {
        SessionScope.CurrentSession.Dispose();
    }
}

Finally we need to configure the service. It is pretty standard. For now I will just use the configuration as generated by Visual Studio.

   1: <system.serviceModel>    
   2: serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
   3: diagnostics>
   4:  <messageLogging logMalformedMessages="true" logMessagesAtTransportLevel="true"/>
   5: /diagnostics>   
   6: client/>    
   7: behaviors>
   8:  <serviceBehaviors>
   9:    <behavior name="debugBehavior">
  10:      <serviceMetadata httpGetEnabled="true" />
  11:      <serviceDebug includeExceptionDetailInFaults="true" />
  12:    </behavior>       
  13:  </serviceBehaviors>
  14: /behaviors>
  15: services>
  16:  <service behaviorConfiguration="debugBehavior" name="Vino.Services.WineService">
  17:    <clear />
  18:    <endpoint binding="basicHttpBinding"
  19:      contract="Vino.Services.IWineService" />
  20:    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
  21:  </service>      
  22: /services>    
  23: ystem.serviceModel>

Notice that I am using a “basicHttpBinding” here and not a WSHttpBinding. That is because Silverlight does not have support for the WSHttpBinding and we want to be able to call this service from a WP7 application of course.  We have a working version of our service now that we can call from within a WP7 application but before putting this service in production we definitely need to think about security. At this moment our service is not secured and if we would deploy it on Azure everyone would be able to call it. Someone could easily mess with my wine cellar and that’s something I want to avoid. It turns out that the combination WCF, Windows Phone 7 and security can be quite challenging. That’s why I will cover it in a next post.

Let’s deploy the service to Azure now.

Right click on your web role for your WCF service and choose publish.

image The “Enable Intellitrace for .NET 4 roles” checkbox will only be enabled if you installed this patch.  Also notice that I deployed to production. That’s because in my service host I set the enpoint to the production URL. That’s somewhat easier for the demo because the staging URL changes with each deploy.

While waiting for my WCF service to get deployed on Azure, I created a small console application to check whether my service was working.  Just add a console application to your solution, add a service reference to the service (when it is running in the development fabric for example) and call one of the methods on your service. I added a stupid HelloWorld method to my service.

image

1 comment:

Microsoft Office said...

Microsoft Outlook 2010 to help control email volume, you can find the desired content, and perform operations at the appropriate time and location. Outlook 2010 download needs no introduction since it is the industry standard.