Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<PackageVersion Include="ReactiveUI.Maui" Version="24.0.0-beta.3" />
<PackageVersion Include="ReactiveUI.Primitives" Version="6.0.0" />
<PackageVersion Include="ReactiveUI.Primitives.Core" Version="6.0.0" />
<PackageVersion Include="ReactiveUI.Primitives.Reactive" Version="6.0.0" />
<PackageVersion Include="ReactiveUI.WinForms" Version="24.0.0-beta.3" />
<PackageVersion Include="ReactiveUI.WinUI" Version="24.0.0-beta.3" />
<PackageVersion Include="ReactiveUI.WPF" Version="24.0.0-beta.3" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Microsoft.Extensions.DependencyInjection;
using ReactiveMarbles.Extensions.Hosting.UiThread;

Expand Down
4 changes: 4 additions & 0 deletions src/Extensions.Hosting.MainUIThread/BaseUiThread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ protected BaseUiThread(IServiceProvider serviceProvider, bool useDedicatedUiThre
IsBackground = true
};

#if NET5_0_OR_GREATER
if (OperatingSystem.IsWindows())
#else
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
#endif
{
// Set the apartment state for Windows desktop UI frameworks.
newUiThread.SetApartmentState(ApartmentState.STA);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net462;net472;net48;net481;net8.0;net9.0;net10.0;net11.0</TargetFrameworks>
<PackageDescription>This extension adds reactive plug-in support to generic host based applications.</PackageDescription>
<PackageId>CP.Extensions.Hosting.Plugins.Reactive</PackageId>
<DefineConstants>$(DefineConstants);REACTIVE_SHIM</DefineConstants>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\Extensions.Hosting.Plugins\**\*.cs" Exclude="..\Extensions.Hosting.Plugins\bin\**;..\Extensions.Hosting.Plugins\obj\**" Link="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="ReactiveUI.Primitives.Reactive" />
</ItemGroup>

<ItemGroup>
<Using Include="ReactiveUI.Primitives.Disposables.MultipleDisposable" Alias="CompositeDisposable" />
</ItemGroup>

<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Extensions.Hosting.Tests</_Parameter1>
</AssemblyAttribute>
</ItemGroup>

</Project>
10 changes: 9 additions & 1 deletion src/Extensions.Hosting.Plugins/HostBuilderPluginExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,17 @@
using System.Reflection;
using Microsoft.Extensions.FileSystemGlobbing;
using Microsoft.Extensions.Hosting;
#if REACTIVE_SHIM
using ReactiveMarbles.Extensions.Hosting.Reactive.Plugins.Internals;
#else
using ReactiveMarbles.Extensions.Hosting.Plugins.Internals;
#endif

#if REACTIVE_SHIM
namespace ReactiveMarbles.Extensions.Hosting.Reactive.Plugins;
#else
namespace ReactiveMarbles.Extensions.Hosting.Plugins;
#endif

/// <summary>Provides extension methods for configuring and loading plugins in host builder pipelines.</summary>
/// <remarks>These extension methods enable plugin discovery and configuration for applications using IHostBuilder
Expand Down Expand Up @@ -200,7 +208,7 @@ private static void LoadFrameworkAssemblies(IPluginBuilder pluginBuilder, HashSe
continue;
}

var loadedAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(frameworkAssemblyPath);
var loadedAssembly = AssemblyLoadContext.LoadFromAssemblyPath(frameworkAssemblyPath);
_ = scannedAssemblies.Add(loadedAssembly);
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/Extensions.Hosting.Plugins/HostedServiceBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

#if REACTIVE_SHIM
namespace ReactiveMarbles.Extensions.Hosting.Reactive.Plugins;
#else
namespace ReactiveMarbles.Extensions.Hosting.Plugins;
#endif

/// <summary>Provides a base implementation for a hosted service with support for resource cleanup, logging, and application lifetime events.</summary>
/// <remarks>This base class integrates with the application's lifetime events to manage service startup,
Expand Down
4 changes: 4 additions & 0 deletions src/Extensions.Hosting.Plugins/IPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

using Microsoft.Extensions.DependencyInjection;

#if REACTIVE_SHIM
namespace ReactiveMarbles.Extensions.Hosting.Reactive.Plugins;
#else
namespace ReactiveMarbles.Extensions.Hosting.Plugins;
#endif

/// <summary>Defines a contract for plug-ins that can configure services and settings for the application host during startup.</summary>
/// <remarks>Implementations of this interface can be used to extend or modify the application's dependency
Expand Down
4 changes: 4 additions & 0 deletions src/Extensions.Hosting.Plugins/IPluginBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
using System.Reflection;
using Microsoft.Extensions.FileSystemGlobbing;

#if REACTIVE_SHIM
namespace ReactiveMarbles.Extensions.Hosting.Reactive.Plugins;
#else
namespace ReactiveMarbles.Extensions.Hosting.Plugins;
#endif

/// <summary>Defines a contract for configuring and building plugin discovery and loading behavior within an application.</summary>
/// <remarks>Implementations of this interface allow customization of plugin and framework assembly scanning,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
using System.IO;
using System.Reflection;

#if REACTIVE_SHIM
namespace ReactiveMarbles.Extensions.Hosting.Reactive.Plugins.Internals;
#else
namespace ReactiveMarbles.Extensions.Hosting.Plugins.Internals;
#endif

/// <summary>Provides methods for resolving the paths of managed assemblies and unmanaged DLLs relative to a specified plugin directory.</summary>
/// <param name="pluginPath">The absolute path to the root directory containing the plugin and its dependencies. Cannot be null or empty.</param>
Expand Down
38 changes: 21 additions & 17 deletions src/Extensions.Hosting.Plugins/Internals/AssemblyLoadContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
using System.Collections.Generic;
using System.Reflection;

#if REACTIVE_SHIM
namespace ReactiveMarbles.Extensions.Hosting.Reactive.Plugins.Internals;
#else
namespace ReactiveMarbles.Extensions.Hosting.Plugins.Internals;
#endif

/// <summary>Provides functionality for loading and managing assemblies in a custom context, enabling isolation and control over assembly loading behavior.</summary>
/// <remarks>AssemblyLoadContext allows applications to load assemblies into isolated contexts, which can be
Expand All @@ -24,11 +28,19 @@ public class AssemblyLoadContext(string name)
/// this property to access the standard assembly loading behavior provided by .NET.</remarks>
public static AssemblyLoadContext Default { get; } = new AssemblyLoadContext("default");

/// <summary>Gets the assemblies that are loaded into the current application domain.</summary>
public static IEnumerable<Assembly> Assemblies => AppDomain.CurrentDomain.GetAssemblies();

/// <summary>Gets the name associated with the current instance.</summary>
public string Name { get; } = name;

/// <summary>Gets the assemblies that are loaded into the current application domain.</summary>
public IEnumerable<Assembly> Assemblies => AppDomain.CurrentDomain.GetAssemblies();
/// <summary>Loads an assembly from the specified file path.</summary>
/// <remarks>The assembly is loaded into the load-from context. If the assembly has already been loaded,
/// this method may return a reference to the existing assembly. This method does not resolve dependencies
/// automatically; dependent assemblies must be available to the loader.</remarks>
/// <param name="assemblyPath">The path to the assembly file to load. The path must be a valid file system path to a managed assembly file.</param>
/// <returns>The loaded assembly represented by the specified file path.</returns>
public static Assembly LoadFromAssemblyPath(string assemblyPath) => Assembly.LoadFrom(assemblyPath);

/// <summary>Loads an assembly given its display name.</summary>
/// <remarks>This method loads the assembly into the current load context. If the assembly has already
Expand All @@ -47,27 +59,19 @@ public Assembly LoadFromAssemblyName(AssemblyName assemblyName)
return Load(assemblyName);
}

