Skip to content

Commit 49a1bbf

Browse files
authored
Enhance DI integration and update dependencies (#57)
* Enhance DI integration and update dependencies Improves and documents extension methods for integrating ReactiveUI with various DI containers (Autofac, DryIoc, Microsoft DI, Ninject) in Avalonia applications. Updates test mocks to match expanded IDependencyResolver interface. Refactors and documents AppBuilderExtensions and related classes for clarity and extensibility. Updates dependencies: Avalonia to 11.3.11, Splat to 19.2.1, and ReactiveUI to 23.1.0-beta.1. * Update test setup and builder to use WithSuspensionHost Refactored AutoSuspendHelperTests setup to use ReactiveUIBuilder and WithSuspensionHost for improved test isolation. Updated AppBuilderExtensions to chain WithSuspensionHost<Unit>() in the builder configuration. * Refactor method signatures for readability Method signatures in AvaloniaCreatesCommandBinding.cs have been reformatted to use multi-line parameter lists for improved readability. The BindCommandToObject overload with custom event handlers now returns Disposable.Empty instead of throwing NotImplementedException. * Fix XML doc comment formatting in ReactiveWindow Corrected the summary comment in the ReactiveWindow constructor to fix a line break and improve readability. * Fix XML doc comment formatting in RoutedViewHost Corrected a line break in the XML documentation comment for the RoutedViewHost constructor to improve readability and maintain consistency. * Fix XML doc comment punctuation in constructor summary Removed an unnecessary period at the end of the first sentence in the XML documentation for the ViewModelViewHost constructor to improve consistency and style.
1 parent 6d2d879 commit 49a1bbf

26 files changed

Lines changed: 824 additions & 465 deletions

src/Directory.Packages.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
55
<AvaloniaVersion>11.3.11</AvaloniaVersion>
66
<AvaloniaLatestVersion>11.3.*</AvaloniaLatestVersion>
7-
<SplatVersion>18.1.1</SplatVersion>
7+
<SplatVersion>19.2.1</SplatVersion>
88
</PropertyGroup>
99
<ItemGroup>
1010
<PackageVersion Include="coverlet.collector" Version="6.0.4">
@@ -19,7 +19,7 @@
1919
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
2020
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
2121
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.9.50" />
22-
<PackageVersion Include="ReactiveUI" Version="22.3.1" />
22+
<PackageVersion Include="ReactiveUI" Version="23.1.0-beta.1" />
2323
<PackageVersion Include="Roslynator.Analyzers" Version="4.15.0" />
2424
<PackageVersion Include="Splat" Version="$(SplatVersion)" />
2525
<PackageVersion Include="Splat.Autofac" Version="$(SplatVersion)" />

src/ReactiveUI.Avalonia.Autofac/AvaloniaMixins.cs

Lines changed: 16 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5,73 +5,34 @@
55

66
using Autofac;
77
using ReactiveUI.Builder;
8-
using Splat;
98
using Splat.Autofac;
109
using Splat.Builder;
1110
using AppBuilder = Avalonia.AppBuilder;
1211

1312
namespace ReactiveUI.Avalonia.Splat;
1413

1514
/// <summary>
16-
/// Avalonia Mixins.
15+
/// Provides extension methods for configuring Avalonia applications to use ReactiveUI with Autofac as the dependency
16+
/// injection container.
1717
/// </summary>
18+
/// <remarks>This class contains extension methods that integrate Autofac with ReactiveUI in Avalonia
19+
/// applications. These methods are intended to be used during application startup to set up dependency resolution and
20+
/// enable further customization of the application's composition and ReactiveUI configuration.</remarks>
1821
public static class AvaloniaMixins
1922
{
2023
/// <summary>
21-
/// Uses the splat with dry ioc.
24+
/// Configures the application to use ReactiveUI with Autofac as the dependency injection container.
2225
/// </summary>
23-
/// <param name="builder">The builder.</param>
24-
/// <param name="containerConfig">The configure.</param>
25-
/// <param name="withResolver">The get resolver.</param>
26-
/// <returns>
27-
/// An App Builder.
28-
/// </returns>
29-
/// <exception cref="System.ArgumentNullException">builder.</exception>
30-
public static AppBuilder UseReactiveUIWithAutofac(this AppBuilder builder, Action<ContainerBuilder> containerConfig, Action<AutofacDependencyResolver>? withResolver = null) =>
31-
builder switch
32-
{
33-
null => throw new ArgumentNullException(nameof(builder)),
34-
_ => builder.UseReactiveUI().AfterPlatformServicesSetup(_ =>
35-
{
36-
if (AppLocator.CurrentMutable is null)
37-
{
38-
return;
39-
}
40-
#if NETSTANDARD
41-
if (containerConfig is null)
42-
{
43-
throw new ArgumentNullException(nameof(containerConfig));
44-
}
45-
#else
46-
ArgumentNullException.ThrowIfNull(containerConfig);
47-
#endif
48-
var containerBuilder = new ContainerBuilder();
49-
var autofacResolver = containerBuilder.UseAutofacDependencyResolver();
50-
containerBuilder.RegisterInstance(autofacResolver);
51-
autofacResolver.InitializeReactiveUI(RegistrationNamespace.Avalonia);
52-
RxSchedulers.MainThreadScheduler = AvaloniaScheduler.Instance;
53-
containerConfig(containerBuilder);
54-
var container = containerBuilder.Build();
55-
autofacResolver.SetLifetimeScope(container);
56-
57-
if (withResolver is not null)
58-
{
59-
withResolver(autofacResolver);
60-
}
61-
})
62-
};
63-
64-
/// <summary>
65-
/// Uses the reactive UI with autofac.
66-
/// </summary>
67-
/// <param name="builder">The builder.</param>
68-
/// <param name="containerConfig">The container configuration.</param>
69-
/// <param name="withResolver">The with resolver.</param>
70-
/// <param name="withReactiveUIBuilder">The with reactive UI builder.</param>
71-
/// <returns>
72-
/// An App Builder.
73-
/// </returns>
74-
/// <exception cref="ArgumentNullException">builder.</exception>
26+
/// <remarks>This method integrates Autofac with ReactiveUI by registering the Autofac dependency resolver
27+
/// and allowing custom container configuration. Additional customization of the resolver and ReactiveUI builder can
28+
/// be performed using the optional delegates. This extension should be called during application startup before
29+
/// registering views and view models.</remarks>
30+
/// <param name="builder">The application builder used to configure the app. Cannot be null.</param>
31+
/// <param name="containerConfig">A delegate that configures the Autofac container by registering services and components. Cannot be null.</param>
32+
/// <param name="withResolver">An optional delegate that allows further customization of the Autofac dependency resolver after it is created.</param>
33+
/// <param name="withReactiveUIBuilder">An optional delegate that allows additional configuration of the ReactiveUI builder.</param>
34+
/// <returns>The application builder instance, enabling further configuration or chaining.</returns>
35+
/// <exception cref="ArgumentNullException">Thrown if <paramref name="builder"/> or <paramref name="containerConfig"/> is null.</exception>
7536
public static AppBuilder UseReactiveUIWithAutofac(this AppBuilder builder, Action<ContainerBuilder> containerConfig, Action<AutofacDependencyResolver>? withResolver = null, Action<ReactiveUIBuilder>? withReactiveUIBuilder = null)
7637
{
7738
if (builder is null)

src/ReactiveUI.Avalonia.DryIoc/AvaloniaMixins.cs

Lines changed: 13 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,52 +13,24 @@
1313
namespace ReactiveUI.Avalonia.Splat;
1414

1515
/// <summary>
16-
/// Avalonia Mixins.
16+
/// Provides extension methods for configuring Avalonia applications to use ReactiveUI with DryIoc as the dependency
17+
/// injection container.
1718
/// </summary>
1819
public static class AvaloniaMixins
1920
{
2021
/// <summary>
21-
/// Uses the splat with dry ioc.
22+
/// Configures the application to use ReactiveUI with DryIoc as the dependency injection container.
2223
/// </summary>
23-
/// <param name="builder">The builder.</param>
24-
/// <param name="containerConfig">The configure.</param>
25-
/// <returns>An App Builder.</returns>
26-
public static AppBuilder UseReactiveUIWithDryIoc(this AppBuilder builder, Action<Container> containerConfig) =>
27-
builder switch
28-
{
29-
null => throw new ArgumentNullException(nameof(builder)),
30-
_ => builder.UseReactiveUI().AfterPlatformServicesSetup(_ =>
31-
{
32-
if (AppLocator.CurrentMutable is null)
33-
{
34-
return;
35-
}
36-
37-
#if NETSTANDARD
38-
if (containerConfig is null)
39-
{
40-
throw new ArgumentNullException(nameof(containerConfig));
41-
}
42-
#else
43-
ArgumentNullException.ThrowIfNull(containerConfig);
44-
#endif
45-
46-
var container = new Container();
47-
container.UseDryIocDependencyResolver();
48-
AppLocator.CurrentMutable.RegisterConstant(container);
49-
RxSchedulers.MainThreadScheduler = AvaloniaScheduler.Instance;
50-
containerConfig(container);
51-
})
52-
};
53-
54-
/// <summary>
55-
/// Uses the reactive UI with dry ioc.
56-
/// </summary>
57-
/// <param name="builder">The builder.</param>
58-
/// <param name="containerConfig">The container configuration.</param>
59-
/// <param name="withReactiveUIBuilder">The with reactive UI builder.</param>
60-
/// <returns>An App Builder.</returns>
61-
/// <exception cref="ArgumentNullException">builder.</exception>
24+
/// <remarks>This method integrates DryIoc with ReactiveUI, allowing services and dependencies to be
25+
/// registered using DryIoc. The provided <paramref name="containerConfig"/> delegate can be used to register
26+
/// application-specific services. If additional ReactiveUI configuration is required, supply the <paramref
27+
/// name="withReactiveUIBuilder"/> delegate.</remarks>
28+
/// <param name="builder">The application builder used to configure the app pipeline. Cannot be null.</param>
29+
/// <param name="containerConfig">A delegate that configures the DryIoc container. This is called after the container is created and registered.</param>
30+
/// <param name="withReactiveUIBuilder">An optional delegate to further configure the ReactiveUI builder. If provided, it is invoked after DryIoc
31+
/// integration.</param>
32+
/// <returns>The application builder instance, configured to use ReactiveUI with DryIoc.</returns>
33+
/// <exception cref="ArgumentNullException">Thrown if <paramref name="builder"/> or <paramref name="containerConfig"/> is null.</exception>
6234
public static AppBuilder UseReactiveUIWithDryIoc(this AppBuilder builder, Action<Container> containerConfig, Action<ReactiveUIBuilder>? withReactiveUIBuilder = null) =>
6335
builder switch
6436
{

src/ReactiveUI.Avalonia.DryIoc1.Tests/AvaloniaMixinsDryIocMoreTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ public void UseReactiveUIWithDIContainer_Generic_Returns_Builder()
3131
builder,
3232
containerFactory: () => new Container(),
3333
containerConfig: _ => { },
34-
dependencyResolverFactory: c => new DryIocDependencyResolver(c));
34+
dependencyResolverFactory: c => new DryIocDependencyResolver(c),
35+
_ => { });
3536

3637
Assert.That(result, Is.SameAs(builder));
3738
}

src/ReactiveUI.Avalonia.DryIoc1.Tests/AvaloniaMixinsDryIocTests.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ public void UseReactiveUIWithDIContainer_ThrowsOnNullBuilder()
3333
builder!,
3434
() => new Container(),
3535
_ => { },
36-
c => new DryIocDependencyResolver(c)));
36+
c => new DryIocDependencyResolver(c),
37+
_ => { }));
3738
}
3839

