Migrating Service Fabric Sample Voting App to ASP.NET Core
July 28, 2017 8 Comments
Service Fabric is emerging as popular hosting model for microservices, due to its scalability, resilience, and powerful service orchestration capabilities. The fact that it can run anywhere (Azure, other cloud providers, and on-prem) makes for a compelling story, particularly when considering that single cluster can be stretched across multiple locations (see this great blog post by my Mexia colleagues talking about a proof-of-concept hybrid cluster). In my recent interview for SSW TV, I talk about Service Fabric being “PaaS for DevOps”.
Recently Microsoft introduced support for ASP.NET Core stateless services. This is especially relevant when building Service Fabric application in Visual Studio 2017 which has built-in support for .NET Core. However, it can be a bit tricky trying to migrate existing .NET Web API services to ASP.NET Core – which is the subject of this post.
Background
Programming for Service Fabric is not necessarily that difficult, but it can be a learning curve to understand how to implement services that take full advantage of the platform – especially when it comes to leveraging the built-in resilience across stateful replicas, designing a partition scheme, and managing inter-service communication. Fortunately, Microsoft has provided several sample applications to help get started.
Several months ago I ran an Instructor Led Lab at Microsoft Ignite Australia on Service Fabric, using a variation of a simple Voting app demo provided in a lab by Microsoft. My modified version (which included a couple of extra features like a total ballot count and application version display) was built in Visual Studio 2015 with Update 3 using version 2.4.164.9494 of the Service Fabric SDK. The extra feature of adding as ballot count was useful in a demo for the Global Azure Bootcamp where I made some “breaking” code changes that unfairly gave Trump multiple votes in each ballot cast – thus triggering an automated rollback via a custom health check testing that the total ballots equals the total number of votes registered. Here’s a screenshot of the modified version (showing a proportional vote count that I wish had represented reality last November):
The architecture of the application is quite simple: it consists of a simple stateless Web API service which hosts a Single Page Application (SPA) using Angular JS that serves as the UI. The votes are stored in a stateful service:
The demo worked really well, and the code is available on GitHub.
If you export the package and deploy it to a Service Fabric cluster running the current runtime version (5.6.220.9494 at the time of writing this), the application will work as expected. You can also open the solution in Visual Studio 2017 (I used the Community edition, v15.2 with updates and the latest Service Fabric SDK installed) and it will also compile and deploy in debug mode.
However, when you create new Service Fabric projects in the VS 2017, your project templates look different than in VS 2015. Here’s the VS 2015 version:
And here’s the VS 2017 version:
Notice the difference? “Stateless Web API” has now turned into “Stateless ASP.NET Core” – indicating Microsoft’s commitment to ASP.NET Core.
Migrating to ASP.NET Core
If you want to migrate your Web API to ASP.NET Core, you will need to make a few changes to the code. Here are the changes I needed to do to migrate the Voting application (assumes that you are starting with my version of the original solution available on GitHub). I found it easiest to just create the solution from scratch in Visual Studio 2017 (start with a Service Fabric application named “Voting”) and import/copy the code from the original where indicated below.
VotesDataService
- Create this project as a “Stateful Service” service template
- Add the following Nuget packages:
– Microsoft.ServiceFabric.Services.Remoting (v2.6.220)- Copy the IVotingDataService.cs and VotingDataService.cs files from the original solution into the project (just as they are, no changes necessary)
- Build the project
VotingService
- Create this project as a “Stateless ASP.NET Core” service template, with a type of “Web API” and version “ASP.NET Core 1.1” (no authentication):
- Add a project reference to the VotingDataService project.
- Add the following Nuget package:
– Microsoft.ServiceFabric.Services.Remoting (v2.6.220) - Copy the code from the ServiceEventSource.cs file from the original solution and use it to completely overwrite the code in the existing file (just as it is, no changes necessary)
- Copy the code from the VotingService.cs file from the original solution and use it to completely overwrite the code in the existing file (ignore syntax errors as these will be fixed up in subsequent steps)
- Add the following using statements to the VotingService.cs file:
using Microsoft.ServiceFabric.Services.Communication.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using System.IO; - Replace the code in the CreateServiceInstanceListeners() method with the following code:
- Create a new HTML file called index.html and copy all code (as is) from same file in original project
- Modify the code in lines 41-43 as highlighted here:
- Find and replace all instances of “../api/” with “./” (there should be five instances)
- Change the path in $http.get(‘./votes’) (~line 73) to just “./” (i.e. remove “votes’)
- Change the Copy to Output build property on this file to “Copy always”
- Add a new folder to the project named Models
- Right-click the Models folder and select “Add –> New Item…” from the context menu
- Under Visual C# –> ASP.NET Core, choose “Class” and name the file Vote.cs
- Add two new properties to the class so that it looks like this:
- Delete the ValuesController.cs file the Controllers folder
- Right-click the Controllers folder and select “Add –> New Item…” from the context menu
- Under Visual C# –> ASP.NET Core –> Web –> ASP.NET, choose “Web API Controller Class” and name the file VotesController.cs
- Copy the code from the VotesController.cs class in the original project to this new file
- Make the following modifications to the file:
– Remove using statement for System.Web.Http
– Remove using statement for System.Net.Http.Headers
– Add the following using statements to the file:
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using VotingDataService;
using VotingService.Models
– Change class inheritance from ApiController to Controller
– Change the return type of the Get() method to Task<List<Vote>>
– Change the highlighted lines in the Get() method to appear as follows:
– Change the return type of the GetTotalBallots() method to Task<string>
– Change the highlighted lines in the GetTotalBallots() method to appear as follows:– Change the return type of the GetAppVersion() method to Task<string>
– Change the highlighted lines in the GetAppVersion() method to appear as follows:
– Change the return line in the Post() method to:
return new HttpResponseMessage(HttpStatusCode.NoContent);
– Change the return line in the Delete() method to:
return new HttpResponseMessage(HttpStatusCode.NoContent);
– Add the following attribute to the GetFile() method:
[Produces(“text/html”)]
– Change the return type of the GetFile() method to string
– Modify the method body as highlighted below: - Build and deploy! The app should work the same way as the original version.
NOTE: Do not run the app in Internet Explorer 11 as I’ve found it won’t work. Try Google Chrome or another browser instead.
Note that the code in the Controller class is much simpler! And there is no need to use an HtmlMediaFormatter class as in the Web API version.
The final version of this can be found on GitHub as well. Of course, just days before I finally got around to publishing this, Microsoft released its own version of an upgrade for this app! However, they’ve re-implemented it as a proper MVC app instead of an SPA. Still, they have not included updated lab instructions to build it yourself. Hopefully I will get to do this sometime soon and will attach it to the GitHub repo.
Pingback: Microsoft Integration Weekly Update: July 31 | Hooking Stuffs Together
Great article
Reblogged this on BizTalk on Fhir™.
Pingback: Serverless Logging & Alerting with Service Fabric & Azure Event Grid | Mind Over Messaging
Pingback: Serverless Logging & Alerting with Service Fabric & Azure Event Grid - BizTalkGurus
This is great, thanks – however I had an issue with WebListenerCommunicationListener as I am using ASP.NET Core 2.0.
This was resolved by using HttpSysCommunicationListener instead and installing the Microsoft.ServiceFabric.AspNetCore.HttpSys package to use instead of the Microsoft.ServiceFabric.AspNetCore.WebListener package.
https://stackoverflow.com/questions/45701503/how-to-use-weblistenercommunicationlistener-in-service-fabric-asp-net-core-2
Thanks for sharing Dan!
How would you implement partitions on both services (Web + Data)?
I wander how it would look like in the CreateServiceReplicaListeners method.
Microsoft wrote about this in this page: https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-concepts-partitioning
but their example is based on HttpCommunicationListener and not on KestrelCommunicationListener like in the example
That’s a really good question, Guy. I might have a look at upgrading this example to use partitioning when I get some time (which unfortunately won’t be within the next few weeks, given the upcoming MVP Summit, Global Integration and Global Azure bootcamps!)
One thing to be aware of is that partitioning would not be required on the stateless service, as you can simply deploy that on all nodes and requests would load balance automatically. It’s really only applicable to the stateful service. The first challenge would be to come up with a good partitioning scheme, something that you would expect to evenly distribute the service calls.
By the way, if you want to see a presentation that shows this example being upgraded in a self-healing deployment & rollback, feel free to check out this recording: https://www.youtube.com/watch?v=HWHZKHvk_9Q&list=PLW-BLrn2azoRfompiZIXzkO5-Q8s3cDmV&index=4