Share via


WCF & App Services in Universal Windows Platform(UWP) using Windows 10

Introduction:

Here my focus is to show different ways (WCF & App Services) to perform same task (provide services\functionality to other client applications). I am not actually comparing these two ways in a sense that both have their own target audience. For example, if you are more concerned about security, also if your clients are based on different versions of Windows OS then your choice should be WCF, but if your client apps are only in Windows 10 and you are not so much concerned about the internal security and you want to keep things simple and efficient then you should go with App Services.

I will create a basic Calculator functionality (Add, Subtract, Multiply & Divide) as a service and client application will use this service to perform the arithmetic operations (in both ways using WCF and AppServices in UWP), so that you can experience and see how things are handled.

WCF Solution:

First, let’s take a look about WCF solution. As you know, a WCF application consists of three components:

  • WCF service,
  • WCF service host, and
  • WCF service client.

Just to mention here, AppServices in Windows 10(UWP) also consist of same three components. You will notice those shortly.

WCF has a default security mechanism which is extremely robust. WCF supports trustworthy messaging, transaction and interoperability. Also if client applications are using different versions of Windows (Vista, XP, Windows 7 etc.) then so far the only choice is WCF because currently AppServices are only for Windows 10(both Service & clients). 

Create a project with the name MyCalculatorWCFService. this will be a WCF service which will perform basic arithmetic operations add, subtract, multiply & divide (complete code is in attachment). 

    [ServiceContract]
    public interface ICalculator
    {
        [OperationContract]
        int Add(int num1, int num2);

        [OperationContract]
        int Subtract(int num1, int num2);

        [OperationContract]
        int Multiply(int num1, int num2);

        [OperationContract]
        int Divide(int num1, int num2);
      }


    public class Calculator : ICalculator
    {

        // This Function Returns addition of two integer numbers
        public int Add(int num1, int num2)
        {
            return num1 + num2;
        }

        // This function returns subtraction of two integer numbers. 
        // If num1 is smaller than number two then this function returns 0
        public int Subtract(int num1, int num2)
        {
            if (num1 > num2)
            {
                return num1 - num2;
            }
            else
            {
                return 0;
            }
        }

        // This function returns multiplication of two integer numbers.
        public int Multiply(int num1, int num2)
        {
            return num1 * num2;
        }

        // This function returns division value of two integer number. 
        // If num2 is 0 then this function returns 1.
      public int Divide(int num1, int num2)
        {
            if (num2 != 0)
            {
                return (num1 / num2);
            }
            else
            {
                return 1;
            }
        }
    }

As of now, service is created. Next step is to host the service. Create a project name MyCalculatorWCFServiceHost.

       

 static void Main(string[] args)
        {
            //Create a URI to serve as the base address
            Uri httpUrl = new Uri("http://localhost:8090/MyCalculatorWCFService/Calculator");

            //Create ServiceHost
            ServiceHost host = new ServiceHost(typeof(MyCalculatorWCFService.Calculator), httpUrl);

            //Add a service endpoint
            host.AddServiceEndpoint(typeof(MyCalculatorWCFService.ICalculator), new WSHttpBinding(), "");

            //Enable metadata exchange
            ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
            smb.HttpGetEnabled = true;
            host.Description.Behaviors.Add(smb);

            //Start the Service
            host.Open();
            Console.WriteLine("MyCalculatorWCFService is host at " + DateTime.Now.ToString());
            Console.WriteLine("Host is running... Press  key to stop");
            Console.ReadLine();
            //host.Close();
            //Console.WriteLine("Service is closed now...");
        }

Service is hosted, now we need to implement the proxy class for the client. Create a project named MyCalculatorWCFServiceProxy. There are different ways of creating the proxy. we will implement ClientBase<T> class

public class CalculatorServiceProxy : ClientBase<ICalculator>,
  ICalculator
    {
        public int Add(int num1, int num2)
        {
            //Call base to do funtion
            return base.Channel.Add(num1, num2);
        }

        public int Subtract(int num1, int num2)
        {
            //Call base to do funtion
            return base.Channel.Subtract(num1, num2);
        }

        public int Multiply(int num1, int num2)
        {
            //Call base to do funtion
            return base.Channel.Multiply(num1, num2);
        }

        public int Divide(int num1, int num2)
        {
            //Call base to do funtion
            return base.Channel.Divide(num1, num2);
        }
    }

And the final step is to create a client application(MyCalculatorWCFServiceClient) to use the service

 public partial class MainWindow : Window
    {
        CalculatorServiceProxy proxy;
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Result_Click(object sender, RoutedEventArgs e)
        {
            int num1 = int.Parse(InputtextBox1.Text);
            int num2 = int.Parse(InputtextBox2.Text);
            int result = 0;

            if (proxy == null)
            {
                proxy = new CalculatorServiceProxy();
            }

            switch(Operation.SelectionBoxItem.ToString())
            {
                case "Add":
                    {
                        result = proxy.Add(num1, num2);
                        break;
                    }
                case "Subtract":
                    {
                        result = proxy.Subtract(num1, num2);
                        break;
                    }
                case "Multiply":
                    {
                        result = proxy.Multiply(num1, num2);
                        break;
                    }
                case "Divide":
                    {
                        result = proxy.Divide(num1, num2);
                        break;
                    }
                default:
                    {
                        MessageBox.Show("Fail: unknown command");
                        break;
                    }
            }
            ResulttextBlock.Text = result.ToString();
        }
    }

