Azure for Developers Tutorial Step 2: Creating the WCF service, part 1

This is the second step of the Azure for Developers tutorial, in which we set up a WCF service running in Azure to provide CRUD operations to a client application. For more information, please check out the Introduction.

In this step, we will create the basic structure of the WCF service, including the service contracts. In the next step, we will add the code to actually access the database.

Set up the project and configuration settings

Open Visual Studio in administrative mode (Right-click on Visual Studio, select Run as Administrator). Click File/New Project.

Select Cloud project, .NET 4 Framework, and specify the name as AzureCustomerServices. Check the box that says “Create directory for solution”. Browse to the location you want the code, and click OK.

Now you should see the New Windows Azure Cloud Service dialog.

Pick your version of the tools in the top left-hand corner. Then select WCF Service Web Role from the list of available roles. Click the > button to add the role to the solution. Then hover over the right side of that entry and a pencil will appear. Click the pencil to change the name, and set it to CustomerServicesWebRole. Then click OK to create the project. Visual Studio will create the web role project for you, and the cloud project with its configuration files.

The difference between a web role and a worker role is that a web role uses IIS. So if you have a web application or service, you should use a web role. Worker roles are generally used for processing that you want to be done asynchronously. Our web role will host our service, and later in the tutorial our worker role will process messages from the queue.

ServiceConfiguration.Cloud.cscfg is the configuration that will be used when you publish to the cloud. Service Configuration.Local.cscfg is the configuration that will be used when you run the application in the compute emulator. ServiceDefinition.csdef has the definition and the list of configuration items, which you will see in a minute.

Double-click on each of the cscfg files and go out to the end of the Service Configuration element. You will see this:

osFamily="1" osVersion="*">

By default, the osFamily will be set to 1. This means your Azure instance will be installed on Windows Server 2008. Generally, you will want to change this to 2, so your Azure instances will be run on Windows Server 2008 R2 VM’s.

If you double-click on the CustomerServicesWebRole under Roles in the cloud project, you will see where to set the instance count and VM Size, as well as the endpoints. You can also set up LocalStorage, which will allocate a disk you can access locally in the Azure instance. At GoldMail, we use this in one of our roles as a scratch disk.

There are two ServiceConfiguration.*.cscfg files – these contain configuration information that you can access programmatically as well as through the Windows Azure portal. You can change these values while your role is running without re-publishing your role. If you are migrating a current website to Azure, be sure to check the settings in your web.config file and see if any should be moved to the role configuration so you can change it without re-publishing.

Let’s add the configuration settings that we will need in the web role. Double-click on the ServiceConfiguration.Local.cscfg file. Remove any configuration settings that you have, and add these:

      <Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" 
               value="UseDevelopmentStorage=true" />
      <Setting name="DataConnectionString" value="UseDevelopmentStorage=true" />
      <Setting name="dbConnString" value="Data Source=YOURLOCALSQLSERVER;Initial Catalog=CodeCamp;
               User ID=YOURUSERNAME;Password=YOURPASSWORD;" />
      <!-- This is the max number of times to try executing the database commands before giving up. -->
      <Setting name="MaxTryCount" value="4" />
      <!-- This is the time interval to sleep between SQL retries in case there's a problem. It's in ms. -->
      <Setting name="RetrySleepInterval" value="2500" />
      <!-- frequency, in seconds, to retrieve the perf counters -->
      <Setting name="PerfMonSampleRate" value="60" />
      <!-- frequency, in seconds, to transfer the perf counters to the logs from the system-->
      <Setting name="PerfMonScheduledTransferPeriod" value="120" />
      <Setting name="ProcessQueueName" value="codecampqueue" />

For dbConnString, change this to your connection string to your local SQL Server database.

Now double-click on ServiceConfiguration.Cloud.cscfg. If you have a storage account and SQL Azure database, you can change the connection strings accordingly. Otherwise, you can use the same values you used in the Local configuration. You need to have the same XML elements in each configuration. Just be sure you don’t publish to the cloud with the local development connection strings – that is one of the most frequent causes of roles not spinning up during deployment.