3940
[Test]
@@ -45,7 +46,8 @@ public void UseReactiveUIWithDIContainer_ReturnsBuilder_NoThrow()
4546
var result = builder.UseReactiveUIWithDIContainer(
4647
containerFactory: () => container,
4748
containerConfig: _ => { },
48-
dependencyResolverFactory: c => new DryIocDependencyResolver(c));
49+
dependencyResolverFactory: c => new DryIocDependencyResolver(c),
50+
_ => { });
4951

5052
Assert.That(result, Is.SameAs(builder));
5153
}
@@ -56,11 +58,15 @@ public void DryIocDependencyResolver_Register_And_Resolve_WithAndWithoutContract
5658
var container = new Container();
5759
var resolver = new DryIocDependencyResolver(container);
5860

59-
resolver.Register(() => "a", typeof(string));
60-
resolver.Register(() => "b", typeof(string), "x");
61+
resolver.Register<string>(() => "a");
62+
resolver.Register<string>(() => "b");
63+
resolver.Register<string>(() => "c", "x");
6164

62-
var last = resolver.GetService(typeof(string));
63-
Assert.That(last, Is.EqualTo("b"));
65+
var noContract = resolver.GetService(typeof(string));
66+
Assert.That(noContract, Is.EqualTo("b"));
67+
68+
var withContract = resolver.GetService(typeof(string), "x");
69+
Assert.That(withContract, Is.EqualTo("c"));
6470