/// <summary>Loads an assembly from the specified file path.</summary>
/// <remarks>The assembly is loaded into the load-from context. If the assembly has already been loaded,
/// this method may return a reference to the existing assembly. This method does not resolve dependencies
/// automatically; dependent assemblies must be available to the loader.</remarks>
/// <param name="assemblyPath">The path to the assembly file to load. The path must be a valid file system path to a managed assembly file.</param>
/// <returns>The loaded assembly represented by the specified file path.</returns>
public Assembly LoadFromAssemblyPath(string assemblyPath) => Assembly.LoadFrom(assemblyPath);

/// <summary>Loads the assembly with the specified name.</summary>
/// <remarks>Override this method to implement custom assembly loading logic in a derived class.</remarks>
/// <param name="assemblyName">The name of the assembly to load. Cannot be null.</param>
/// <returns>The loaded assembly, or null if the assembly cannot be found.</returns>
protected virtual Assembly Load(AssemblyName assemblyName) => null!;

/// <summary>Loads an unmanaged dynamic-link library (DLL) from the specified absolute path.</summary>
/// <remarks>This method is intended to be called by derived classes to provide custom logic for loading
/// unmanaged libraries. The caller is responsible for ensuring that the specified path points to a valid and
/// compatible DLL.</remarks>
/// <param name="dllPath">The absolute path to the unmanaged DLL to load. Cannot be null or empty.</param>
/// <returns>A handle to the loaded unmanaged DLL. Returns <see cref="IntPtr.Zero"/> if the library could not be loaded.</returns>
protected IntPtr LoadUnmanagedDllFromPath(string dllPath) => IntPtr.Zero;
protected static IntPtr LoadUnmanagedDllFromPath(string dllPath) => IntPtr.Zero;