For publishing to the cloud, your ServiceConfiguration.cloud.cscfg configuration settings element should look like this, but with the variables filled in:

      <Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" 
               value="DefaultEndpointsProtocol=https;AccountName=PUTYOURACCOUNTNAMEHERE;
               AccountKey=PUTYOURACCOUNTKEYHERE" />
      <Setting name="DataConnectionString" value="DefaultEndpointsProtocol=https;
               AccountName=PUTYOURACCOUNTNAMEHERE;AccountKey=PUTYOURACCOUNTKEYHERE" />
      <Setting name="dbConnString" value="Server=tcp:YOURAZUREDATABASE.database.windows.net;
               Database=CodeCamp;User ID=YOURDBACCOUNT@YOURDATABASE;Password=YOURPASSWORD;
               Trusted_Connection=False;Encrypt=True;" />
      <!-- This is the max number of times to try executing the database commands before giving up. -->
      <Setting name="MaxTryCount" value="4" />
      <!-- This is the time interval to sleep between SQL retries in case there's a problem. It's in ms. -->
      <Setting name="RetrySleepInterval" value="2500" />
      <!-- frequency, in seconds, to retrieve the perf counters -->
      <Setting name="PerfMonSampleRate" value="60" />
      <!-- frequency, in seconds, to transfer the perf counters to the logs from the system-->
      <Setting name="PerfMonScheduledTransferPeriod" value="120" />
      <Setting name="ProcessQueueName" value="codecampqueue" />

Now open the csdef file; you need to add the definitions of the elements here as well. You probably don’t even have a Configuration section (I don’t). Just add it after Endpoints.

    <ConfigurationSettings>
      <Setting name="dbConnString" />
      <Setting name="DataConnectionString" />
      <Setting name="MaxTryCount" />
      <Setting name="RetrySleepInterval" />
      <Setting name="PerfMonSampleRate" />
      <Setting name="PerfMonScheduledTransferPeriod" />
      <Setting name="ProcessQueueName" />
    </ConfigurationSettings>

I’ll explain the settings as we use them. Now let’s add a class to our web role project (CustomerServicesWebRole) and call it GlobalStaticProperties. Rather than retrieve the configuration values from the Role repeatedly, I retrieve them from this class, which only reads them the first time they are retrieved. So right-click on CustomerServicesWebRole and choose Add Class. Name it GlobalStaticProperties and click OK. Change the class from Public to Internal Static.

You will need these using clauses at the top of the class:

using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;

Here is the actual class code itself. I’ve put comments inline to explain what each variable is.

internal static class GlobalStaticProperties
{

  private static string _dbConnectionString;
  /// <summary>
  /// connection string to the database; only retrieve it the first time
  /// </summary>
  internal static string dbConnectionString
  {
    get
    {
      if (string.IsNullOrEmpty(_dbConnectionString))
      {
        _dbConnectionString = RoleEnvironment.GetConfigurationSettingValue("dbConnString");
        Trace.TraceInformation("[CustomerServicesWebRole.GlobalStaticProperties] " +
            " Setting dbConnectionString to {0}", _dbConnectionString);
      }
      return _dbConnectionString;
    }
  }

  private static int _MaxTryCount;
  /// <summary>
  /// max number of times to try reading the SQL database before giving up
  /// </summary>
  internal static int MaxTryCount
  {
    get
    {
      if (_MaxTryCount <= 0)
      {
        //hasn't been loaded yet, so load it 
        string maxTryCount = RoleEnvironment.GetConfigurationSettingValue("MaxTryCount");
        int intTest = 0;
        bool success = int.TryParse(maxTryCount, out intTest);
        //if it's <= 0, set it to 1.
        if (!success || intTest <= 0)
          _MaxTryCount = 1;
        else
          _MaxTryCount = intTest;
        Trace.TraceInformation("[CustomerServicesWebRole.GlobalStaticProperties] "
          + "Setting MaxTryCount to {0}", MaxTryCount);
      }
      return _MaxTryCount;
    }
  }

