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();
}
}
}