6571
var all = resolver.GetServices(typeof(string)).ToArray();
6672
Assert.That(all, Does.Contain("a"));

src/ReactiveUI.Avalonia.Microsoft.Extensions.DependencyInjection/AvaloniaMixins.cs

Lines changed: 21 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -13,63 +13,32 @@
1313
namespace ReactiveUI.Avalonia.Splat;
1414

1515
/// <summary>
16-
/// Avalonia Mixins.
16+
/// Provides extension methods for integrating ReactiveUI with Avalonia applications using the Microsoft dependency
17+
/// resolver.
1718
/// </summary>
19+
/// <remarks>This static class contains mixin methods that enable the use of ReactiveUI in Avalonia applications
20+
/// with dependency injection support via Microsoft's IServiceCollection and IServiceProvider. These methods facilitate
21+
/// the configuration of services and the setup of the dependency resolver, streamlining the integration process for
22+
/// applications that leverage both Avalonia and ReactiveUI.</remarks>
1823
public static class AvaloniaMixins
1924
{
2025
/// <summary>
21-
/// Uses the splat with microsoft dependency resolver.
26+
/// Configures the application to use ReactiveUI with the Microsoft dependency injection system, allowing services
27+
/// to be registered and resolved via IServiceCollection and IServiceProvider.
2228
/// </summary>
23-
/// <param name="builder">The builder.</param>
24-
/// <param name="containerConfig">The configure.</param>
25-
/// <param name="withResolver">The get service provider.</param>
26-
/// <returns>An App Builder.</returns>
27-
public static AppBuilder UseReactiveUIWithMicrosoftDependencyResolver(this AppBuilder builder, Action<IServiceCollection> containerConfig, Action<IServiceProvider?>? withResolver = null) =>
28-
builder switch
29-
{
30-
null => throw new ArgumentNullException(nameof(builder)),
31-
_ => builder.UseReactiveUI().AfterPlatformServicesSetup(_ =>
32-
{
33-
if (AppLocator.CurrentMutable is null)
34-
{
35-
return;
36-
}
37-
38-
#if NETSTANDARD
39-
if (containerConfig is null)
40-
{
41-
throw new ArgumentNullException(nameof(containerConfig));
42-
}
43-
#else
44-
ArgumentNullException.ThrowIfNull(containerConfig);
45-
#endif
46-
47-
IServiceCollection serviceCollection = new ServiceCollection();
48-
serviceCollection.UseMicrosoftDependencyResolver();
49-
AppLocator.CurrentMutable.RegisterConstant(serviceCollection);
50-
RxSchedulers.MainThreadScheduler = AvaloniaScheduler.Instance;
51-
containerConfig(serviceCollection);
52-
var serviceProvider = serviceCollection.BuildServiceProvider();
53-
serviceProvider.UseMicrosoftDependencyResolver();
54-
55-
if (withResolver is not null)
56-
{
57-
withResolver(serviceProvider);
58-
}
59-
})
60-
};
61-
62-
/// <summary>
63-
/// Uses the splat with microsoft dependency resolver.
64-
/// </summary>
65-
/// <param name="builder">The builder.</param>
66-
/// <param name="containerConfig">The configure.</param>
67-
/// <param name="withResolver">The get service provider.</param>
68-
/// <param name="withReactiveUIBuilder">The with reactive UI builder.</param>
69-
/// <returns>
70-
/// An App Builder.
71-
/// </returns>
72-
/// <exception cref="ArgumentNullException">builder.</exception>
29+
/// <remarks>This method integrates ReactiveUI with Microsoft's dependency injection by registering
30+
/// services in an IServiceCollection and building an IServiceProvider. It is typically used during application
31+
/// startup to enable service resolution throughout the app. The optional delegates allow for advanced customization
32+
/// of both the dependency resolver and ReactiveUI configuration.</remarks>
33+
/// <param name="builder">The application builder used to configure the app. Cannot be null.</param>
34+
/// <param name="containerConfig">A delegate that configures the IServiceCollection for dependency injection. This is used to register services
35+
/// required by the application. Cannot be null.</param>
36+
/// <param name="withResolver">An optional delegate invoked with the IServiceProvider after it has been built, allowing additional
37+
/// configuration or initialization using the resolved services.</param>
38+
/// <param name="withReactiveUIBuilder">An optional delegate invoked with the ReactiveUIBuilder to allow further customization of ReactiveUI
39+
/// configuration.</param>
40+
/// <returns>The application builder instance, configured to use ReactiveUI with Microsoft dependency injection.</returns>
41+
/// <exception cref="ArgumentNullException">Thrown if builder or containerConfig is null.</exception>
7342
public static AppBuilder UseReactiveUIWithMicrosoftDependencyResolver(this AppBuilder builder, Action<IServiceCollection> containerConfig, Action<IServiceProvider?>? withResolver = null, Action<ReactiveUIBuilder>? withReactiveUIBuilder = null) =>
7443
builder switch
7544
{

0 commit comments

Comments
 (0)