  private static List<int> _retrySleepTime;
  /// <summary>
  /// amount of time to wait between retries when reading the SQL database
  /// This loads a list, which is then referenced in code. 
  /// This means my intervals are the same, just multiplied by the index.
  /// First retry waits 0 seconds, second waits 2.5, third waits 5, and last is irrelevant.
  /// (It stops if it retries 4 times.)
  /// </summary>
  internal static List<int> retrySleepTime
  {
    get
    {
      if (_retrySleepTime == null || _retrySleepTime.Count <= 0)
      {
        //hasn't been loaded yet, so load it 
        string interval = RoleEnvironment.GetConfigurationSettingValue("RetrySleepInterval");
        int intTest = 0;
        int intInterval = 0;
        bool success = int.TryParse(interval, out intTest);
        if (intTest <= 0)
          intInterval = 2500; //2.5 seconds
        else
          intInterval = intTest;
        Trace.TraceInformation("[CustomerServicesWebRole.GlobalStaticProperties] " 
          + "Setting Sleep Interval to {0}", intInterval);

        //put these in an array so they are completely dynamic rather than having
        //  variables for each one. You can change the interval and number of times
        //  to retry simply by changing the configuration settings.
        _retrySleepTime = new List<int>();
        //set the sleep times 0, 5, 10, etc.
        intTest = 0;
        _retrySleepTime.Add(0);
        for (int i = 1; i < MaxTryCount; i++)
        {
          intTest += intInterval;
          _retrySleepTime.Add(intTest);
        }

        for (int i = 0; i < MaxTryCount; i++)
        {
          Trace.TraceInformation("[CustomerServicesWebRole.GlobalStaticProperties] "
            + "Setting retrySleepTime({0}) to {1}", i, _retrySleepTime[i]);
        }
      }
      return _retrySleepTime;
    }
  }

  private static string _ProcessQueueName;
  /// <summary>
  /// name of the queue. You should never hard code this. If you do, you will have to 
  /// re-publish your application in order to change it.
  /// </summary>
  internal static string ProcessQueueName
  {
    get
    {
      if (string.IsNullOrEmpty(_ProcessQueueName))
      {
        _ProcessQueueName = RoleEnvironment.GetConfigurationSettingValue("ProcessQueueName");
        Trace.TraceInformation("[CustomerServicesWebRole.GlobalStaticProperties] "
          + "Setting ProcessQueueName to {0}", _ProcessQueueName);
      }
      return _ProcessQueueName;
    }
  }
}

Configure the diagnostics

Now that we have the configuration set up and handled, we want to add code to set up the diagnostics configuration. At the very least, you should set up and use trace diagnostics. You can not debug into your instance running in the cloud, and if you trace log exceptions (at the very least), it will help you figure out what’s happening.

We want the diagnostics code to execute when the web role starts up, so open WebRole.cs. Add this using statement at the top:

using Microsoft.WindowsAzure.Diagnostics.Management;

Remove the code in the OnStart() method and replace it with the following. I’ve included comments in-line to explain the code.  This basically sets the configuration for the diagnostics, for trace logging, IIS logs, Windows Event logs, and one performance statistic (%CPU). The log information is written locally and then transferred to Windows Azure storage. IIS logs are transferred to blob storage; the other logs are transferred to table storage.

