I have a .NET Generic Host with an IHostedService
that receives events from an OPC-UA interface and that process them.
The problem is that if an unhandled exception occurs during the processing of an event, the whole application crashes
I read the documentation but I didn't find anything about a global exception handler or a similar mechanism that allows to catch unhandled exception and prevent an application crash.
Is there a solution to protect the Generic Host, as well as the IHostedService
against unhandled exceptions ?
EDIT
I know, here the simplest thing is to try/catch the exception and not let it bubble up. But I'd like to know if there is a similar mechanism like in WinForms / WPF, where you can catch such exceptions more globally and prevent a crash.
EDIT
This is a simplified version of the code:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
var environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
return new HostBuilder()
.UseEnvironment(environmentName)
.ConfigureLogging((hostContext, logging) =>
{
...
})
.ConfigureAppConfiguration((hostContext, builder) =>
{
builder
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddEnvironmentVariables();
})
.ConfigureServices((hostContext, services) =>
{
services.AddSingleton<IHostedService, OpcClientHostedService>();
...
});
}
}
public class OpcClientHostedService : IHostedService
{
private readonly OpcConfiguration _opcConfiguration;
private readonly ILogger _logger;
private readonly OpcClient _opcClient;
public OpcClientHostedService(OpcConfiguration opcConfiguration, ILogger<OpcClientHostedService> logger)
{
_opcConfiguration = opcConfiguration;
_logger = logger;
_opcClient = new OpcClient(opcConfiguration.ServerUri);
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Connecting to OPC server with URI '{0}'", _opcConfiguration.ServerUri);
try
{
_opcClient.Connect();
}
catch (Exception ex)
{
_logger.LogCritical(ex, "Cannot connect to OPC server");
throw;
}
_logger.LogInformation("Connection to OPC server is successful");
CreateOpcObservable().Subscribe(async args => await ProcessOpcEvent(args));
return Task.CompletedTask;
}
private async Task ProcessOpcEvent(OpcValueChangedEventArgs args)
{
await MethodThatCanThrowAnException(args); // If the exception is not handled, the whole application crashes!
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Disconnecting to OPC server");
_opcClient.Disconnect();
_logger.LogInformation("Disconnection to OPC server is successful");
return Task.CompletedTask;
}
...
}
In this example, it is easy to add a try/catch block in the ProcessOpcEvent
method, but it would be great to have a mechanism to avoid complete application crash in this kind of situation.
IHostedService
and have your services extend from that class instead of implementing the interface directly. Write your error handling in the abstract class, and provide a virtual / abstract method for your sub classes to implement instead of the normalStartAsync
, etc. – WhipperinCreateOpcObservable()
and then throwing away the subscription/disposable? It seems like you should keep the subscription around so you can dispose of it if/when necessary. – Whipperin