Electron.NET
Electron.NET è una libreria che integra ASP.NET Core con Electron. Viene usata in un progetto MyApp.Desktop dedicato, separato dal WebAPI.
Il progetto Desktop ha un solo compito: aprire la shell Electron, servire i file statici di Angular, e avviare il WebAPI come child process. Il WebAPI rimane invariato.
Struttura della soluzione
MyApp.sln
├── MyApp.Api/ ← WebAPI puro, invariato
├── MyApp.Frontend/ ← progetto Angular
└── MyApp.Desktop/ ← Electron.NET: shell + static files + avvio API
Installazione (nel progetto Desktop)
cd MyApp.Desktop
dotnet add package ElectronNET.API
dotnet tool install ElectronNET.CLI -g
MyApp.Desktop
Il progetto Desktop è una minimal ASP.NET Core app con Electron.NET. Serve solo i file statici di Angular: nessun controller, nessuna logica di business.
<!-- MyApp.Desktop/MyApp.Desktop.csproj -->
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ElectronNET.API" Version="*" />
</ItemGroup>
</Project>
// MyApp.Desktop/Program.cs
using ElectronNET.API;
using ElectronNET.API.Entities;
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseElectron(args);
var app = builder.Build();
// Serve solo i file statici di Angular: nessuna API qui
app.UseDefaultFiles();
app.UseStaticFiles();
app.MapFallbackToFile("index.html");
if (HybridSupport.IsElectronActive)
{
await app.StartAsync();
StartApi();
await WaitForApi("http://localhost:5000/health");
await CreateWindowAsync();
app.WaitForShutdown();
}
else
{
app.Run();
}
void StartApi()
{
var apiExe = Path.Combine(AppContext.BaseDirectory, "api", "MyApp.Api.exe");
Process.Start(new ProcessStartInfo
{
FileName = apiExe,
UseShellExecute = false,
CreateNoWindow = true,
Environment = { ["ASPNETCORE_URLS"] = "http://localhost:5000" }
});
}
async Task WaitForApi(string healthUrl, int maxRetries = 30)
{
using var http = new HttpClient();
for (var i = 0; i < maxRetries; i++)
{
try
{
var res = await http.GetAsync(healthUrl);
if (res.IsSuccessStatusCode) return;
}
catch { /* non ancora pronto */ }
await Task.Delay(500);
}
throw new Exception("WebAPI non raggiungibile");
}
async Task CreateWindowAsync()
{
var window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions
{
Width = 1280,
Height = 800,
Show = false
});
window.OnReadyToShow += () => window.Show();
window.SetTitle("La mia app");
}
La wwwroot/ del progetto Desktop contiene la build Angular (ng build). Angular chiama il WebAPI su http://localhost:5000 tramite environment.apiBaseUrl.
Accesso alle API Electron da C#
Electron.NET espone le principali API Electron lato C#:
// Dialog di apertura file
var files = await Electron.Dialog.ShowOpenDialogAsync(window, new OpenDialogOptions
{
Properties = new[] { OpenDialogProperty.openFile },
Filters = new[] { new FileFilter { Name = "PDF", Extensions = new[] { "pdf" } } }
});
// Notifica di sistema
Electron.Notification.Show(new NotificationOptions
{
Title = "Operazione completata",
Body = "Il file è stato salvato."
});
// Menu applicazione
var menu = new MenuItem[]
{
new MenuItem
{
Label = "File",
Submenu = new MenuItem[]
{
new MenuItem { Label = "Esci", Role = MenuRole.quit }
}
}
};
Electron.Menu.SetApplicationMenu(menu);
Avvio in sviluppo
cd MyApp.Desktop
electronize start
In sviluppo è possibile configurare Angular per girare sul proprio dev server (ng serve) e il Desktop per fare proxy verso di esso, mantenendo l'hot reload:
// MyApp.Desktop/Properties/launchSettings.json
{
"profiles": {
"MyApp.Desktop": {
"commandName": "Project",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Build per distribuzione
# Pubblica il WebAPI
dotnet publish MyApp.Api -c Release -r win-x64 --self-contained -o MyApp.Desktop/bin/api/
# Build Angular → wwwroot del progetto Desktop
ng build --configuration production --output-path MyApp.Desktop/wwwroot/
# Build Electron
cd MyApp.Desktop
electronize build /target win
L'output in bin/Desktop/ include runtime .NET, Node.js, Chromium e il WebAPI publicato.
Limitazioni
- Bundle pesante (~150–200 MB) per via di Chromium e Node.js
- Electron.NET segue le release di Electron con un certo ritardo
- Le API Electron più avanzate non sono sempre esposte lato C#: in quei casi occorre usare l'IPC con JavaScript