public override bool OnStart()
{
  // Get a reference to the initial default configuration.
  string wadConnectionString = "Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString";

  // First, get a reference to the storage account where the diagnostics will be written. 
  // It is recommended that you use a separate account for diagnostics and data, so the 
  //   performance of your data access is not impacted by the diagnostics.
  CloudStorageAccount storageAccount =
      CloudStorageAccount.Parse(
      RoleEnvironment.GetConfigurationSettingValue(wadConnectionString));

  // Get an reference to the diagnostic manager for the role instance, 
  //   and then get the default initial configuration, which we will then change.
  RoleInstanceDiagnosticManager roleInstanceDiagnosticManager =
      storageAccount.CreateRoleInstanceDiagnosticManager(RoleEnvironment.DeploymentId, 
      RoleEnvironment.CurrentRoleInstance.Role.Name, RoleEnvironment.CurrentRoleInstance.Id);
  DiagnosticMonitorConfiguration config = DiagnosticMonitor.GetDefaultInitialConfiguration();

  // Change the polling interval for checking for configuration changes
  //   and the buffer quota for the logs. 
  config.ConfigurationChangePollInterval = TimeSpan.FromSeconds(30.0);
  config.DiagnosticInfrastructureLogs.BufferQuotaInMB = 256;

  // The diagnostics data is written locally and then transferred to Azure Storage. 
  // These are the transfer intervals for doing that operation.
  config.Logs.ScheduledTransferPeriod = TimeSpan.FromMinutes(1.0); //for trace logs
  config.Directories.ScheduledTransferPeriod = TimeSpan.FromMinutes(1.0); //for iis logs

  // Configure the monitoring of one Windows performance counter
  // and add it to the configuration.
  int sampleRate = 0;
  int scheduledTransferPeriod = 0;
  bool success = false;
  //this is sample rate, in seconds, for the performance monitoring in %CPU.
  //By making this configurable, you can change the azure config rather than republish the role.
  success = int.TryParse(RoleEnvironment.GetConfigurationSettingValue("PerfMonSampleRate"), 
    out sampleRate);
  if (!success || sampleRate <= 0)
    sampleRate = 60;  //default is 60 seconds
  success =
    int.TryParse(RoleEnvironment.GetConfigurationSettingValue("PerfMonScheduledTransferPeriod"), 
    out scheduledTransferPeriod);
  if (!success || scheduledTransferPeriod <= 0)
    scheduledTransferPeriod = 120;  //default is 120 seconds

  PerformanceCounterConfiguration perfConfig
      = new PerformanceCounterConfiguration();
  perfConfig.CounterSpecifier = @"\Processor(*)\% Processor Time";
  perfConfig.SampleRate = TimeSpan.FromSeconds((double)sampleRate);
  config.PerformanceCounters.DataSources.Add(perfConfig);
  config.PerformanceCounters.ScheduledTransferPeriod = 
    TimeSpan.FromSeconds((double)scheduledTransferPeriod);

  // Configure monitoring of Windows Application and System Event logs,
  // including the quota and scheduled transfer interval, and add them 
  // to the configuration.
  WindowsEventLogsBufferConfiguration eventsConfig
      = new WindowsEventLogsBufferConfiguration();
  eventsConfig.BufferQuotaInMB = 256;
  eventsConfig.ScheduledTransferLogLevelFilter = LogLevel.Undefined; //was warning
  eventsConfig.ScheduledTransferPeriod = TimeSpan.FromMinutes(2.0); //was 10
  eventsConfig.DataSources.Add("Application!*");
  eventsConfig.DataSources.Add("System!*");
  config.WindowsEventLog = eventsConfig;

  //set the configuration to be used by the current role instance
  roleInstanceDiagnosticManager.SetCurrentConfiguration(config);

  //add an event handler for the configuration being changed while the role is running
  RoleEnvironment.Changing += 
    new EventHandler<RoleEnvironmentChangingEventArgs>(RoleEnvironment_Changing);
  return base.OnStart();
}

Add an event handler for the Role Environment changing:

/// <summary>
/// If they change any of the configuration values while the role is running,
/// recycle it. You can also have this check which setting got changed and 
/// handle it rather than recycling the role. 
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void RoleEnvironment_Changing(object sender, RoleEnvironmentChangingEventArgs e)
{

  // If a configuration setting is changing
  if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange))
  {
    // Set e.Cancel to true to restart this role instance
    e.Cancel = true;
  }
}

That takes care of the diagnostics. They will be collected and transferred to Windows Azure storage periodically while the role is running.

Set up the service contract

In the Solution Explorer, rename IService1 to ICustomerServices, then double-click on it to open it. Remove the sample code that Microsoft provides. Add this to the using statements:

using System.Data;

And here is your service contract:

[ServiceContract]
public interface ICustomerServices
{
  [OperationContract]
  string GetFavorites(out string favoriteMovie, out string favoriteLanguage,
      string firstName, string lastName);

