I've successfully gotten .net core webapi deployed to service fabric. However, I want to move a majority of the setup to a class library file. Doing so creates a circular reference between my class library and the DAL. So I can't do that. (The reason is because the class library contains constants and an Odata service client interface that is part of the DAL, and the data entities projects are in dal, so registering db context needs a reference to DAL, and therefore cannot be included in the class library)
My thought was to use Autofac to create a container with all of my web host stuff up front then register the SF type.
I have a couple of main issues.
- I can't find a way to specify the port.
- I can't find a way to pass in the ServiceFabricIntegrationOptions.UseReverseProxyIntegration
Here is the important parts of code:
Program.cs
public static IHost BuildWebHost(string[] args) {
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.{environment}.json", true, true)
.AddEnvironmentVariables()
.Build();
return Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureAppConfiguration(builder =>
{
builder.Sources.Clear();
builder.AddConfiguration(configuration);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
//.UseStartup<Startup>()
.Build();
}
Startup.cs
// Add dependency injection items
services.AddHttpContextAccessor();
services.AddVersioning();
services.RegisterApplicationServices(assembly.GetName().Name, assembliesToScan);
services.RegisterClassDependencies();
services.RegisterContexts(_configuration);
var builder = new ContainerBuilder();
builder.Populate(services);
builder.RegisterModule(new SerilogModule());
builder.RegisterServiceFabricSupport();
builder.RegisterStatelessService<ServiceFabricSetup>("Query.Policy.CoreType");
var container = builder.Build();
ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(ServiceFabricSetup).Name);
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
return new AutofacServiceProvider(container);
ServiceFabricSetup.cs
/// <summary>
/// Injected logger
/// </summary>
private readonly ILogger<ServiceFabricSetup> _logger;
/// <summary>
/// Constructor
/// </summary>
/// <param name="context">Context</param>
/// <param name="logger">Injected logger</param>
public ServiceFabricSetup(StatelessServiceContext context, ILogger<ServiceFabricSetup> logger)
: base (context)
{
_logger = logger;
}
/// <summary>
/// This is the main entry point for your service instance.
/// </summary>
/// <param name="cancellationToken">Canceled when Service Fabric needs to shut down this service instance.</param>
protected override async Task RunAsync(CancellationToken cancellationToken)
{
// TODO: Replace the following sample code with your own logic
// or remove this RunAsync override if it's not needed in your service.
_logger.LogDebug($"{nameof(RunAsync)} invoked");
long iterations = 0;
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
ServiceEventSource.Current.ServiceMessage(this.Context, "Working-{0}", ++iterations);
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
if (iterations != 30)
continue;
// Restart the replica after 30 seconds to trigger deactivate.
var fabricClient = new FabricClient();
await fabricClient.ServiceManager.RemoveReplicaAsync(
Context.NodeContext.NodeName, Context.PartitionId, Context.ReplicaOrInstanceId);
}
}
/// <summary>
/// On Close Async
/// </summary>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Task</returns>
protected override Task OnCloseAsync(CancellationToken cancellationToken)
{
_logger.LogDebug($"{nameof(OnCloseAsync)} invoked");
return base.OnCloseAsync(cancellationToken);
}
/// <summary>
/// On Abort
/// </summary>
protected override void OnAbort()
{
_logger.LogDebug($"{nameof(OnAbort)} invoked");
base.OnAbort();
}
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
_logger.LogDebug($"{nameof(Dispose)} invoked");
}
/// <summary>
/// Optional override to create listeners (e.g., TCP, HTTP) for this service replica to handle client or user requests.
/// </summary>
/// <returns>A collection of listeners.</returns>
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new ServiceInstanceListener[0];
}
I've tried several options to figure out how to set the port and Service Fabric Reverse Proxy. This is how I had it before I tried autofac
Previous iteration of ServiceFabricSetup.cs CreateServiceInstanceListeners():
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new ServiceInstanceListener[]
{
new ServiceInstanceListener(serviceContext =>
new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
{
ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}");
// Get the environment out of service fabric configuration and inject it.
var environment = FabricRuntime.GetActivationContext()?
.GetConfigurationPackageObject("Config")?
.Settings.Sections["Environment"]?
.Parameters["ASPNETCORE_ENVIRONMENT"]?.Value;
ServiceEventSource.Current.ServiceMessage(serviceContext, $"Environment set to {environment}");
var webApplicationOptions = new WebApplicationOptions
{
ContentRootPath = Directory.GetCurrentDirectory(),
};
var builder = WebApplication.CreateBuilder(webApplicationOptions);
builder.Host.ConfigureAppConfiguration((ctx, builder) =>
{
builder.AddJsonFile("appsettings.json", false, true);
builder.AddJsonFile($"appsettings.{environment}.json", true, true);
builder.AddEnvironmentVariables();
});
builder.Host.UseSerilog((context, configuration) => configuration.ReadFrom.Configuration(context.Configuration));
//Serilog.Debugging.SelfLog.Enable(msg => File.AppendAllText ("SFLog.txt", msg));
builder.WebHost
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseEnvironment(environment)
.ConfigureAppConfiguration(builder =>
{
builder.AddJsonFile("appsettings.json", false, true);
builder.AddJsonFile($"appsettings.{environment}.json", true, true);
builder.AddEnvironmentVariables();
})
.ConfigureServices(services =>
{
services.AddSingleton<StatelessServiceContext>(serviceContext);
services.RegisterClassDependencies();
services.RegisterContexts(builder.Configuration);
})
.UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.UseReverseProxyIntegration)
.UseUrls(url);
var startup = new IML.Core.WebAPI.Startup(builder.Configuration);
startup.ConfigureServices(builder.Services, _appName);
var app = builder.Build();
startup.Configure(app, builder.Environment);
return app;
}))
};
}
Anyone know how to set up autofac + service fabric with static ports that are defined in the Service Manifest and set UseReverseProxyIntegration? (forgot to mention, need to add environment as well as I did in the older version)
The latest ASP.NET Core 3+ uses a different registration process per the generic host example in Autofac's documentation. This requires IHost support which was introduced several versions ago in Service Fabric and is documented here. Tweaking Autofac's example to include all the Service Fabric bits, we wind up with the following:
Two additional points on the above:
IServiceCollection.serviceContexthere since it's still in scope where it wouldn't be in Startup.cs.There's also an example farther down in the Service Fabric documentation linked above (here for convenience) indicating how to do without the Startup.cs altogether if you should want to go down that route as well.