Sat Thiru
Sat Thiru

Reputation: 990

Is rapid development possible - (portal) Webapplication inside Service Fabric project

I added a WebApplication to build our web portal into the existing ServiceFabric cluster VS sln setup. However, what I am quickly realizing is that this will not work for rapid development/debug cycle. In order to make changes to CSHTML files and then quickly try them out in the browser, I need the capability to hit F5 in VSTS and quickly have the portal.exe run and serve up the pages; so I switch to the "Portal" project as the Startup project (it is bold) and use the IISExpress or the Portal profile (from launchSettings.json).

However, that fails with an exception:

System.Fabric.FabricException: 'An error occurred during this operation.  Please check the trace logs for more details.'

from this line:

ServiceRuntime.RegisterServiceAsync("PortalApp",
     context => new APIGateway(context)).GetAwaiter().GetResult();

This is because I am not running inside the SF cluster. Which makes sense.

So then how do I make this scenario work? Is there or isnt there a way to do my development WITHOUT having to deploy the solution every time to the cluster? That is VERY SLOW (upto 30-45 secs) even in a single node local cluster and a serious productivity hamper.

Is there a "RunLocal" codepath I can add to circumvent and create a WebHost that doesnt depend on SF runtime, but still uses the rest of my project and runs outside the cluster?

Upvotes: 1

Views: 124

Answers (2)

Diego Mendes
Diego Mendes

Reputation: 11341

On service fabric SDK, you have the application debug mode called "Refresh Application", deploying usign this feature will create a symbolic link from SF deployment to your development path, every change you make to your files, will reflect the changes on SF running application

To use this deployment mode, you can change this setting through: Right click on your SF project > Properties > the properties window will show the settings(below) > switch from "remove application" to "Refresh Application" Service Fabric Project Settings

There are some limitation to this mode:

  • It only works on a one node development cluster.
  • It only supports changes to the contents of code, config, or data packages and the project directory contents in the case of ASP.NET Core projects. Application structural changes - like changes to ServiceManifest.xml or ApplicationManifest.xml - aren't supported because the structure of the deployed application may change with unwanted results. You can still make structural changes while in Refresh Application debug mode, but if you do, the application will be redeployed.
  • Stateful services don't maintain their state if you recompile. In order to get your new binaries running, the service has to be removed to stop the process so new binaries can be written to disk, which of course means all your state is wiped out. This is usually fine for rapid development, but to really test your stateful services you'll still need to run them in a 5-node cluster.

Upvotes: 1

Sat Thiru
Sat Thiru

Reputation: 990

OK, I solved this (mostly) for my scenario.

Here's what I did. Basically, instead of using the Service FabricRuntime to create the WebHost in program.cs (which basically only works inside the SF cluster), I check if we are running inside the cluster or not (borrowed from here) and then instantiate my own WebHost instance using the same Startup class so the webHost (Kestrel in my case) is configured with the same settings. From that point on, it's all the original code that takes over.

This works well for the ServiceFabric projects that host a WebHost (http listeners). However, I have one other SF project that doesn't, and is called by others via Service Remoting. I suspect that one can be made to work as easily as the above, unfortunately.

Once you make these changes, you can change the Solution's StartUp projects to include 1 or all of the webhost EXEs. Then hit F5 which will launch the (1..N) EXEs locally outside the cluster and you can debug them there. This way my workflow is to LEAVE the webhost EXEs running, make changes to my CSHTML files, Save, and then merely refresh the webpage. Changes can be instantly seen and tested! Changes to other compiled code is also testable very fast.

(got some of the above ideas from this thread: How to see if running under service fabric)

Here's the code:

// program.cs

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.ServiceFabric.Services.Runtime;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace Web.Portal
{
    internal static class Program
    {
        /// <summary>
        /// This is the entry point of the service host process.
        /// </summary>
        private static void Main(string[] args)
        {
            if (Environment.GetEnvironmentVariable("Fabric_ApplicationName") == null)
            {
                ServiceEventSource.Current.Message("Detected as running locally OUTSIDE the Fabric Cluster");
                BuildWebHost(args).Run();
            }
            else
            {
                ServiceEventSource.Current.Message("Detected as running INSIDE the Fabric Cluster");
                try
                {
                    ServiceRuntime.RegisterServiceAsync("Web.PortalType",
                        context => new Portal(context)).GetAwaiter().GetResult();

                    // The ServiceManifest.XML file defines one or more service type names.
                    // Registering a service maps a service type name to a .NET type.
                    // When Service Fabric creates an instance of this service type,
                    // an instance of the class is created in this host process.

                    ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(Portal).Name);

                    // Prevents this host process from terminating so services keeps running. 
                    Thread.Sleep(Timeout.Infinite);
                }
                catch (Exception e)
                {
                    ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString());
                    throw;
                }
            }
        }

        public static IWebHost BuildWebHost(string[] args)
        {
            // for some reason, the appSetting.json values arent getting loaded by default config builder
            // even though, per MSDN, they should be in ASPNET Core 2.0. I am adding them in manually for now.
            IConfiguration config = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appSettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"appSettings.Development.json", optional: true, reloadOnChange: true)
                .AddEnvironmentVariables()
                .Build();

            return new WebHostBuilder()
                    .UseConfiguration(config)
                    .UseKestrel()
                    .UseContentRoot(Directory.GetCurrentDirectory())
                    .UseStartup<Startup>()
                    .Build();
        }
    }
}

Upvotes: 3

Related Questions