/// <summary>Loads the assembly with the specified name.</summary>
/// <remarks>Override this method to implement custom assembly loading logic in a derived class.</remarks>
/// <param name="assemblyName">The name of the assembly to load. Cannot be null.</param>
/// <returns>The loaded assembly, or null if the assembly cannot be found.</returns>
protected virtual Assembly Load(AssemblyName assemblyName) => null!;

/// <summary>Loads the specified unmanaged DLL into the process address space.</summary>
/// <remarks>Override this method to provide custom logic for loading unmanaged libraries when resolving
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

using System.Reflection;

#if REACTIVE_SHIM
namespace ReactiveMarbles.Extensions.Hosting.Reactive.Plugins.Internals;
#else
namespace ReactiveMarbles.Extensions.Hosting.Plugins.Internals;
#endif

/// <summary>Provides extension methods for the AssemblyLoadContext class.</summary>
/// <remarks>This static class contains methods that extend the functionality of AssemblyLoadContext, enabling
Expand Down Expand Up @@ -32,7 +36,7 @@ public bool TryGetAssembly(AssemblyName assemblyName, out Assembly? foundAssembl
return false;
}

foreach (var assembly in assemblyLoadContext.Assemblies)
foreach (var assembly in AssemblyLoadContext.Assemblies)
{
var name = assembly.GetName().Name;
if (name is null)
Expand Down
4 changes: 4 additions & 0 deletions src/Extensions.Hosting.Plugins/Internals/PluginBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
using System.Reflection;
using Microsoft.Extensions.FileSystemGlobbing;

#if REACTIVE_SHIM
namespace ReactiveMarbles.Extensions.Hosting.Reactive.Plugins.Internals;
#else
namespace ReactiveMarbles.Extensions.Hosting.Plugins.Internals;
#endif

/// <summary>Provides configuration and matching logic for plugin discovery and loading within the application.</summary>
/// <remarks>The PluginBuilder class exposes properties and delegates that allow customization of plugin scanning,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
using System;
using System.Reflection;

#if REACTIVE_SHIM
namespace ReactiveMarbles.Extensions.Hosting.Reactive.Plugins.Internals;
#else
namespace ReactiveMarbles.Extensions.Hosting.Plugins.Internals;
#endif

/// <summary>Provides an isolated assembly load context for loading plugins from a specified directory.</summary>
/// <remarks>PluginLoadContext enables loading and resolving assemblies and unmanaged libraries for plugins
Expand Down Expand Up @@ -45,6 +49,6 @@ protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
private Assembly? LoadAssemblyFromResolvedPath(AssemblyName assemblyName)
{
var assemblyPath = ResolveAssemblyPath(assemblyName);
return assemblyPath is null ? null : LoadFromAssemblyPath(assemblyPath);
return assemblyPath is null ? null : AssemblyLoadContext.LoadFromAssemblyPath(assemblyPath);
}
}
4 changes: 4 additions & 0 deletions src/Extensions.Hosting.Plugins/PluginBase{T1,T2,T3}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