AppServices in Universal Windows Platform(UWP):

UWP applications can provide services to another Universal Windows Platform(UWP) applications. In Windows 10, an app service is a way or mechanism for an application to provide services to other applications. An app service works in the form of a background task (You can run code in the background by writing classes that implement the IBackgroundTask interface). Foreground(client) applications can call an app service in another app to perform tasks in the background.

App services are like web services but these are used in Windows 10 device.

First, let’s create a Windows Runtime Component (Universal Windows) Project named "MyCalculatorService". this will be our calculator service application.

Create a class named Calculator which implements IBackgroundTask. in WCF terminology, you can say this class is our ServiceContract which will have OperationContract 

 public sealed class Calculator : IBackgroundTask
    {
        private BackgroundTaskDeferral backgroundTaskDeferral;
        private AppServiceConnection appServiceConnection;
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            this.backgroundTaskDeferral = taskInstance.GetDeferral();

            var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
            appServiceConnection = details.AppServiceConnection;

            appServiceConnection.RequestReceived += OnRequestReceived;
            taskInstance.Canceled += OnTaskCanceled;
        }

        private async void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
        {
            var messageDeferral = args.GetDeferral();
            ValueSet message = args.Request.Message;
            ValueSet returnData = new ValueSet();

            string command = message["Command"] as string;      //Add, Subtract, Multiply, Divide
            int? firstNumber = message["Input1"] as int?;
            int? secondNumber = message["Input2"] as int?;
            int? result = 0;

            if (firstNumber.HasValue && secondNumber.HasValue)
            {
                switch (command)
                {
                    case "Add":
                        {
                            result = firstNumber + secondNumber;
                            returnData.Add("Result", result.ToString());
                            returnData.Add("Status", "Complete");
                            break;
                        }
                    case "Subtract":
                        {
                            result = firstNumber - secondNumber;
                            returnData.Add("Result", result.ToString());
                            returnData.Add("Status", "Complete");
                            break;
                        }
                    case "Multiply":
                        {
                            result = firstNumber * secondNumber;
                            returnData.Add("Result", result.ToString());
                            returnData.Add("Status", "Complete");
                            break;
                        }
                    case "Divide":
                        {
                            result = firstNumber / secondNumber;
                            returnData.Add("Result", result.ToString());
                            returnData.Add("Status", "Complete");
                            break;
                        }
                    default:
                        {
                            returnData.Add("Status", "Fail: unknown command");
                            break;
                        }
                }
            }

            await args.Request.SendResponseAsync(returnData);
            messageDeferral.Complete();
        }

        private void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
        {
            if (this.backgroundTaskDeferral != null)
            {
                this.backgroundTaskDeferral.Complete();
            }
        }
    }

Now the next step is to create a project which will expose this functionality for the client applications (kind of host in WCF environment).

Create a UWP project named MyCalculatorServiceProvider. import service *.winmd file (Windows Meta Data file) from the MyCalculatorService project. now in Package.appxmanifest file, declare App Service instance named CalculatorService1 with the entry point "MyCalculatorService.Calculator" (look at the above code, this is the class which implement the IBackgroundTask)

Now the final step is to create a client application which will consume this service. The service provider app must be deployed before you can call it from a client. You will also need the package family name of the app service app in order to call it. Create a UWP project named CalculatorClient. here you can notice AppServiceName is the name of App Service we defined in MyCalculatorServiceProvider project. In order to get the PackageFamilyName we have two different approaches.

One solution to get the package family name of the App Service application is by calling Windows.ApplicationModel.Package.Current.Id.FamilyName within the **MyCalculatorServiceProvider **project at the Application level.Another approach to get the package family name is by deploying the **MyCalculatorServiceProvider **solution and note the full package name in the output window, then remove the platform information from the full package name to get the desire result.

here is the core functionality.

public sealed partial class MainPage : Page
    {
    private AppServiceConnection calculatorService;

    public MainPage()
        {
            this.InitializeComponent();
        }

    private async void button_Click(object sender, RoutedEventArgs e)
    {
      //Add the connection
        if (calculatorService == null)
        {
          calculatorService = new AppServiceConnection();
            calculatorService.AppServiceName = "CalculatorService1";
            calculatorService.PackageFamilyName = "83da5395-2473-49fb-b361-37072e87e9b9_xe3s0d4n4696a";

            var status = await calculatorService.OpenAsync();

            if (status != AppServiceConnectionStatus.Success)
            {
                Result.Content = "Failed to connect";
                return;
            }
        }

        //Call the service
        int num1 = int.Parse(InputtextBox1.Text);
        int num2 = int.Parse(InputtextBox2.Text);
        var message = new ValueSet();

        message.Add("Command", Operation.SelectionBoxItem);
        message.Add("Input1", num1);
        message.Add("Input2", num2);

        AppServiceResponse response = await calculatorService.SendMessageAsync(message);
        string result = "";

        if (response.Status == AppServiceResponseStatus.Success)
        {
          //Get the data that the service sent
            if (response.Message["Status"] as string == "Complete")
            {
                result = response.Message["Result"] as string;
            }
        }
        message.Clear();
        ResulttextBlock.Text = result;
    }
    }

Conclusion:

Here I tried to give two different ways to perform the same functionality. Based on target OS platform & features including security, performance etc., you can choose which way to go. if time permits I will give more detail focus on UWP in the upcoming article...