diff --git a/Directory.Packages.props b/Directory.Packages.props
index 65f53ad..8d0b503 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -27,18 +27,27 @@
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/README.md b/README.md
index b653150..19e1dd7 100644
--- a/README.md
+++ b/README.md
@@ -1,353 +1,990 @@
-NOTE: The namespacing has been changed to ReactiveMarbles.Extensions.Hosting. Please update your references to the new namespace.
+# Extensions.Hosting
+
+`Extensions.Hosting` adds Generic Host integration for desktop UI frameworks, ReactiveUI/Splat, plug-ins, Windows service hosting, single-instance applications, and Identity/Entity Framework Core helpers.
+
+The public namespaces use `ReactiveMarbles.Extensions.Hosting.*`.
+
+## Package Matrix
+
+| Package | Main namespace | Purpose |
+| --- | --- | --- |
+| `CP.Extensions.Hosting.MainUIThread` | `ReactiveMarbles.Extensions.Hosting.UiThread` | Shared UI context and UI-thread base types used by the platform host packages. |
+| `CP.Extensions.Hosting.Wpf` | `ReactiveMarbles.Extensions.Hosting.Wpf` | WPF host integration, application/window registration, and WPF lifetime linking. |
+| `CP.Extensions.Hosting.WinForms` | `ReactiveMarbles.Extensions.Hosting.WinForms` | WinForms host integration, form/shell registration, and WinForms lifetime linking. |
+| `CP.Extensions.Hosting.WinUI` | `ReactiveMarbles.Extensions.Hosting.WinUI` | WinUI host integration for `Application` and main `Window` types. |
+| `CP.Extensions.Hosting.Avalonia` | `ReactiveMarbles.Extensions.Hosting.Avalonia` | Avalonia host integration, `AppBuilder` configuration, application/window registration, and Avalonia lifetime linking. |
+| `CP.Extensions.Hosting.Maui` | `ReactiveMarbles.Extensions.Hosting.Maui` | .NET MAUI host integration, `MauiAppBuilder` configuration, application/page registration, and MAUI lifetime linking. |
+| `CP.Extensions.Hosting.SingleInstance` | `ReactiveMarbles.Extensions.Hosting.AppServices` | Mutex-backed single-instance enforcement and direct named resource mutex helpers. |
+| `CP.Extensions.Hosting.Plugins` | `ReactiveMarbles.Extensions.Hosting.Plugins` | Plug-in discovery, assembly loading, ordering, and hosted-service plug-in base types. |
+| `CP.Extensions.Hosting.Plugins.Reactive` | `ReactiveMarbles.Extensions.Hosting.Reactive.Plugins` | Reactive package variant of the plug-in API with the same source and API shape. |
+| `CP.Extensions.Hosting.PluginService` | `ReactiveMarbles.Extensions.Hosting.PluginService` | Service-host bootstrapper, plug-in service discovery defaults, and `ServiceBase` lifetime support. |
+| `CP.Extensions.Hosting.PluginService.Reactive` | `ReactiveMarbles.Extensions.Hosting.Reactive.PluginService` | Reactive package variant of the service-host API, referencing `CP.Extensions.Hosting.Plugins.Reactive`. |
+| `CP.Extensions.Hosting.ReactiveUI.Wpf` | `ReactiveMarbles.Extensions.Hosting.ReactiveUI` | ReactiveUI/Splat integration for WPF using the standard ReactiveUI package set. |
+| `CP.Extensions.Hosting.ReactiveUI.Wpf.Reactive` | `ReactiveMarbles.Extensions.Hosting.Reactive.ReactiveUI` | Reactive package variant of the WPF ReactiveUI/Splat integration. |
+| `CP.Extensions.Hosting.ReactiveUI.WinForms` | `ReactiveMarbles.Extensions.Hosting.ReactiveUI` | ReactiveUI/Splat integration for WinForms. |
+| `CP.Extensions.Hosting.ReactiveUI.WinForms.Reactive` | `ReactiveMarbles.Extensions.Hosting.Reactive.ReactiveUI` | Reactive package variant of the WinForms ReactiveUI/Splat integration. |
+| `CP.Extensions.Hosting.ReactiveUI.WinUI` | `ReactiveMarbles.Extensions.Hosting.ReactiveUI` | ReactiveUI/Splat integration for WinUI. |
+| `CP.Extensions.Hosting.ReactiveUI.WinUI.Reactive` | `ReactiveMarbles.Extensions.Hosting.Reactive.ReactiveUI` | Reactive package variant of the WinUI ReactiveUI/Splat integration. |
+| `CP.Extensions.Hosting.ReactiveUI.Avalonia` | `ReactiveMarbles.Extensions.Hosting.ReactiveUI` | ReactiveUI/Splat integration for Avalonia. |
+| `CP.Extensions.Hosting.ReactiveUI.Avalonia.Reactive` | `ReactiveMarbles.Extensions.Hosting.Reactive.ReactiveUI` | Reactive package variant of the Avalonia ReactiveUI/Splat integration. |
+| `CP.Extensions.Hosting.ReactiveUI.Maui` | `ReactiveMarbles.Extensions.Hosting.ReactiveUI` | ReactiveUI/Splat integration for .NET MAUI. |
+| `CP.Extensions.Hosting.ReactiveUI.Maui.Reactive` | `ReactiveMarbles.Extensions.Hosting.Reactive.ReactiveUI` | Reactive package variant of the MAUI ReactiveUI/Splat integration. |
+| `CP.Extensions.Hosting.Identity.EntityFrameworkCore.SqlServer` | `ReactiveMarbles.Extensions.Hosting.Identity.EntityFrameworkCore` | SQL Server DbContext and ASP.NET Core Identity registration helpers. |
+| `CP.Extensions.Hosting.Identity.EntityFrameworkCore.Sqlite` | `ReactiveMarbles.Extensions.Hosting.Identity.EntityFrameworkCore.Sqlite` | SQLite DbContext and ASP.NET Core Identity registration helpers. |
+
+## Normal And Reactive Packages
+
+The normal packages use the standard `ReactiveMarbles.Extensions.Hosting.*` namespaces. The `.Reactive` packages are source-linked sibling projects compiled with `REACTIVE_SHIM` and use `ReactiveMarbles.Extensions.Hosting.Reactive.*` namespaces where a namespace split is needed.
+
+Use the normal packages when the application uses the standard ReactiveUI/ReactiveUI.Primitives package set. Use the `.Reactive` packages when the application uses `ReactiveUI.Primitives.*.Reactive` or `ReactiveUI.*.Reactive` packages. The hosting APIs are intentionally the same; migration usually means changing the package reference and namespace.
+
+Do not add direct Rx.NET package references to application projects for this repository's hosting APIs. Use the ReactiveUI.Primitives package family, and use the `.Reactive` package variants only when a reactive bridge package is required.
+
+```xml
+
+
+
+
+```
+
+```xml
+
+
+
+
+```
+
+```csharp
+// Normal package
+using ReactiveMarbles.Extensions.Hosting.Plugins;
+using ReactiveMarbles.Extensions.Hosting.ReactiveUI;
+
+// Reactive package
+using ReactiveMarbles.Extensions.Hosting.Reactive.Plugins;
+using ReactiveMarbles.Extensions.Hosting.Reactive.ReactiveUI;
+```
-# ReactiveMarbles.Extensions.Hosting
-Extensions for Microsoft.Extensions.Hosting that bring WPF, WinForms, WinUI, ReactiveUI, plug-ins, single-instance control, and common host utilities to desktop apps.
+For ReactiveUI.Primitives application code, project-file using aliases keep view models small and avoid repeated namespace noise:
-This repository supports both classic IHostBuilder and the newer IHostApplicationBuilder hosting model introduced in .NET 8+. Existing IHostBuilder APIs remain unchanged; equivalent IHostApplicationBuilder overloads are available where appropriate.
+```xml
+
+
+
+
+
+```
+
+## Target Frameworks
+
+The libraries target .NET Framework where the platform supports it, current .NET TFMs, and `net11.0` preview TFMs. Windows desktop packages use Windows-specific TFMs.
-Supported targets include .NET Framework 4.6.2/4.8, .NET Standard 2.0, and .NET 8/9/10 (Windows where applicable).
+| Area | Target frameworks |
+| --- | --- |
+| Shared, plug-ins, single instance | `net462`, `net472`, `net48`, `net481`, `net8.0`, `net9.0`, `net10.0`, `net11.0` |
+| WPF, WinForms, PluginService | .NET Framework TFMs plus `net8.0-windows`, `net9.0-windows`, `net10.0-windows`, `net11.0-windows` |
+| ReactiveUI WPF/WinForms | .NET Framework TFMs plus `net8.0-windows10.0.19041`, `net9.0-windows10.0.19041`, `net10.0-windows10.0.19041`, `net11.0-windows10.0.19041` |
+| WinUI and ReactiveUI WinUI | `net8.0-windows10.0.19041`, `net9.0-windows10.0.19041`, `net10.0-windows10.0.19041`, `net11.0-windows10.0.19041` |
+| Avalonia and ReactiveUI Avalonia | `net8.0`, `net9.0`, `net10.0`, `net11.0` |
+| MAUI and ReactiveUI MAUI | `net10.0`, `net11.0`, Android, iOS, Mac Catalyst, macOS, tvOS, and Windows where supported |
+| Identity EF Core helpers | `net8.0`, `net9.0`, `net10.0`, `net11.0` |
-## Quick start
+The repository uses Central Package Management in `Directory.Packages.props`. Notable centrally managed versions include `StyleSharp.Analyzers` `3.16.0`, `ReactiveUI.Primitives` `6.0.0`, `ReactiveUI` `24.0.0-beta.3`, and `ReactiveUI.Avalonia` `12.1.0-beta.1`.
-Choose a hosting model:
-- IHostBuilder (generic host): Host.CreateDefaultBuilder(args)
-- IHostApplicationBuilder (new app builder): Host.CreateApplicationBuilder(args)
+## Host Builder Styles
+
+Most packages support both Generic Host builder styles:
-### Example: WPF app with IHostBuilder
```csharp
using Microsoft.Extensions.Hosting;
-using ReactiveMarbles.Extensions.Hosting.Wpf;
-var host = Host.CreateDefaultBuilder(args)
- .ConfigureWpf(wpf =>
+var host = new HostBuilder()
+ .ConfigureServices(services =>
{
- // Optional: register Application type and windows via the WpfBuilder
- wpf.ApplicationType = typeof(App);
- wpf.WindowTypes.Add(typeof(MainWindow));
- wpf.ConfigureContextAction = ctx => ctx.ShutdownMode = ShutdownMode.OnMainWindowClose;
+ // Classic IHostBuilder configuration.
})
- .UseWpfLifetime(ShutdownMode.OnMainWindowClose)
.Build();
+```
+
+```csharp
+using Microsoft.Extensions.Hosting;
-await host.RunAsync();
+var builder = Host.CreateApplicationBuilder(args);
+
+// Modern IHostApplicationBuilder configuration.
+
+using var host = builder.Build();
+await host.RunAsync().ConfigureAwait(false);
```
-### Example: WPF app with IHostApplicationBuilder
+The extension methods are fluent. Platform packages usually register a context, a UI thread adapter, and an `IHostedService` that starts the UI loop with the host.
+
+## Shared UI Thread Infrastructure
+
+Package: `CP.Extensions.Hosting.MainUIThread`
+
+Namespace: `ReactiveMarbles.Extensions.Hosting.UiThread`
+
+### API
+
+| API | Description |
+| --- | --- |
+| `IUiContext.IsLifetimeLinked` | When `true`, the host is stopped when the UI application exits. |
+| `IUiContext.IsRunning` | Tracks whether the UI application has started and is still running. |
+| `BaseUiContext` | Simple base implementation of `IUiContext`. |
+| `BaseUiThread` | Base class for UI-thread adapters where `TContext : class, IUiContext`. |
+| `BaseUiThread.Start()` | Signals or starts the UI loop. |
+| `BaseUiThread.Dispose()` | Releases startup synchronization resources. |
+| `BaseUiThread.PreUiThreadStart()` | Override point that runs before the UI loop starts. |
+| `BaseUiThread.UiThreadStart()` | Override point that runs the platform message loop. |
+| `BaseUiThread.HandleApplicationExit()` | Marks the UI context stopped and requests host shutdown when lifetime is linked. |
+
+### Custom UI Adapter Example
+
+Most applications consume a platform package instead of deriving from `BaseUiThread` directly. Use the base class when adding a new UI framework adapter.
+
```csharp
+using Microsoft.Extensions.DependencyInjection;
+using ReactiveMarbles.Extensions.Hosting.UiThread;
+
+public sealed class CustomUiContext : BaseUiContext
+{
+ public object? Dispatcher { get; set; }
+}
+
+public sealed class CustomUiThread(IServiceProvider services)
+ : BaseUiThread(services)
+{
+ protected override void PreUiThreadStart()
+ {
+ UiContext.Dispatcher = new object();
+ }
+
+ protected override void UiThreadStart()
+ {
+ try
+ {
+ // Run the UI framework message loop here.
+ }
+ finally
+ {
+ HandleApplicationExit();
+ }
+ }
+}
+```
+
+## WPF Hosting
+
+Package: `CP.Extensions.Hosting.Wpf`
+
+Namespace: `ReactiveMarbles.Extensions.Hosting.Wpf`
+
+### API
+
+| API | Description |
+| --- | --- |
+| `ConfigureWpf(Action? configureDelegate = null)` | Registers WPF hosting services and optional application/window types. Available on `IHostBuilder` and `IHostApplicationBuilder`. |
+| `UseWpfLifetime(ShutdownMode shutdownMode = ShutdownMode.OnLastWindowClose)` | Links host shutdown to the WPF application lifetime. Call after `ConfigureWpf`. |
+| `IWpfBuilder.UseApplication()` | Registers a WPF `Application` type. |
+| `IWpfBuilder.UseCurrentApplication(TApplication currentApplication)` | Registers an existing WPF `Application` instance. |
+| `IWpfBuilder.UseWindow()` | Registers a WPF `Window` type. If the window implements `IWpfShell`, it is also registered as the shell. |
+| `IWpfBuilder.ConfigureContext(Action configureAction)` | Customizes the WPF context before it is used. |
+| `IWpfContext.ShutdownMode` | WPF shutdown behavior. |
+| `IWpfContext.WpfApplication` | Current WPF application instance. |
+| `IWpfContext.Dispatcher` | WPF dispatcher for UI-thread work. |
+| `IWpfShell` | Marker interface for a shell window. |
+
+### Example
+
+```csharp
+using System.Windows;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ReactiveMarbles.Extensions.Hosting.Wpf;
-var builder = Host.CreateApplicationBuilder(args)
- .ConfigureWpf(wpf =>
- {
- wpf.ApplicationType = typeof(App);
- wpf.WindowTypes.Add(typeof(MainWindow));
- wpf.ConfigureContextAction = ctx => ctx.ShutdownMode = ShutdownMode.OnMainWindowClose;
- })
- .UseWpfLifetime(ShutdownMode.OnMainWindowClose);
+public sealed partial class App : Application
+{
+ private IHost? _host;
-await builder.Build().RunAsync();
+ protected override void OnStartup(StartupEventArgs e)
+ {
+ _host = new HostBuilder()
+ .ConfigureServices(services =>
+ {
+ services.AddSingleton();
+ })
+ .ConfigureWpf(wpf =>
+ {
+ wpf.UseCurrentApplication(this);
+ wpf.UseWindow();
+ wpf.ConfigureContext(context =>
+ context.ShutdownMode = ShutdownMode.OnLastWindowClose);
+ })
+ .UseWpfLifetime()
+ .Build();
+
+ _ = _host.RunAsync();
+ base.OnStartup(e);
+ }
+}
+
+public sealed partial class MainWindow : Window, IWpfShell
+{
+ public MainWindow(MainViewModel viewModel)
+ {
+ InitializeComponent();
+ DataContext = viewModel;
+ }
+}
```
----
-
-## Packages and APIs
-The following sections outline the main features, their APIs for both hosting models, and example usage.
-
-### WPF (CP.Extensions.Hosting.Wpf)
-Namespace: ReactiveMarbles.Extensions.Hosting.Wpf
-- ConfigureWpf
- - IHostBuilder ConfigureWpf(this IHostBuilder, Action?)
- - IHostApplicationBuilder ConfigureWpf(this IHostApplicationBuilder, Action?)
- - Use IWpfBuilder to set:
- - Type? ApplicationType
- - Application? Application (optional existing instance)
- - IList WindowTypes
- - Action? ConfigureContextAction
-- UseWpfLifetime
- - IHostBuilder UseWpfLifetime(this IHostBuilder, ShutdownMode = OnLastWindowClose)
- - IHostApplicationBuilder UseWpfLifetime(this IHostApplicationBuilder, ShutdownMode = OnLastWindowClose)
-- IWpfContext
- - ShutdownMode ShutdownMode { get; set; }
- - Application? WpfApplication { get; set; }
- - Dispatcher Dispatcher { get; }
- - bool IsLifetimeLinked { get; set; } (set internally when using UseWpfLifetime)
-
-Example (builder model): see Quick start above.
-
-### WinForms (CP.Extensions.Hosting.WinForms)
-Namespace: ReactiveMarbles.Extensions.Hosting.WinForms
-- ConfigureWinForms
- - IHostBuilder ConfigureWinForms(this IHostBuilder, Action?)
- - IHostApplicationBuilder ConfigureWinForms(this IHostApplicationBuilder, Action?)
-- ConfigureWinForms() where TView : Form
- - IHostBuilder ConfigureWinForms(...)
- - IHostApplicationBuilder ConfigureWinForms(...)
- - Registers the main form and, if it implements IWinFormsShell, also registers it as IWinFormsShell
-- ConfigureWinFormsShell() where TShell : Form, IWinFormsShell
- - IHostBuilder ConfigureWinFormsShell()
- - IHostApplicationBuilder ConfigureWinFormsShell()
-- UseWinFormsLifetime
- - IHostBuilder UseWinFormsLifetime(this IHostBuilder)
- - IHostApplicationBuilder UseWinFormsLifetime(this IHostApplicationBuilder)
-- IWinFormsContext
- - bool EnableVisualStyles { get; set; }
- - Dispatcher? Dispatcher { get; set; } (WinForms dispatcher abstraction)
-
-Example (application builder):
+## WinForms Hosting
+
+Package: `CP.Extensions.Hosting.WinForms`
+
+Namespace: `ReactiveMarbles.Extensions.Hosting.WinForms`
+
+### API
+
+| API | Description |
+| --- | --- |
+| `ConfigureWinForms(Action? configureAction = null)` | Registers WinForms hosting services. Available on `IHostBuilder` and `IHostApplicationBuilder`. |
+| `ConfigureWinForms(Action? configureAction = null)` | Registers a `Form` as a singleton. If it implements `IWinFormsShell`, it is also registered as the shell. |
+| `ConfigureWinFormsShell()` | Registers a shell form where `TShell : Form, IWinFormsShell`. |
+| `UseWinFormsLifetime()` | Links host shutdown to the WinForms message loop. |
+| `IWinFormsContext.EnableVisualStyles` | Enables or disables WinForms visual styles before the UI starts. |
+| `IWinFormsContext.Dispatcher` | Optional dispatcher used for marshaling work. |
+| `IWinFormsShell` | Marker interface for the main form. |
+
+### Example
+
```csharp
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ReactiveMarbles.Extensions.Hosting.WinForms;
+using System.Windows.Forms;
-var builder = Host.CreateApplicationBuilder(args)
- .ConfigureWinForms(ctx =>
+[STAThread]
+public static class Program
+{
+ public static async Task Main(string[] args)
{
- ctx.EnableVisualStyles = true;
- })
- .ConfigureWinFormsShell()
- .UseWinFormsLifetime();
-
-await builder.Build().RunAsync();
+ using var host = new HostBuilder()
+ .ConfigureServices(services =>
+ {
+ services.AddSingleton();
+ })
+ .ConfigureWinForms(context => context.EnableVisualStyles = true)
+ .UseWinFormsLifetime()
+ .Build();
+
+ await host.RunAsync().ConfigureAwait(false);
+ }
+}
+
+public sealed class MainForm : Form, IWinFormsShell
+{
+ public MainForm(MainPresenter presenter)
+ {
+ Text = presenter.Title;
+ }
+}
```
-### WinUI (CP.Extensions.Hosting.WinUI)
-Namespace: ReactiveMarbles.Extensions.Hosting.WinUI
-- ConfigureWinUI() where TApp : Microsoft.UI.Xaml.Application where TAppWindow : Microsoft.UI.Xaml.Window
- - IHostBuilder ConfigureWinUI()
- - IHostApplicationBuilder ConfigureWinUI()
-- IWinUIContext
- - Window? AppWindow { get; set; }
- - Type? AppWindowType { get; set; }
- - DispatcherQueue? Dispatcher { get; set; }
- - Application? WinUIApplication { get; set; }
-
-Example (application builder):
+## WinUI Hosting
+
+Package: `CP.Extensions.Hosting.WinUI`
+
+Namespace: `ReactiveMarbles.Extensions.Hosting.WinUI`
+
+### API
+
+| API | Description |
+| --- | --- |
+| `ConfigureWinUI()` | Registers the WinUI `Application` and main `Window` types, the WinUI context, and the hosted service. Available on `IHostBuilder` and `IHostApplicationBuilder`. |
+| `IWinUIContext.AppWindow` | Current WinUI window instance. |
+| `IWinUIContext.AppWindowType` | Window type to create. Set by `ConfigureWinUI()`. |
+| `IWinUIContext.Dispatcher` | `DispatcherQueue` for UI-thread work. |
+| `IWinUIContext.WinUIApplication` | Current WinUI application instance. |
+| `IWinUIService` | Marker interface for WinUI services. |
+
+### Example
+
```csharp
using Microsoft.Extensions.Hosting;
+using Microsoft.UI.Xaml;
using ReactiveMarbles.Extensions.Hosting.WinUI;
-var builder = Host.CreateApplicationBuilder(args)
- .ConfigureWinUI();
+public partial class App : Application
+{
+ private readonly IHost _host;
+
+ public App()
+ {
+ _host = new HostBuilder()
+ .ConfigureWinUI()
+ .Build();
+ }
+}
+
+public sealed partial class MainWindow : Window
+{
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
+```
+
+## Avalonia Hosting
+
+Package: `CP.Extensions.Hosting.Avalonia`
+
+Namespace: `ReactiveMarbles.Extensions.Hosting.Avalonia`
+
+### API
+
+| API | Description |
+| --- | --- |
+| `ConfigureAvalonia(Action? configureDelegate = null)` | Registers Avalonia hosting services and optional application/window types. Available on `IHostBuilder` and `IHostApplicationBuilder`. |
+| `UseAvaloniaLifetime(ShutdownMode shutdownMode = ShutdownMode.OnLastWindowClose)` | Links host shutdown to the Avalonia desktop lifetime. |
+| `IAvaloniaBuilder.UseApplication()` | Registers an Avalonia `Application` type. |
+| `IAvaloniaBuilder.UseCurrentApplication(TApplication currentApplication)` | Registers an existing Avalonia application instance. |
+| `IAvaloniaBuilder.UseWindow()` | Registers an Avalonia `Window` type. If it implements `IAvaloniaShell`, it is also registered as the shell. |
+| `IAvaloniaBuilder.ConfigureContext(Action configureAction)` | Customizes the Avalonia context. |
+| `IAvaloniaBuilder.ConfigureAppBuilder(Action configureAction)` | Customizes Avalonia `AppBuilder` before startup. |
+| `IAvaloniaContext.ShutdownMode` | Avalonia shutdown behavior. |
+| `IAvaloniaContext.AvaloniaApplication` | Current Avalonia application. |
+| `IAvaloniaContext.ApplicationLifetime` | Avalonia desktop lifetime. |
+| `IAvaloniaContext.Dispatcher` | Avalonia dispatcher. |
+| `IAvaloniaShell` | Marker interface for the main window. |
+
+### Example
+
+```csharp
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
+using Microsoft.Extensions.Hosting;
+using ReactiveMarbles.Extensions.Hosting.Avalonia;
+
+public static class Program
+{
+ [STAThread]
+ public static int Main(string[] args)
+ {
+ using var host = new HostBuilder()
+ .ConfigureAvalonia(avalonia =>
+ {
+ avalonia.UseApplication();
+ avalonia.UseWindow();
+ avalonia.ConfigureAppBuilder(appBuilder =>
+ appBuilder.UsePlatformDetect().LogToTrace());
+ })
+ .UseAvaloniaLifetime(ShutdownMode.OnLastWindowClose)
+ .Build();
+
+ host.Run();
+ return 0;
+ }
+}
+
+public sealed class MainWindow : Window, IAvaloniaShell
+{
+}
+```
+
+## MAUI Hosting
+
+Package: `CP.Extensions.Hosting.Maui`
+
+Namespace: `ReactiveMarbles.Extensions.Hosting.Maui`
+
+### API
-await builder.Build().RunAsync();
+| API | Description |
+| --- | --- |
+| `ConfigureMaui(Action? configureDelegate = null)` | Registers MAUI hosting services, builds a `MauiApp`, and registers application/page types. Available on `IHostBuilder` and `IHostApplicationBuilder`. |
+| `UseMauiLifetime()` | Links host shutdown to the MAUI lifetime. |
+| `ConfigureMauiShell()` | Registers a singleton shell page where `TShell : Page, IMauiShell`. |
+| `IMauiBuilder.AddSingletonPage()` | Registers a MAUI `Page` as a singleton. If it implements `IMauiShell`, it is also registered as the shell. |
+| `IMauiBuilder.UseMauiApp(Action? configureMauiApp = null)` | Registers a MAUI `Application` type and configures the underlying `MauiAppBuilder`. |
+| `IMauiBuilder.UseMauiApp(TApplication currentApplication, Action? configureMauiApp = null)` | Registers an existing MAUI application instance. |
+| `IMauiBuilder.ConfigureContext(Action configureAction)` | Customizes the MAUI context. |
+| `IMauiBuilder.MauiAppBuilder` | Underlying MAUI builder for fonts, handlers, services, and app configuration. |
+| `IMauiContext.MauiApplication` | Current MAUI application. |
+| `IMauiContext.Dispatcher` | MAUI dispatcher. |
+| `IMauiShell` | Marker interface for the main shell page. |
+
+### Example
+
+```csharp
+using Microsoft.Extensions.Hosting;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Hosting;
+using ReactiveMarbles.Extensions.Hosting.Maui;
+
+public static class MauiProgram
+{
+ public static IHost CreateHost()
+ {
+ return Host.CreateDefaultBuilder()
+ .ConfigureMaui(maui =>
+ {
+ maui.UseMauiApp(builder =>
+ {
+ builder.ConfigureFonts(fonts =>
+ {
+ fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
+ });
+ });
+
+ maui.AddSingletonPage();
+ })
+ .UseMauiLifetime()
+ .Build();
+ }
+}
+
+public sealed class AppShell : Shell, IMauiShell
+{
+}
```
-### ReactiveUI integration
-Namespaces: ReactiveMarbles.Extensions.Hosting.ReactiveUI (per UI stack)
-- ConfigureSplatForMicrosoftDependencyResolver
- - IHostBuilder ConfigureSplatForMicrosoftDependencyResolver(this IHostBuilder)
- - IHostApplicationBuilder ConfigureSplatForMicrosoftDependencyResolver(this IHostApplicationBuilder)
-- Variants:
- - WPF: Adds WithWpf() builder stage
- - WinForms: Adds WithWinForms() builder stage
- - WinUI: Adds WithWinUI() builder stage
-- MapSplatLocator
- - IHost MapSplatLocator(this IHost host, Action containerFactory)
-
-Example (WPF + ReactiveUI with application builder):
+## ReactiveUI And Splat Integration
+
+Packages:
+
+- `CP.Extensions.Hosting.ReactiveUI.Wpf`
+- `CP.Extensions.Hosting.ReactiveUI.WinForms`
+- `CP.Extensions.Hosting.ReactiveUI.WinUI`
+- `CP.Extensions.Hosting.ReactiveUI.Avalonia`
+- `CP.Extensions.Hosting.ReactiveUI.Maui`
+- `.Reactive` siblings for each package
+
+Normal namespace: `ReactiveMarbles.Extensions.Hosting.ReactiveUI`
+
+Reactive namespace: `ReactiveMarbles.Extensions.Hosting.Reactive.ReactiveUI`
+
+### API
+
+| API | Description |
+| --- | --- |
+| `ConfigureSplatForMicrosoftDependencyResolver()` on `IHostBuilder` | Registers Splat to use Microsoft.Extensions.DependencyInjection and initializes ReactiveUI for the target platform. |
+| `ConfigureSplatForMicrosoftDependencyResolver()` on `IHostApplicationBuilder` | Same behavior for the modern builder API. |
+| `MapSplatLocator(Action containerFactory)` on `IHost` | Maps Splat to the built host service provider and invokes a custom service-provider callback. |
+
+Each platform package initializes the matching ReactiveUI builder extension:
+
+| Package | ReactiveUI builder call |
+| --- | --- |
+| WPF | `WithWpf()` |
+| WinForms | `WithWinForms()` |
+| WinUI | `WithWinUI()` |
+| Avalonia | `WithAvalonia()` |
+| MAUI | `WithMaui()` |
+
+### WPF Example
+
```csharp
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ReactiveMarbles.Extensions.Hosting.ReactiveUI;
using ReactiveMarbles.Extensions.Hosting.Wpf;
+using ReactiveUI;
-var builder = Host.CreateApplicationBuilder(args)
+var host = new HostBuilder()
.ConfigureSplatForMicrosoftDependencyResolver()
- .ConfigureWpf(wpf =>
+ .ConfigureServices(services =>
{
- wpf.ApplicationType = typeof(App);
- wpf.WindowTypes.Add(typeof(MainWindow));
+ services.AddSingleton();
+ services.AddTransient, MainWindow>();
})
- .UseWpfLifetime();
+ .ConfigureWpf(wpf => wpf.UseApplication().UseWindow())
+ .UseWpfLifetime()
+ .Build();
-await builder.Build().RunAsync();
+host.MapSplatLocator(serviceProvider =>
+{
+ // Register additional Splat services that need the built provider.
+});
+
+await host.RunAsync().ConfigureAwait(false);
```
-### Plug-in system (CP.Extensions.Hosting.Plugins)
-Namespace: ReactiveMarbles.Extensions.Hosting.Plugins
-- ConfigurePlugins
- - IHostBuilder ConfigurePlugins(this IHostBuilder, Action configure)
- - IHostApplicationBuilder ConfigurePlugins(this IHostApplicationBuilder, Action configure)
-- IPluginBuilder key options (typical):
- - UseContentRoot (bool): also scan content root
- - IncludeFrameworks(params string[] globs)
- - IncludePlugins(params string[] globs)
- - AddScanDirectories(params string[] directories)
- - PluginMatcher / FrameworkMatcher (advanced globbing)
- - AssemblyScanFunc: Func> for discovery
-
-Example (application builder):
+### Reactive Package Example
+
+The API is the same. Use the `.Reactive` package and namespace.
+
```csharp
using Microsoft.Extensions.Hosting;
-using ReactiveMarbles.Extensions.Hosting.Plugins;
+using ReactiveMarbles.Extensions.Hosting.Reactive.ReactiveUI;
+using ReactiveMarbles.Extensions.Hosting.Wpf;
-var builder = Host.CreateApplicationBuilder(args)
- .ConfigurePlugins(plugins =>
+var host = new HostBuilder()
+ .ConfigureSplatForMicrosoftDependencyResolver()
+ .ConfigureWpf(wpf => wpf.UseApplication().UseWindow())
+ .UseWpfLifetime()
+ .Build();
+```
+
+### ReactiveUI.Primitives View Model Example
+
+```csharp
+using ReactiveUI;
+
+public sealed class SearchViewModel : ReactiveObject
+{
+ private string? _searchText;
+ private IReadOnlyList _results = [];
+
+ public string? SearchText
{
- plugins.UseContentRoot = true;
- // Add framework assemblies and plugin patterns
- plugins.IncludeFrameworks(@"\\netstandard2.0\\*.FrameworkLib.dll");
- plugins.IncludePlugins(@"\\Plugins\\{runtime}\\ReactiveMarbles.Plugin.*.dll");
- });
+ get => _searchText;
+ set => this.RaiseAndSetIfChanged(ref _searchText, value);
+ }
-await builder.Build().RunAsync();
+ public IReadOnlyList Results
+ {
+ get => _results;
+ private set => this.RaiseAndSetIfChanged(ref _results, value);
+ }
+
+ public void Search(IEnumerable source)
+ {
+ var filter = SearchText ?? string.Empty;
+ Results = source
+ .Where(item => item.Contains(filter, StringComparison.OrdinalIgnoreCase))
+ .ToArray();
+ }
+}
```
-### Single instance (CP.Extensions.Hosting.SingleInstance)
-Namespace: ReactiveMarbles.Extensions.Hosting.AppServices
-- ConfigureSingleInstance
- - IHostBuilder ConfigureSingleInstance(this IHostBuilder, Action configure)
- - IHostApplicationBuilder ConfigureSingleInstance(this IHostApplicationBuilder, Action configure)
- - IHostBuilder ConfigureSingleInstance(this IHostBuilder, string mutexId)
- - IHostApplicationBuilder ConfigureSingleInstance(this IHostApplicationBuilder, string mutexId)
-- IMutexBuilder
- - string MutexId { get; set; }
- - Action? WhenNotFirstInstance { get; set; }
-
-Example (application builder):
+## Single-Instance Applications
+
+Package: `CP.Extensions.Hosting.SingleInstance`
+
+Namespace: `ReactiveMarbles.Extensions.Hosting.AppServices`
+
+### API
+
+| API | Description |
+| --- | --- |
+| `ConfigureSingleInstance(Action configureAction)` | Registers mutex-backed single-instance enforcement. Available on `IHostBuilder` and `IHostApplicationBuilder`. |
+| `ConfigureSingleInstance(string mutexId)` | Convenience overload that sets only the mutex id. |
+| `IMutexBuilder.MutexId` | Unique named mutex identifier. |
+| `IMutexBuilder.IsGlobal` | Uses the `Global\` mutex namespace when `true`; otherwise uses `Local\`. |
+| `IMutexBuilder.WhenNotFirstInstance` | Callback invoked with `IHostEnvironment` and `ILogger` when another instance already owns the mutex. |
+| `ResourceMutex.Create(ILogger? logger, string? mutexId, string? resourceName = null, bool global = false)` | Creates and locks a named mutex directly. |
+| `ResourceMutex.IsLocked` | Indicates whether the mutex was acquired. |
+| `ResourceMutex.Lock()` | Attempts to acquire the mutex. |
+| `ResourceMutex.Dispose()` | Releases the mutex on the owning thread. |
+
+### Host Example
+
```csharp
using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
using ReactiveMarbles.Extensions.Hosting.AppServices;
-var builder = Host.CreateApplicationBuilder(args)
- .ConfigureSingleInstance(cfg =>
+var host = new HostBuilder()
+ .ConfigureSingleInstance(singleInstance =>
+ {
+ singleInstance.MutexId = "{D7F93D6D-6B4D-4F58-8E76-E68C51D9E92C}";
+ singleInstance.IsGlobal = false;
+ singleInstance.WhenNotFirstInstance = (environment, logger) =>
+ {
+ logger.LogWarning(
+ "Application {ApplicationName} is already running.",
+ environment.ApplicationName);
+ };
+ })
+ .Build();
+
+await host.RunAsync().ConfigureAwait(false);
+```
+
+### Direct ResourceMutex Example
+
+```csharp
+using Microsoft.Extensions.Logging;
+using ReactiveMarbles.Extensions.Hosting.AppServices;
+
+using var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
+var logger = loggerFactory.CreateLogger("Mutex");
+
+using var mutex = ResourceMutex.Create(
+ logger,
+ "import-job",
+ resourceName: "Nightly import",
+ global: false);
+
+if (!mutex.IsLocked)
+{
+ return;
+}
+
+// Run the protected work.
+```
+
+## Plug-ins
+
+Packages:
+
+- `CP.Extensions.Hosting.Plugins`
+- `CP.Extensions.Hosting.Plugins.Reactive`
+
+Normal namespace: `ReactiveMarbles.Extensions.Hosting.Plugins`
+
+Reactive namespace: `ReactiveMarbles.Extensions.Hosting.Reactive.Plugins`
+
+### API
+
+| API | Description |
+| --- | --- |
+| `ConfigurePlugins(Action configurePlugin)` | Configures plug-in scanning and loading. Available on `IHostBuilder` and `IHostApplicationBuilder`. |
+| `IPlugin.ConfigureHost(object hostBuilderContext, IServiceCollection serviceCollection)` | Called for each discovered plug-in so it can register services. |
+| `IPluginBuilder.PluginDirectories` | Root directories scanned for plug-in assemblies. |
+| `IPluginBuilder.FrameworkDirectories` | Root directories scanned for framework assemblies. |
+| `IPluginBuilder.UseContentRoot` | Adds the host content root to the scan directories when enabled. |
+| `IPluginBuilder.FailIfNoPlugins` | Throws during startup when no plug-ins are discovered. Defaults to `false`. |
+| `IPluginBuilder.FrameworkMatcher` | Glob matcher for framework assemblies. |
+| `IPluginBuilder.PluginMatcher` | Glob matcher for plug-in assemblies. |
+| `IPluginBuilder.ValidatePlugin` | Predicate used before loading a plug-in assembly path. |
+| `IPluginBuilder.AssemblyScanFunc` | Assembly scanning delegate. Defaults to scanning exported public plug-in types. |
+| `AddScanDirectories(params string[] directories)` | Adds directories to both framework and plug-in scan roots. |
+| `IncludeFrameworks(params string[] frameworkGlobs)` | Adds framework include globs. |
+| `ExcludeFrameworks(params string[] frameworkGlobs)` | Adds framework exclude globs. |
+| `IncludePlugins(params string[] pluginGlobs)` | Adds plug-in include globs. |
+| `ExcludePlugins(params string[] pluginGlobs)` | Adds plug-in exclude globs. |
+| `RequirePlugins(bool failIfNone = true)` | Sets `FailIfNoPlugins`. |
+| `PluginScanner.ByNamingConvention(Assembly pluginAssembly)` | Finds a conventional `{AssemblyName}.Plugin` type. |
+| `PluginScanner.ScanForPluginInstances(Assembly pluginAssembly)` | Finds public concrete classes implementing `IPlugin`. |
+| `PluginOrderAttribute` | Orders discovered plug-ins. Lower values run earlier. Constructors accept no value, an `int`, or an enum/object value. |
+| `PluginBase`, `PluginBase`, `PluginBase` | Base plug-ins that register one, two, or three hosted services. |
+| `HostedServiceBase` | Base hosted service with logging, cleanup, and lifetime callback hooks. |
+
+`HostedServiceBase` exposes:
+
+| Member | Description |
+| --- | --- |
+| `CleanUp` | Disposable collection cleaned when the service stops or is disposed. |
+| `Logger` | Logger passed to the base constructor. |
+| `IsDisposed` | Indicates whether the cleanup collection has been disposed. |
+| `OnStarted()` | Override to start work after the application starts. |
+| `OnStopping()` | Override to stop work and dispose resources. Call the base implementation unless you intentionally replace cleanup behavior. |
+| `OnStopped()` | Override to run final stopped logic. |
+
+### Host Scanning Example
+
+```csharp
+using Microsoft.Extensions.Hosting;
+using ReactiveMarbles.Extensions.Hosting.Plugins;
+
+var host = new HostBuilder()
+ .ConfigurePlugins(plugins =>
{
- cfg.MutexId = "{ea031523-3a63-45e5-85f2-6fa75fbf37ed}";
- cfg.WhenNotFirstInstance = (env, logger) =>
- logger.LogWarning("Application {0} already running.", env.ApplicationName);
- });
+ plugins?.AddScanDirectories(AppContext.BaseDirectory);
+ plugins?.IncludeFrameworks("frameworks/**/*.dll");
+ plugins?.IncludePlugins("plugins/**/*.Plugin.dll");
+ plugins?.ExcludePlugins("plugins/**/*.Disabled.dll");
+ plugins?.RequirePlugins();
+ if (plugins is not null)
+ {
+ plugins.AssemblyScanFunc = PluginScanner.ScanForPluginInstances;
+ }
+ })
+ .Build();
-await builder.Build().RunAsync();
+await host.RunAsync().ConfigureAwait(false);
```
-### Service host utilities (CP.Extensions.Hosting.PluginService)
-Namespace: ReactiveMarbles.Extensions.Hosting.PluginService
-- UseServiceBaseLifetime
- - IHostBuilder UseServiceBaseLifetime(this IHostBuilder)
- - IHostApplicationBuilder UseServiceBaseLifetime(this IHostApplicationBuilder)
-- UseConsoleLifetime (IHostApplicationBuilder only)
- - IHostApplicationBuilder UseConsoleLifetime(this IHostApplicationBuilder)
-- RunAsServiceAsync
- - Task RunAsServiceAsync(this IHostBuilder, CancellationToken = default)
- - Task RunAsServiceAsync(this HostApplicationBuilder, CancellationToken = default)
-- ServiceHost
- - Task Create(Type type, string[] args, Func? configureHostBuilder = null, Action? configureHost = null, string nameSpace = "ReactiveMarbles.Plugin", string? targetRuntime = null)
- - Task CreateApplication(Type type, string[] args, Func? configureHostBuilder = null, Action? configureHost = null, string nameSpace = "ReactiveMarbles.Plugin", string? targetRuntime = null)
-
-Example (service/console dual mode using IHostApplicationBuilder):
+### Plug-in Assembly Example
+
```csharp
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
-using ReactiveMarbles.Extensions.Hosting.PluginService;
+using Microsoft.Extensions.Logging;
+using ReactiveMarbles.Extensions.Hosting.Plugins;
+
+[PluginOrder(10)]
+public sealed class Plugin : IPlugin
+{
+ public void ConfigureHost(object hostBuilderContext, IServiceCollection serviceCollection)
+ {
+ serviceCollection.AddHostedService();
+ }
+}
+
+public sealed class ImportHostedService(
+ ILogger logger,
+ IHostApplicationLifetime lifetime)
+ : HostedServiceBase(logger, lifetime)
+{
+ public override Task OnStarted()
+ {
+ Logger.LogInformation("Import service started.");
+ return Task.CompletedTask;
+ }
+}
+```
+
+### Plug-in Base Example
+
+```csharp
+using ReactiveMarbles.Extensions.Hosting.Plugins;
+
+public sealed class Plugin
+ : PluginBase
+{
+}
+```
+
+### Reactive Plug-in Example
+
+Only the package and namespace change.
+
+```csharp
+using Microsoft.Extensions.DependencyInjection;
+using ReactiveMarbles.Extensions.Hosting.Reactive.Plugins;
-var builder = Host.CreateApplicationBuilder(args)
- .UseContentRoot(Directory.GetCurrentDirectory())
- .ConfigureLogging()
- .ConfigureConfiguration(args)
- .UseConsoleLifetime(); // or .UseServiceBaseLifetime() for Windows Service
+public sealed class Plugin : IPlugin
+{
+ public void ConfigureHost(object hostBuilderContext, IServiceCollection serviceCollection)
+ {
+ serviceCollection.AddSingleton();
+ }
+}
+```
+
+## Plug-in Service Hosting
+
+Packages:
+
+- `CP.Extensions.Hosting.PluginService`
+- `CP.Extensions.Hosting.PluginService.Reactive`
+
+Normal namespace: `ReactiveMarbles.Extensions.Hosting.PluginService`
+
+Reactive namespace: `ReactiveMarbles.Extensions.Hosting.Reactive.PluginService`
+
+### API
+
+| API | Description |
+| --- | --- |
+| `ServiceHost.Logger` | Gets the `ILogger` created by the service host after startup. |
+| `ServiceHost.Create(Type type, string[] args, Func? hostBuilder = default, Action? configureHost = default, string nameSpace = "ReactiveMarbles.Plugin", string? targetRuntime = null)` | Creates, configures, and runs a classic `IHostBuilder` service host. |
+| `ServiceHost.CreateApplication(Type type, string[] args, Func? hostBuilder = default, Action? configureHost = default, string nameSpace = "ReactiveMarbles.Plugin", string? targetRuntime = null)` | Creates, configures, and runs a modern `IHostApplicationBuilder` service host. |
+| `UseServiceBaseLifetime()` on `IHostBuilder` | Registers `ServiceBaseLifetime` as `IHostLifetime`. |
+| `UseServiceBaseLifetime()` on `IHostApplicationBuilder` | Registers `ServiceBaseLifetime` as `IHostLifetime`. |
+| `UseConsoleLifetime()` on `IHostApplicationBuilder` | Registers `ConsoleLifetime` as `IHostLifetime`. |
+| `RunAsServiceAsync(CancellationToken cancellationToken = default)` on `IHostBuilder` | Builds and runs with `ServiceBaseLifetime`. |
+| `RunAsServiceAsync(CancellationToken cancellationToken = default)` on `HostApplicationBuilder` | Builds and runs with `ServiceBaseLifetime`. |
+| `ServiceBaseLifetime` | `IHostLifetime` implementation backed by `System.ServiceProcess.ServiceBase`. |
+
+`ServiceHost` configures:
+
+- Content root as the current directory.
+- Logging from configuration plus console, event log, log4net, and debug providers.
+- Host configuration from `hostsettings.json`, environment variables with the `PREFIX_` prefix, and command line.
+- Application configuration from `appsettings.json`, `appsettings.{Environment}.json`, environment variables with the `PREFIX_` prefix, and command line.
+- Plug-in scanning from the process directory, framework assemblies matching `\netstandard2.0\*.FrameworkLib.dll`, and plug-ins matching `\Plugins\{runtime}\{nameSpace}*.dll`.
+- Windows service lifetime when a debugger is not attached and `--console` is not supplied; console lifetime otherwise.
+
+### Console Or Service Entry Point
+
+```csharp
+using ReactiveMarbles.Extensions.Hosting.PluginService;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
-await builder.Build().RunAsync();
+await ServiceHost.Create(
+ typeof(Program),
+ args,
+ hostBuilder: builder => builder?.ConfigureServices(services =>
+ {
+ services.AddSingleton();
+ }),
+ configureHost: host =>
+ {
+ ServiceHost.Logger?.LogInformation("Host configured.");
+ },
+ nameSpace: "Contoso.Plugin",
+ targetRuntime: "win-x64")
+ .ConfigureAwait(false);
```
-Example (helper):
+### Modern Builder Entry Point
+
```csharp
using ReactiveMarbles.Extensions.Hosting.PluginService;
await ServiceHost.CreateApplication(
typeof(Program),
args,
- hb => hb // external builder customization
- .ConfigurePlugins(pb =>
- {
- /* plugin globs */
- pb.RequirePlugins(true); // fail if none found - optional default false
- }),
- host => { /* use host.Services */ },
- nameSpace: "ReactiveMarbles.Plugin");
+ hostBuilder: builder =>
+ {
+ builder?.Services.AddSingleton();
+ return builder;
+ })
+ .ConfigureAwait(false);
+```
+
+### Direct Service Lifetime Example
+
+```csharp
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using ReactiveMarbles.Extensions.Hosting.PluginService;
+
+var builder = Host.CreateApplicationBuilder(args);
+builder.Services.AddHostedService();
+builder.UseServiceBaseLifetime();
+
+await builder.Build().RunAsync().ConfigureAwait(false);
```
-### Identity + Entity Framework Core
-Namespaces:
-- ReactiveMarbles.Extensions.Hosting.Identity.EntityFrameworkCore (SqlServer)
-- ReactiveMarbles.Extensions.Hosting.Identity.EntityFrameworkCore.Sqlite (Sqlite)
+### Reactive Service Host Example
-APIs (service collection extensions used inside UseWebHostServices):
-- IServiceCollection UseEntityFrameworkCoreSqlServer(WebHostBuilderContext, string connectionStringName, ServiceLifetime = Scoped)
-- IServiceCollection UseEntityFrameworkCoreSqlServer(WebHostBuilderContext, string connectionStringName, ServiceLifetime = Scoped)
-- IServiceCollection UseEntityFrameworkCoreSqlite(WebHostBuilderContext, string connectionStringName, ServiceLifetime = Scoped)
-- IServiceCollection UseEntityFrameworkCoreSqlite(WebHostBuilderContext, string connectionStringName, ServiceLifetime = Scoped)
+```csharp
+using ReactiveMarbles.Extensions.Hosting.Reactive.PluginService;
+
+await ServiceHost.CreateApplication(typeof(Program), args)
+ .ConfigureAwait(false);
+```
+
+## Identity And Entity Framework Core
+
+SQL Server package: `CP.Extensions.Hosting.Identity.EntityFrameworkCore.SqlServer`
+
+SQL Server namespace: `ReactiveMarbles.Extensions.Hosting.Identity.EntityFrameworkCore`
+
+SQLite package: `CP.Extensions.Hosting.Identity.EntityFrameworkCore.Sqlite`
+
+SQLite namespace: `ReactiveMarbles.Extensions.Hosting.Identity.EntityFrameworkCore.Sqlite`
+
+### SQL Server API
-Host/web host wiring:
-- IHostBuilder UseWebHostServices(this IHostBuilder, Action configureServices, bool validateScopes = false)
-- IHostBuilder UseWebHostServices(this IHostBuilder, Action configureServices, Func configureWebHost, bool validateScopes = false)
-- IHostBuilder UseWebHostServices(this IHostBuilder, Action configureServices, Func configureWebHost, Func configureApp, bool validateScopes = false)
+| API | Description |
+| --- | --- |
+| `IConfiguration.HasConnectionString(string connectionStringName)` | Returns `true` when the named connection string exists and is not empty. |
+| `IConfiguration.GetRequiredConnectionString(string connectionStringName)` | Gets the named connection string or throws a descriptive `InvalidOperationException`. |
+| `IHostApplicationBuilder.AddSqlServerDbContext(string connectionStringName, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)` | Registers a SQL Server `DbContext`. |
+| `IHostApplicationBuilder.AddSqlServerWithIdentity(string connectionStringName, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)` | Registers SQL Server EF Core plus ASP.NET Core Identity with roles. |
+| `IHostApplicationBuilder.AddSqlServerWithIdentity(string connectionStringName, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)` | Registers SQL Server EF Core plus ASP.NET Core Identity without a custom role type. |
+| `IHostBuilder.UseWebHostServices(...)` | Adds minimal ASP.NET Core web-host services. Three overloads support service-only, web-host customization, and web-host plus app-pipeline customization. |
+| `IServiceCollection.UseEntityFrameworkCoreSqlServer(WebHostBuilderContext context, string connectionStringName, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)` | Registers SQL Server EF Core plus Identity with roles in a web-host service callback. |
+| `IServiceCollection.UseEntityFrameworkCoreSqlServer(WebHostBuilderContext context, string connectionStringName, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)` | Registers SQL Server EF Core plus Identity without a custom role type. |
+| `IServiceCollection.AddSqlServerDbContext(IConfiguration configuration, string connectionStringName, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)` | Registers a SQL Server `DbContext` from configuration. |
+| `IServiceCollection.AddSqlServerDbContextWithConnectionString(string connectionString, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)` | Registers a SQL Server `DbContext` from a direct connection string. |
+
+### SQLite API
+
+| API | Description |
+| --- | --- |
+| `IHostApplicationBuilder.AddSqliteDbContext(string connectionStringName, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)` | Registers a SQLite `DbContext`. |
+| `IHostApplicationBuilder.AddSqliteWithIdentity(string connectionStringName, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)` | Registers SQLite EF Core plus ASP.NET Core Identity with roles. |
+| `IHostApplicationBuilder.AddSqliteWithIdentity(string connectionStringName, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)` | Registers SQLite EF Core plus ASP.NET Core Identity without a custom role type. |
+| `IHostBuilder.UseWebHostServices(...)` | Adds minimal ASP.NET Core web-host services. Three overloads match the SQL Server package. |
+| `IServiceCollection.UseEntityFrameworkCoreSqlite(WebHostBuilderContext context, string connectionStringName, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)` | Registers SQLite EF Core plus Identity with roles in a web-host service callback. |
+| `IServiceCollection.UseEntityFrameworkCoreSqlite(WebHostBuilderContext context, string connectionStringName, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)` | Registers SQLite EF Core plus Identity without a custom role type. |
+| `IServiceCollection.AddSqliteDbContext(IConfiguration configuration, string connectionStringName, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)` | Registers a SQLite `DbContext` from configuration. |
+| `IServiceCollection.AddSqliteDbContextWithConnectionString(string connectionString, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)` | Registers a SQLite `DbContext` from a direct connection string. |
+| `CreateInMemoryConnectionString(string? databaseName = null)` | Creates a SQLite in-memory connection string. |
+| `CreateFileConnectionString(string filePath)` | Creates a SQLite file connection string. |
+
+### Modern Builder Example
-Example:
```csharp
using Microsoft.AspNetCore.Identity;
-using Microsoft.Extensions.DependencyInjection;
+using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using ReactiveMarbles.Extensions.Hosting.Identity.EntityFrameworkCore;
-var host = Host.CreateDefaultBuilder(args)
- .UseWebHostServices((whb, services) =>
- {
- services.UseEntityFrameworkCoreSqlServer(whb, "DefaultConnection");
- })
- .Build();
+var builder = Host.CreateApplicationBuilder(args);
+
+builder.AddSqlServerWithIdentity(
+ "DefaultConnection");
-await host.RunAsync();
+using var host = builder.Build();
+await host.RunAsync().ConfigureAwait(false);
+
+public sealed class AppDbContext(DbContextOptions options)
+ : IdentityDbContext(options)
+{
+}
```
-### Reactive examples
-- WPF + ReactiveUI + Single instance:
+### Web Host Services Example
+
```csharp
-var builder = Host.CreateApplicationBuilder(args)
- .ConfigureSplatForMicrosoftDependencyResolver()
- .ConfigureWpf(wpf =>
+using Microsoft.AspNetCore.Identity;
+using Microsoft.Extensions.Hosting;
+using ReactiveMarbles.Extensions.Hosting.Identity.EntityFrameworkCore.Sqlite;
+
+var host = new HostBuilder()
+ .UseWebHostServices((context, services) =>
{
- wpf.ApplicationType = typeof(App);
- wpf.WindowTypes.Add(typeof(MainWindow));
+ services.UseEntityFrameworkCoreSqlite(
+ context,
+ "DefaultConnection");
})
- .UseWpfLifetime()
- .ConfigureSingleInstance("{ea031523-3a63-45e5-85f2-6fa75fbf37ed}");
+ .Build();
-await builder.Build().RunAsync();
+await host.RunAsync().ConfigureAwait(false);
```
-### Notes
-- WPF/WinForms/WinUI components target Windows only.
-- When using IHostApplicationBuilder, prefer chaining extension methods that return IHostApplicationBuilder so you can call Build() on the final HostApplicationBuilder instance.
-- Plugin scanning uses glob patterns. Ensure your plugin folders are copied to output and the patterns match your runtime folder (e.g., Plugins\net9.0-windows\Your.Plugin.*.dll).
+### Direct SQLite Connection Example
----
+```csharp
+using Microsoft.Extensions.DependencyInjection;
+using ReactiveMarbles.Extensions.Hosting.Identity.EntityFrameworkCore.Sqlite;
+
+var services = new ServiceCollection();
+var connectionString =
+ HostBuilderEntityFrameworkCoreExtensions.CreateInMemoryConnectionString("tests");
+
+services.AddSqliteDbContextWithConnectionString(connectionString);
+```
+
+## Recommended Composition
-## Legacy snippets (IHostBuilder only)
+The packages are designed to compose as small host extensions. A desktop app can combine single-instance enforcement, ReactiveUI/Splat, platform lifetime, and plug-in loading in one host.
-#### ReactiveMarbles.Extensions.Hosting.Plugins
```csharp
-.ConfigurePlugins(pluginBuilder =>
-{
- Console.ForegroundColor = ConsoleColor.Yellow;
- Console.WriteLine("Running using dotNet {0}", Environment.Version);
+using Microsoft.Extensions.Hosting;
+using ReactiveMarbles.Extensions.Hosting.AppServices;
+using ReactiveMarbles.Extensions.Hosting.Plugins;
+using ReactiveMarbles.Extensions.Hosting.ReactiveUI;
+using ReactiveMarbles.Extensions.Hosting.Wpf;
- var process = Process.GetCurrentProcess();
- var fullPath = process.MainModule?.FileName?.Replace(process.MainModule.ModuleName!, string.Empty);
- Console.WriteLine("Add Scan Directories: {0}", fullPath);
- pluginBuilder?.AddScanDirectories(fullPath!);
+var host = new HostBuilder()
+ .ConfigureSplatForMicrosoftDependencyResolver()
+ .ConfigureSingleInstance("{1E938C68-6B3E-4105-BE52-2432FE80F19C}")
+ .ConfigurePlugins(plugins =>
+ {
+ plugins?.AddScanDirectories(AppContext.BaseDirectory);
+ plugins?.IncludePlugins("plugins/**/*.dll");
+ })
+ .ConfigureWpf(wpf => wpf.UseApplication().UseWindow())
+ .UseWpfLifetime()
+ .Build();
+
+await host.RunAsync().ConfigureAwait(false);
+```
+
+## Build And Test
- pluginBuilder?.IncludeFrameworks(@"\netstandard2.0\*.FrameworkLib.dll");
+The repository uses the XML `.slnx` solution format and Microsoft Testing Platform with TUnit.
- var runtime = Path.GetFileName(AppContext.BaseDirectory);
- Console.WriteLine(@"Include Plugins from: \Plugins\{0}\{1}*.dll", runtime, "ReactiveMarbles.Plugin");
- pluginBuilder?.IncludePlugins(@$"\Plugins\{runtime}\{{YourPluginNamespace}}*.dll");
- Console.ResetColor();
-})
+```powershell
+cd src
+dotnet workload restore
+dotnet restore Extensions.Hosting.slnx
+dotnet build Extensions.Hosting.slnx -c Release -warnaserror
+dotnet test --solution Extensions.Hosting.slnx -c Release
+dotnet test --solution Extensions.Hosting.slnx --coverage --coverage-output-format cobertura
```
-#### ReactiveMarbles.Extensions.Hosting.PluginService
-```csharp
-await ServiceHost.Create(
- typeof(Program),
- args,
- hb => hb, // Configure the HostBuilder
- host => {}, // Configure the Host
- nameSpace: "ReactiveMarbles.Plugin").ConfigureAwait(false);
+Run full builds on Windows because WPF, WinForms, WinUI, Windows service, and .NET Framework target frameworks are part of the solution.
diff --git a/src/Extensions.Hosting.PluginService.Reactive/Extensions.Hosting.PluginService.Reactive.csproj b/src/Extensions.Hosting.PluginService.Reactive/Extensions.Hosting.PluginService.Reactive.csproj
new file mode 100644
index 0000000..95159fc
--- /dev/null
+++ b/src/Extensions.Hosting.PluginService.Reactive/Extensions.Hosting.PluginService.Reactive.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net462;net472;net48;net481;net8.0-windows;net9.0-windows;net10.0-windows;net11.0-windows
+ enable
+ enable
+ CP.Extensions.Hosting.PluginService.Reactive
+ $(DefineConstants);REACTIVE_SHIM
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Extensions.Hosting.PluginService/DefaultLogger.cs b/src/Extensions.Hosting.PluginService/DefaultLogger.cs
index af13208..0e93a9c 100644
--- a/src/Extensions.Hosting.PluginService/DefaultLogger.cs
+++ b/src/Extensions.Hosting.PluginService/DefaultLogger.cs
@@ -4,7 +4,11 @@
using Microsoft.Extensions.Logging;
+#if REACTIVE_SHIM
+namespace ReactiveMarbles.Extensions.Hosting.Reactive.PluginService;
+#else
namespace ReactiveMarbles.Extensions.Hosting.PluginService;
+#endif
/// Provides a default implementation of a logger that wraps an existing instance.
/// The underlying instance to use for logging operations. Cannot be null.
diff --git a/src/Extensions.Hosting.PluginService/ServiceBaseLifetime.cs b/src/Extensions.Hosting.PluginService/ServiceBaseLifetime.cs
index 547fda8..c3bdc57 100644
--- a/src/Extensions.Hosting.PluginService/ServiceBaseLifetime.cs
+++ b/src/Extensions.Hosting.PluginService/ServiceBaseLifetime.cs
@@ -5,7 +5,11 @@
using System.ServiceProcess;
using Microsoft.Extensions.Hosting;
+#if REACTIVE_SHIM
+namespace ReactiveMarbles.Extensions.Hosting.Reactive.PluginService;
+#else
namespace ReactiveMarbles.Extensions.Hosting.PluginService;
+#endif
/// Provides an implementation backed by .
///
diff --git a/src/Extensions.Hosting.PluginService/ServiceBaseLifetimeHostExtensions.cs b/src/Extensions.Hosting.PluginService/ServiceBaseLifetimeHostExtensions.cs
index 0156c66..41bdf78 100644
--- a/src/Extensions.Hosting.PluginService/ServiceBaseLifetimeHostExtensions.cs
+++ b/src/Extensions.Hosting.PluginService/ServiceBaseLifetimeHostExtensions.cs
@@ -6,7 +6,11 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Hosting.Internal;
+#if REACTIVE_SHIM
+namespace ReactiveMarbles.Extensions.Hosting.Reactive.PluginService;
+#else
namespace ReactiveMarbles.Extensions.Hosting.PluginService;
+#endif
/// Provides extension methods for configuring host lifetimes to enable running .NET applications as Windows services or with console lifetime support.
/// These extensions allow integration of service-based or console-based lifetimes into host builders,
diff --git a/src/Extensions.Hosting.PluginService/ServiceHost.cs b/src/Extensions.Hosting.PluginService/ServiceHost.cs
index fe4ed61..bb236da 100644
--- a/src/Extensions.Hosting.PluginService/ServiceHost.cs
+++ b/src/Extensions.Hosting.PluginService/ServiceHost.cs
@@ -7,10 +7,18 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
+#if REACTIVE_SHIM
+using ReactiveMarbles.Extensions.Hosting.Reactive.Plugins;
+#else
using ReactiveMarbles.Extensions.Hosting.Plugins;
+#endif
using ReactiveMarbles.Extensions.Logging;
+#if REACTIVE_SHIM
+namespace ReactiveMarbles.Extensions.Hosting.Reactive.PluginService;
+#else
namespace ReactiveMarbles.Extensions.Hosting.PluginService;
+#endif
/// Provides static methods for configuring and running a host or application host with plugin and logging support.
/// The ServiceHost class simplifies the setup of .NET hosts, including configuration, logging, and
diff --git a/src/Extensions.Hosting.ReactiveUI.Avalonia.Reactive/Extensions.Hosting.ReactiveUI.Avalonia.Reactive.csproj b/src/Extensions.Hosting.ReactiveUI.Avalonia.Reactive/Extensions.Hosting.ReactiveUI.Avalonia.Reactive.csproj
new file mode 100644
index 0000000..137a849
--- /dev/null
+++ b/src/Extensions.Hosting.ReactiveUI.Avalonia.Reactive/Extensions.Hosting.ReactiveUI.Avalonia.Reactive.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net8.0;net9.0;net10.0;net11.0
+ enable
+ enable
+ CP.Extensions.Hosting.ReactiveUI.Avalonia.Reactive
+ $(DefineConstants);REACTIVE_SHIM
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Extensions.Hosting.ReactiveUI.Avalonia/HostBuilderReactiveUiExtensions.cs b/src/Extensions.Hosting.ReactiveUI.Avalonia/HostBuilderReactiveUiExtensions.cs
index 4ca054e..7435edb 100644
--- a/src/Extensions.Hosting.ReactiveUI.Avalonia/HostBuilderReactiveUiExtensions.cs
+++ b/src/Extensions.Hosting.ReactiveUI.Avalonia/HostBuilderReactiveUiExtensions.cs
@@ -3,12 +3,21 @@
// See the LICENSE file in the project root for full license information.
using Microsoft.Extensions.Hosting;
+#if REACTIVE_SHIM
+using ReactiveUI.Avalonia.Reactive;
+using ReactiveUI.Reactive.Builder;
+#else
using ReactiveUI.Avalonia;
using ReactiveUI.Builder;
+#endif
using Splat;
using Splat.Microsoft.Extensions.DependencyInjection;
+#if REACTIVE_SHIM
+namespace ReactiveMarbles.Extensions.Hosting.Reactive.ReactiveUI;
+#else
namespace ReactiveMarbles.Extensions.Hosting.ReactiveUI;
+#endif
/// Provides extension methods for configuring ReactiveUI with Microsoft dependency injection in .NET host builder scenarios.
/// These extension methods enable seamless integration of ReactiveUI and Splat with the Microsoft
diff --git a/src/Extensions.Hosting.ReactiveUI.Maui.Reactive/Extensions.Hosting.ReactiveUI.Maui.Reactive.csproj b/src/Extensions.Hosting.ReactiveUI.Maui.Reactive/Extensions.Hosting.ReactiveUI.Maui.Reactive.csproj
new file mode 100644
index 0000000..42eead1
--- /dev/null
+++ b/src/Extensions.Hosting.ReactiveUI.Maui.Reactive/Extensions.Hosting.ReactiveUI.Maui.Reactive.csproj
@@ -0,0 +1,36 @@
+
+
+
+ net10.0;net11.0;net10.0-android;net11.0-android;net10.0-ios;net10.0-tvos;net10.0-macos;net10.0-maccatalyst;net11.0-ios;net11.0-tvos;net11.0-macos;net11.0-maccatalyst;
+ $(TargetFrameworks);net10.0-windows10.0.19041.0;net11.0-windows10.0.19041.0
+ enable
+ enable
+ true
+ CP.Extensions.Hosting.ReactiveUI.Maui.Reactive
+ $(DefineConstants);REACTIVE_SHIM
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Extensions.Hosting.ReactiveUI.Maui/HostBuilderReactiveUiExtensions.cs b/src/Extensions.Hosting.ReactiveUI.Maui/HostBuilderReactiveUiExtensions.cs
index 4edef8d..57c6154 100644
--- a/src/Extensions.Hosting.ReactiveUI.Maui/HostBuilderReactiveUiExtensions.cs
+++ b/src/Extensions.Hosting.ReactiveUI.Maui/HostBuilderReactiveUiExtensions.cs
@@ -3,11 +3,19 @@
// See the LICENSE file in the project root for full license information.
using Microsoft.Extensions.Hosting;
+#if REACTIVE_SHIM
+using ReactiveUI.Reactive.Builder;
+#else
using ReactiveUI.Builder;
+#endif
using Splat;
using Splat.Microsoft.Extensions.DependencyInjection;
+#if REACTIVE_SHIM
+namespace ReactiveMarbles.Extensions.Hosting.Reactive.ReactiveUI;
+#else
namespace ReactiveMarbles.Extensions.Hosting.ReactiveUI;
+#endif
/// Provides extension methods for configuring ReactiveUI integration with Microsoft dependency injection in .NET host builders.
/// These extensions enable seamless setup of ReactiveUI and Splat with Microsoft.Extensions.Hosting and
diff --git a/src/Extensions.Hosting.ReactiveUI.WinForms.Reactive/Extensions.Hosting.ReactiveUI.WinForms.Reactive.csproj b/src/Extensions.Hosting.ReactiveUI.WinForms.Reactive/Extensions.Hosting.ReactiveUI.WinForms.Reactive.csproj
new file mode 100644
index 0000000..ee4ce25
--- /dev/null
+++ b/src/Extensions.Hosting.ReactiveUI.WinForms.Reactive/Extensions.Hosting.ReactiveUI.WinForms.Reactive.csproj
@@ -0,0 +1,28 @@
+
+
+
+
+ net462;net472;net48;net481;net8.0-windows10.0.19041;net9.0-windows10.0.19041;net10.0-windows10.0.19041;net11.0-windows10.0.19041
+ enable
+ enable
+ This extension adds reactive ReactiveUI support to generic host based WinForms applications.
+ CP.Extensions.Hosting.ReactiveUI.WinForms.Reactive
+ $(DefineConstants);REACTIVE_SHIM
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Extensions.Hosting.ReactiveUI.WinForms/HostBuilderReactiveUiExtensions.cs b/src/Extensions.Hosting.ReactiveUI.WinForms/HostBuilderReactiveUiExtensions.cs
index 0aa305c..44c0336 100644
--- a/src/Extensions.Hosting.ReactiveUI.WinForms/HostBuilderReactiveUiExtensions.cs
+++ b/src/Extensions.Hosting.ReactiveUI.WinForms/HostBuilderReactiveUiExtensions.cs
@@ -3,11 +3,19 @@
// See the LICENSE file in the project root for full license information.
using Microsoft.Extensions.Hosting;
+#if REACTIVE_SHIM
+using ReactiveUI.Reactive.Builder;
+#else
using ReactiveUI.Builder;
+#endif
using Splat;
using Splat.Microsoft.Extensions.DependencyInjection;
+#if REACTIVE_SHIM
+namespace ReactiveMarbles.Extensions.Hosting.Reactive.ReactiveUI;
+#else
namespace ReactiveMarbles.Extensions.Hosting.ReactiveUI;
+#endif
/// Provides extension methods for configuring ReactiveUI with Microsoft dependency injection in .NET host builders.
/// These extensions enable integration of ReactiveUI's dependency resolution with the
diff --git a/src/Extensions.Hosting.ReactiveUI.WinUI.Reactive/Extensions.Hosting.ReactiveUI.WinUI.Reactive.csproj b/src/Extensions.Hosting.ReactiveUI.WinUI.Reactive/Extensions.Hosting.ReactiveUI.WinUI.Reactive.csproj
new file mode 100644
index 0000000..95931d0
--- /dev/null
+++ b/src/Extensions.Hosting.ReactiveUI.WinUI.Reactive/Extensions.Hosting.ReactiveUI.WinUI.Reactive.csproj
@@ -0,0 +1,29 @@
+
+
+
+
+ net8.0-windows10.0.19041;net9.0-windows10.0.19041;net10.0-windows10.0.19041;net11.0-windows10.0.19041
+ enable
+ enable
+ $(NoWarn);NETSDK1206
+ win-x64;win-x86;win-arm64
+ This extension adds reactive ReactiveUI support to generic host based WinUI applications.
+ CP.Extensions.Hosting.ReactiveUI.WinUI.Reactive
+ $(DefineConstants);REACTIVE_SHIM
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Extensions.Hosting.ReactiveUI.WinUI/HostBuilderReactiveUiExtensions.cs b/src/Extensions.Hosting.ReactiveUI.WinUI/HostBuilderReactiveUiExtensions.cs
index 1fe75de..8ff3e61 100644
--- a/src/Extensions.Hosting.ReactiveUI.WinUI/HostBuilderReactiveUiExtensions.cs
+++ b/src/Extensions.Hosting.ReactiveUI.WinUI/HostBuilderReactiveUiExtensions.cs
@@ -4,10 +4,18 @@
using Microsoft.Extensions.Hosting;
using ReactiveUI.Builder;
+
+#if REACTIVE_SHIM
+using ReactiveUI.Reactive.Builder;
+#endif
using Splat;
using Splat.Microsoft.Extensions.DependencyInjection;
+#if REACTIVE_SHIM
+namespace ReactiveMarbles.Extensions.Hosting.Reactive.ReactiveUI;
+#else
namespace ReactiveMarbles.Extensions.Hosting.ReactiveUI;
+#endif
/// Provides extension methods for configuring ReactiveUI integration with Microsoft dependency injection in .NET host builders.
/// These extensions enable seamless setup of ReactiveUI and Splat with Microsoft.Extensions.Hosting and
diff --git a/src/Extensions.Hosting.ReactiveUI.Wpf.Reactive/Extensions.Hosting.ReactiveUI.Wpf.Reactive.csproj b/src/Extensions.Hosting.ReactiveUI.Wpf.Reactive/Extensions.Hosting.ReactiveUI.Wpf.Reactive.csproj
new file mode 100644
index 0000000..bcb05ab
--- /dev/null
+++ b/src/Extensions.Hosting.ReactiveUI.Wpf.Reactive/Extensions.Hosting.ReactiveUI.Wpf.Reactive.csproj
@@ -0,0 +1,28 @@
+
+
+
+
+ net462;net472;net48;net481;net8.0-windows10.0.19041;net9.0-windows10.0.19041;net10.0-windows10.0.19041;net11.0-windows10.0.19041
+ enable
+ enable
+ This extension adds reactive ReactiveUI support to generic host based WPF applications.
+ CP.Extensions.Hosting.ReactiveUI.Wpf.Reactive
+ $(DefineConstants);REACTIVE_SHIM
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Extensions.Hosting.ReactiveUI.Wpf/HostBuilderReactiveUiExtensions.cs b/src/Extensions.Hosting.ReactiveUI.Wpf/HostBuilderReactiveUiExtensions.cs
index feb65c3..1c709f1 100644
--- a/src/Extensions.Hosting.ReactiveUI.Wpf/HostBuilderReactiveUiExtensions.cs
+++ b/src/Extensions.Hosting.ReactiveUI.Wpf/HostBuilderReactiveUiExtensions.cs
@@ -3,11 +3,19 @@
// See the LICENSE file in the project root for full license information.
using Microsoft.Extensions.Hosting;
+#if REACTIVE_SHIM
+using ReactiveUI.Reactive.Builder;
+#else
using ReactiveUI.Builder;
+#endif
using Splat;
using Splat.Microsoft.Extensions.DependencyInjection;
+#if REACTIVE_SHIM
+namespace ReactiveMarbles.Extensions.Hosting.Reactive.ReactiveUI;
+#else
namespace ReactiveMarbles.Extensions.Hosting.ReactiveUI;
+#endif
/// Provides extension methods for configuring ReactiveUI with Microsoft dependency injection in .NET host builder scenarios.
/// These extension methods enable seamless integration of ReactiveUI and Splat with the Microsoft
diff --git a/src/Extensions.Hosting.slnx b/src/Extensions.Hosting.slnx
index aecfb10..4a989a3 100644
--- a/src/Extensions.Hosting.slnx
+++ b/src/Extensions.Hosting.slnx
@@ -33,11 +33,17 @@
+
+
+
+
+
+