#if REACTIVE_SHIM
namespace ReactiveMarbles.Extensions.Hosting.Reactive.Plugins;
#else
namespace ReactiveMarbles.Extensions.Hosting.Plugins;
#endif

/// <summary>Provides a base implementation for plugins that register three hosted services with the application host.</summary>
/// <remarks>This class is intended to be used as a base for plugins that require multiple hosted services to be
Expand Down
4 changes: 4 additions & 0 deletions src/Extensions.Hosting.Plugins/PluginBase{T1,T2}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

#if REACTIVE_SHIM
namespace ReactiveMarbles.Extensions.Hosting.Reactive.Plugins;
#else
namespace ReactiveMarbles.Extensions.Hosting.Plugins;
#endif

/// <summary>Provides a base implementation for plugins that register two hosted services with a dependency injection container.</summary>
/// <remarks>This class is intended to be used as a base for plugins that require multiple hosted services to be
Expand Down
4 changes: 4 additions & 0 deletions src/Extensions.Hosting.Plugins/PluginBase{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

#if REACTIVE_SHIM
namespace ReactiveMarbles.Extensions.Hosting.Reactive.Plugins;
#else
namespace ReactiveMarbles.Extensions.Hosting.Plugins;
#endif

/// <summary>Provides a base implementation for plugins that register a hosted service of the specified type with the application's dependency injection container.</summary>
/// <typeparam name="T">The type of the hosted service to register. Must implement <see cref="IHostedService"/>.</typeparam>
Expand Down
4 changes: 4 additions & 0 deletions src/Extensions.Hosting.Plugins/PluginBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
using System;
using System.IO;

#if REACTIVE_SHIM
namespace ReactiveMarbles.Extensions.Hosting.Reactive.Plugins;
#else
namespace ReactiveMarbles.Extensions.Hosting.Plugins;
#endif

/// <summary>Provides extension methods for configuring plug-in and framework assembly scanning behavior on an <see cref="IPluginBuilder"/> instance.</summary>
/// <remarks>These extension methods allow customization of directory scanning, inclusion and exclusion patterns
Expand Down
4 changes: 4 additions & 0 deletions src/Extensions.Hosting.Plugins/PluginOrderAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

using System;

#if REACTIVE_SHIM
namespace ReactiveMarbles.Extensions.Hosting.Reactive.Plugins;
#else
namespace ReactiveMarbles.Extensions.Hosting.Plugins;
#endif

/// <summary>Specifies the initialization order for a plug-in class.</summary>
/// <remarks>Apply this attribute to a plug-in class to control the sequence in which plug-ins are initialized.
Expand Down
4 changes: 4 additions & 0 deletions src/Extensions.Hosting.Plugins/PluginScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
using System.Collections.Generic;
using System.Reflection;

#if REACTIVE_SHIM
namespace ReactiveMarbles.Extensions.Hosting.Reactive.Plugins;
#else
namespace ReactiveMarbles.Extensions.Hosting.Plugins;
#endif

/// <summary>Provides methods for discovering and instantiating plugin implementations from assemblies using naming conventions or type scanning.</summary>
/// <remarks>The PluginScanner class is intended for use in scenarios where plugins implementing the IPlugin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@
<PackageReference Include="Microsoft.Extensions.Hosting" />
</ItemGroup>

<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Extensions.Hosting.Tests</_Parameter1>
</AssemblyAttribute>
</ItemGroup>

</Project>
Loading