  [OperationContract]
  string UpdateFavoritesByName(string firstName, string lastName, string favoriteMovie, 
    string favoriteLanguage);

  [OperationContract]
  string GetCustomerList(out DataSet customers);

  [OperationContract]
  string AddACustomer(string firstName, string lastName, string favoriteMovie, 
    string favoriteLanguage);
}

As we discussed back in the introduction article, we are exposing four methods for retrieving, adding, and updating the data in the database. Now we need to implement our interface.

Rename Service1.svc to CustomerServices.svc, then double-click on it to open the C# code. Remove the sample code provided by Microsoft. Right-click on Service1 and select Refactor/Rename, and rename it to CustomerServices. (Am I the only one who finds it annoying that they don’t change this when you change the file name, but they do when you change the interface’s file name?)

So now we need some code for the service itself. First, add this using statement to the top:

using System.Data;

Add this attribute to the class itself. This allows the WCF service to be called from outside the Azure load balancer.

  [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
  public class CustomerServices : ICustomerServices

And here is the code for CustomerServices.svc.cs:

public string GetFavorites(out string favoriteMovie, out string favoriteLanguage,
  string firstName, string lastName)
{
  string errorMessage = string.Empty;
  favoriteMovie = string.Empty;
  favoriteLanguage = string.Empty;
  //CustomerFavorites cf = new CustomerFavorites();
  //errorMessage = cf.GetCustomerFavorites(out favoriteMovie, out favoriteLanguage, firstName, lastName);
  return errorMessage;
}

public string UpdateFavoritesByName(string firstName, string lastName, string favoriteMovie, 
  string favoriteLanguage)
{
  string errorMessage = string.Empty;
  //CustomerFavoritesUpdate cfu = new CustomerFavoritesUpdate();
  //errorMessage = cfu.SetCustomerFavorites(firstName, lastName, favoriteMovie, favoriteLanguage);
  return errorMessage;
}

public string AddACustomer(string firstName, string lastName,
  string favoriteMovie, string favoriteLanguage)
{
  string errorMessage = string.Empty;
  //CustomerFavoritesAdd cfa = new CustomerFavoritesAdd();
  //errorMessage = cfa.AddCustomer(firstName, lastName, favoriteMovie, favoriteLanguage);
  return errorMessage;
}


public string GetCustomerList(out DataSet customers)
{
  string errorMessage = string.Empty;
  customers = new DataSet();
  //CustomerList cl = new CustomerList();
  //errorMessage = cl.GetListOfCustomers(out customers);
  return errorMessage;
}

This is basically a proxy layer. Next, we need to set up the classes for modifying or retrieving the data. I’ll show you how to do that in the next step, and also discuss the SQL Azure connection management.

Tags:

5 Responses to “Azure for Developers Tutorial Step 2: Creating the WCF service, part 1”

  1. chefdehome1 Says:

    hi, I am kind a new to Azure Dev and WCf services and came across your step by step Azure Tutorial. I am working on a MVC 3 project in which we are in process of adding WCF service. I am using Castle WIndsor DI container to inject my BLs to WCF service and also to inject WCF to Controller. Our goal is to later migrate this project to Azure. Do you think we can keep on using Castle Windsor for DI as we are using now while we migrate to Azure? I needed some expert advice to understand if I am on right trak. Thanks.

    • robindotnet Says:

      I don’t know anything about Castle Windsor DI and what you have to deploy to make that work. If you simply add assemblies to your project to be able to use that, then yes, you can probably deploy it to Azure. If you have to *install* something on the machine, then you’d have to figure out how to do it with a startup task.

  2. anil Says:

    Give me COmplete source code on this Example in ZIP file
    plz help me,

    What is Onstart() method ?
    What is the purpase of this one ?
    Can you explain this concept?
    plz…….

    • robindotnet Says:

      The complete source code is available on the introduction to the tutorial. The OnStart() method is in the WebRole.cs; code in that method is executed when the web role starts up.

  3. Azure for Developers: Introduction to the Tutorial | RobinDotNet's Blog Says:

    […] Step 2: Creating the WCF service, part 1 […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: