com um clique
shiny-aspire-orleans
// Generate code using Shiny Aspire integrations — Orleans ADO.NET hosting and Gluetun VPN container routing
// Generate code using Shiny Aspire integrations — Orleans ADO.NET hosting and Gluetun VPN container routing
Generate code using Shiny.Speech for cross-platform speech-to-text, text-to-speech, audio capture, and audio playback with pluggable cloud providers
Generate code for Shiny.AiConversation - a centralized AI service library for .NET MAUI apps with chat client abstraction, wake word detection, speech-to-text/text-to-speech, acknowledgement modes (None/AudioBlip/LessWordy/Full), persistent message store, optional AI chat history lookup tool, and configurable sound effects
Generate code using Shiny.BluetoothLE.Hosting, a BLE peripheral hosting library for .NET with GATT server, advertising, and managed characteristic patterns
Shiny BluetoothLE client/central operations for scanning, connecting, and communicating with BLE peripherals
Core infrastructure, hosting, DI, key-value stores, lifecycle hooks, and platform abstractions for Shiny on .NET MAUI, iOS, and Android
Guide for implementing Firebase Cloud Messaging push notifications in .NET MAUI apps using Shiny.Push.FirebaseMessaging on iOS and Android.
| name | shiny-aspire-orleans |
| description | Generate code using Shiny Aspire integrations — Orleans ADO.NET hosting and Gluetun VPN container routing |
| triggers | ["aspire orleans","orleans aspire","WithDatabaseSetup","UseAdoNet","UseAdoNetClient","orleans database setup","orleans schema","orleans clustering","orleans grain storage","orleans reminders","OrleansFeature","Shiny.Aspire.Orleans","aspire orleans hosting","aspire orleans server","aspire orleans client","orleans adonet","orleans ado.net","gluetun","gluetun vpn","AddGluetun","WithRoutedContainer","WithVpnProvider","WithWireGuard","WithOpenVpn","vpn container","aspire vpn","Shiny.Aspire.Hosting.Gluetun","GluetunResource","network_mode","vpn routing"] |
You are an expert in Shiny's .NET Aspire integrations:
Invoke this skill when the user wants to:
WithDatabaseSetup to auto-provision Orleans tablessilo.UseAdoNet() to configure a silo inside UseOrleansclient.UseAdoNetClient() to configure a client inside UseOrleansClientAddGluetun, WithRoutedContainer, WithVpnProvider, WithWireGuard, or WithOpenVpnnet10.0| Package | NuGet | Usage |
|---|---|---|
Shiny.Aspire.Orleans.Hosting | Install in Aspire AppHost | Auto-runs Orleans schema scripts when the database becomes ready |
Shiny.Aspire.Orleans.Server | Install in Orleans silo | Registers ADO.NET provider builders for clustering, grain storage, and reminders |
Shiny.Aspire.Orleans.Client | Install in Orleans client | Registers ADO.NET provider builder for clustering |
Shiny.Aspire.Hosting.Gluetun | Install in Aspire AppHost | Adds a Gluetun VPN container and routes other containers through it |
| Database | ADO.NET Invariant | ProviderType Value |
|---|---|---|
| PostgreSQL | Npgsql | PostgresDatabase |
| SQL Server | Microsoft.Data.SqlClient | SqlServerDatabase |
| MySQL | MySql.Data.MySqlClient | MySqlDatabase |
The Server and Client packages register Orleans provider builders via [assembly: RegisterProvider] attributes. When Orleans calls ApplyConfiguration during UseOrleans(), it reads the Aspire-injected configuration (e.g. Orleans:Clustering:ProviderType = "PostgresDatabase") and resolves the matching provider builder automatically. The provider builder maps the database type to the correct ADO.NET invariant and resolves the connection string from the ServiceKey.
This means:
UseAdoNet() on ISiloBuilder and UseAdoNetClient() on IClientBuilder are identity methods for discoverability. The actual wiring happens through the registered provider builders.UseOrleans(silo => { ... }) or UseOrleansClient(client => { ... }), users can add other features alongside.Install Shiny.Aspire.Orleans.Hosting in the AppHost project.
using Shiny.Aspire.Orleans.Hosting;
var builder = DistributedApplication.CreateBuilder(args);
var db = builder.AddPostgres("pg")
.WithPgAdmin()
.AddDatabase("orleans-db");
var orleans = builder.AddOrleans("cluster")
.WithClustering(db)
.WithGrainStorage("Default", db)
.WithReminders(db)
.WithDatabaseSetup(db); // creates all Orleans tables automatically
builder.AddProject<Projects.MySilo>("silo")
.WithReference(orleans)
.WaitFor(db);
builder.AddProject<Projects.MyApi>("api")
.WithReference(orleans.AsClient())
.WaitFor(db);
builder.Build().Run();
Install Shiny.Aspire.Orleans.Server in the silo project. Call silo.UseAdoNet() inside UseOrleans.
using Shiny.Aspire.Orleans.Server;
var builder = WebApplication.CreateBuilder(args);
builder.UseOrleans(silo =>
{
silo.UseAdoNet();
});
var app = builder.Build();
app.Run();
Install Shiny.Aspire.Orleans.Client in the client project (e.g. an API gateway). Call client.UseAdoNetClient() inside UseOrleansClient.
using Shiny.Aspire.Orleans.Client;
var builder = WebApplication.CreateBuilder(args);
builder.UseOrleansClient(client =>
{
client.UseAdoNetClient();
});
var app = builder.Build();
app.MapGet("/counter/{name}", async (string name, IClusterClient client) =>
{
var grain = client.GetGrain<ICounterGrain>(name);
var count = await grain.GetCount();
return Results.Ok(new { name, count });
});
app.Run();
Shiny.Aspire.Orleans.Hostingpublic static OrleansService WithDatabaseSetup(
this OrleansService orleans,
IResourceBuilder<IResourceWithConnectionString> database,
OrleansFeature features = OrleansFeature.All
)
Subscribes to Aspire's ResourceReadyEvent for the database resource. When the database is up and accepting connections, it executes embedded Orleans SQL schema scripts. The database type (PostgreSQL, SQL Server, MySQL) is auto-detected from the Aspire resource.
Parameters:
orleans — The Orleans service from builder.AddOrleans()database — An Aspire database resource (e.g. from AddPostgres, AddSqlServer, AddMySql)features — Which Orleans features to provision (default: OrleansFeature.All)[Flags]
public enum OrleansFeature
{
Clustering = 1, // Membership tables for silo discovery
Persistence = 2, // Grain storage tables
Reminders = 4, // Reminder tables
All = Clustering | Persistence | Reminders
}
Use to limit which schemas are provisioned:
// Only clustering and persistence (no reminders)
orleans.WithDatabaseSetup(db, OrleansFeature.Clustering | OrleansFeature.Persistence);
// Only clustering
orleans.WithDatabaseSetup(db, OrleansFeature.Clustering);
public enum DatabaseType
{
SqlServer,
PostgreSQL,
MySql
}
Auto-detected from the Aspire resource — you do not need to specify this directly.
Shiny.Aspire.Orleans.Serverpublic static ISiloBuilder UseAdoNet(this ISiloBuilder siloBuilder)
Marker extension for discoverability. The actual provider registration happens automatically via [assembly: RegisterProvider] attributes when the package is referenced. Providers are registered for all three database types across Clustering, GrainStorage, and Reminders.
Call inside UseOrleans:
builder.UseOrleans(silo =>
{
silo.UseAdoNet();
// compose with other silo features here
});
Shiny.Aspire.Orleans.Clientpublic static IClientBuilder UseAdoNetClient(this IClientBuilder clientBuilder)
Marker extension for discoverability. Registers ADO.NET clustering provider builders for both Silo and Client targets. Clients do not need grain storage or reminders.
Call inside UseOrleansClient:
builder.UseOrleansClient(client =>
{
client.UseAdoNetClient();
});
Aspire injects the following configuration when you use .WithReference(orleans):
Orleans:Clustering:ProviderType = "PostgresDatabase"
Orleans:Clustering:ServiceKey = "orleans-db"
Orleans:GrainStorage:Default:ProviderType = "PostgresDatabase"
Orleans:GrainStorage:Default:ServiceKey = "orleans-db"
Orleans:Reminders:ProviderType = "PostgresDatabase"
Orleans:Reminders:ServiceKey = "orleans-db"
ConnectionStrings:orleans-db = "Host=...;Database=..."
Orleans' ApplyConfiguration reads these sections and delegates to the registered provider builders, which configure the ADO.NET providers with the correct connection strings and invariants.
WithDatabaseSetup runs embedded SQL scripts in order:
OrleansQuery table (query registry)OrleansMembershipVersionTable, OrleansMembershipTable, and stored proceduresOrleansStorage table and stored proceduresOrleansRemindersTable and stored proceduresSwap the Aspire resource builder — everything else stays the same:
// PostgreSQL
var db = builder.AddPostgres("pg").AddDatabase("orleans-db");
// SQL Server
var db = builder.AddSqlServer("sql").AddDatabase("orleans-db");
// MySQL
var db = builder.AddMySql("mysql").AddDatabase("orleans-db");
// AppHost
var orleans = builder.AddOrleans("cluster")
.WithClustering(db)
.WithGrainStorage("Default", db)
.WithGrainStorage("Archive", archiveDb)
.WithDatabaseSetup(db);
// Grain
public class MyGrain(
[PersistentState("state", "Default")] IPersistentState<MyState> state,
[PersistentState("archive", "Archive")] IPersistentState<ArchiveState> archive
) : Grain, IMyGrain { }
Each named provider reads from Orleans:GrainStorage:{Name}:ProviderType and Orleans:GrainStorage:{Name}:ServiceKey.
WithDatabaseSetup in the AppHost to auto-provision schemas — never require manual SQL scripts.WaitFor(db) on projects that reference Orleans, so the database is ready before the silo starts..AsClient() when wiring a client project — this provides only clustering config, not full silo config.UseOrleans and UseOrleansClient — call silo.UseAdoNet() in silo projects inside UseOrleans and client.UseAdoNetClient() in client projects inside UseOrleansClient.OrleansFeature flags if the user explicitly wants to skip certain schemas.Shiny.Aspire.Hosting.GluetunInstall Shiny.Aspire.Hosting.Gluetun in the Aspire AppHost project.
var builder = DistributedApplication.CreateBuilder(args);
var vpn = builder.AddGluetun("vpn")
.WithVpnProvider("mullvad")
.WithWireGuard(builder.AddParameter("wireguard-key", secret: true))
.WithServerCountries("US", "Canada");
var scraper = builder.AddContainer("scraper", "my-scraper")
.WithHttpEndpoint(targetPort: 8080);
vpn.WithRoutedContainer(scraper);
builder.Build().Run();
public static IResourceBuilder<GluetunResource> AddGluetun(
this IDistributedApplicationBuilder builder,
string name,
int? httpProxyPort = null,
int? shadowsocksPort = null)
Creates a Gluetun container resource with:
qmcgaw/gluetun:latest from docker.io--cap-add NET_ADMIN runtime arg--device /dev/net/tun runtime argcap_add, devices, and transfers ports from routed containersOptional port parameters expose Gluetun's built-in HTTP proxy (target port 8888) and Shadowsocks proxy (target port 8388).
vpn.WithVpnProvider("mullvad");
Sets the VPN_SERVICE_PROVIDER environment variable. Required for all Gluetun setups.
// String credentials
vpn.WithOpenVpn("username", "password");
// Aspire parameter resources (recommended for secrets)
vpn.WithOpenVpn(
builder.AddParameter("openvpn-user"),
builder.AddParameter("openvpn-pass", secret: true));
Sets VPN_TYPE=openvpn, OPENVPN_USER, and OPENVPN_PASSWORD.
// String key
vpn.WithWireGuard("my-private-key");
// Aspire parameter resource (recommended for secrets)
vpn.WithWireGuard(builder.AddParameter("wireguard-key", secret: true));
Sets VPN_TYPE=wireguard and WIREGUARD_PRIVATE_KEY.
vpn.WithServerCountries("US", "Canada", "Germany");
vpn.WithServerCities("New York", "Toronto");
Values are comma-joined and set as SERVER_COUNTRIES / SERVER_CITIES environment variables.
vpn.WithHttpProxy(); // HTTPPROXY=on
vpn.WithHttpProxy(false); // HTTPPROXY=off
vpn.WithShadowsocks(); // SHADOWSOCKS=on
vpn.WithShadowsocks(false); // SHADOWSOCKS=off
vpn.WithFirewallOutboundSubnets("10.0.0.0/8", "192.168.0.0/16");
Sets FIREWALL_OUTBOUND_SUBNETS (comma-joined). Useful for allowing traffic to local network resources outside the VPN tunnel.
vpn.WithTimezone("America/New_York");
Sets the TZ environment variable.
// String value
vpn.WithGluetunEnvironment("DNS_ADDRESS", "1.1.1.1");
// Aspire parameter resource
vpn.WithGluetunEnvironment("UPDATER_PERIOD", builder.AddParameter("updater-period"));
Generic passthrough for any Gluetun environment variable not covered by the typed methods.
vpn.WithRoutedContainer(scraper);
vpn.WithRoutedContainer(downloader);
Routes a container's traffic through the Gluetun VPN tunnel. Each call:
GluetunRoutedResourceAnnotation to the Gluetun resource--network container:<vpn-name> runtime args on the routed containernetwork_mode: "service:<vpn-name>" and transfers port mappings to the Gluetun serviceYou can route multiple containers through the same VPN.
When published as Docker Compose, routed containers automatically get:
services:
vpn:
image: qmcgaw/gluetun:latest
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun
environment:
- VPN_SERVICE_PROVIDER=mullvad
- VPN_TYPE=wireguard
- WIREGUARD_PRIVATE_KEY=${wireguard-key}
- SERVER_COUNTRIES=US,Canada
ports:
- "8080:8080" # forwarded from scraper
scraper:
image: my-scraper
network_mode: "service:vpn"
# ports moved to vpn service
namespace Aspire.Hosting.ApplicationModel;
public class GluetunResource(string name) : ContainerResource(name);
namespace Aspire.Hosting.ApplicationModel;
public sealed record GluetunRoutedResourceAnnotation(
GluetunResource GluetunResource,
ContainerResource RoutedResource) : IResourceAnnotation;
Stored on the Gluetun resource. References each container that routes through it.
WithVpnProvider — it is required for Gluetun to connect to any VPN service.ParameterResource for secrets — never hardcode private keys or passwords in the AppHost. Use builder.AddParameter("key", secret: true).WithRoutedContainer on the VPN builder — not on the container. The method is on IResourceBuilder<GluetunResource>.WithGluetunEnvironment for provider-specific settings — the typed methods cover common settings, but many providers have additional options documented in the Gluetun wiki.WithFirewallOutboundSubnets for local network access — if routed containers need to reach local services (databases, APIs) outside the VPN, allow those subnets explicitly.WithRoutedContainer multiple times on the same Gluetun resource.