This is an automated email from the ASF dual-hosted git repository.
ptupitsyn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new b6be0ce886 IGNITE-23651 .NET: Add ServiceCollection extensions for DI
integration (#4811)
b6be0ce886 is described below
commit b6be0ce886608830d20c38e5beae3e0d05f56bac
Author: gurustron <[email protected]>
AuthorDate: Wed Dec 4 18:50:42 2024 +0300
IGNITE-23651 .NET: Add ServiceCollection extensions for DI integration
(#4811)
Add ServiceCollection extensions for DI integration with IgniteClientGroup.
---------
Co-authored-by: gurustron <[email protected]>
---
.../IgniteServiceCollectionExtensionsTests.cs | 239 +++++++++++++++++++++
.../dotnet/Apache.Ignite/Apache.Ignite.csproj | 1 +
.../IgniteServiceCollectionExtensions.cs | 148 +++++++++++++
modules/platforms/dotnet/DEVNOTES.md | 2 +-
4 files changed, 389 insertions(+), 1 deletion(-)
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteServiceCollectionExtensionsTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteServiceCollectionExtensionsTests.cs
new file mode 100644
index 0000000000..ecdd56e26a
--- /dev/null
+++
b/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteServiceCollectionExtensionsTests.cs
@@ -0,0 +1,239 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace Apache.Ignite.Tests;
+
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using NUnit.Framework;
+
+/// <summary>
+/// Tests for <see cref="IgniteServiceCollectionExtensionsTests"/>.
+/// </summary>
+public class IgniteServiceCollectionExtensionsTests
+{
+ private FakeServer _server;
+
+ [SetUp]
+ public void StartServer() => _server = new FakeServer
+ {
+ AllowMultipleConnections = true
+ };
+
+ [TearDown]
+ public void StopServer() => _server.Dispose();
+
+ [Test]
+ public async Task TestRegisterSingleClient()
+ {
+ await ValidateRegisterSingleClient(
+ services => services.AddIgniteClientGroup(CreateGroupConfig()),
+ services => services.GetService<IgniteClientGroup>());
+
+ await ValidateRegisterSingleClient(
+ services => services.AddIgniteClientGroup(_ =>
CreateGroupConfig()),
+ services => services.GetService<IgniteClientGroup>());
+
+ const string serviceKey = "key";
+
+ await ValidateRegisterSingleClient(
+ services => services.AddIgniteClientGroupKeyed(serviceKey,
CreateGroupConfig()),
+ services =>
services.GetKeyedService<IgniteClientGroup>(serviceKey));
+
+ await ValidateRegisterSingleClient(
+ services => services.AddIgniteClientGroupKeyed(serviceKey, _ =>
CreateGroupConfig()),
+ services =>
services.GetKeyedService<IgniteClientGroup>(serviceKey));
+
+ await ValidateRegisterSingleClient(
+ services => services.AddIgniteClientGroupKeyed(serviceKey, (_, _)
=> CreateGroupConfig()),
+ services =>
services.GetKeyedService<IgniteClientGroup>(serviceKey));
+ }
+
+ [Test]
+ public async Task TestRegisterConfigurationInstanceRoundRobin()
+ {
+ const int count = 3;
+
+ await ValidateRegisterRoundRobin(
+ count,
+ services =>
services.AddIgniteClientGroup(CreateGroupConfig(count)),
+ services => services.GetService<IgniteClientGroup>());
+
+ await ValidateRegisterRoundRobin(
+ count,
+ services => services.AddIgniteClientGroup(_ =>
CreateGroupConfig(count)),
+ services => services.GetService<IgniteClientGroup>());
+
+ const string? serviceKey = "key";
+
+ await ValidateRegisterRoundRobin(
+ count,
+ services => services.AddIgniteClientGroupKeyed(serviceKey,
CreateGroupConfig(count)),
+ services =>
services.GetKeyedService<IgniteClientGroup>(serviceKey));
+
+ await ValidateRegisterRoundRobin(
+ count,
+ services => services.AddIgniteClientGroupKeyed(serviceKey, _ =>
CreateGroupConfig(count)),
+ services =>
services.GetKeyedService<IgniteClientGroup>(serviceKey));
+
+ await ValidateRegisterRoundRobin(
+ count,
+ services => services.AddIgniteClientGroupKeyed(serviceKey, (_, _)
=> CreateGroupConfig(count)),
+ services =>
services.GetKeyedService<IgniteClientGroup>(serviceKey));
+ }
+
+ [Test]
+ public void TestRegisterScopesConfiguration([Values]ServiceLifetime
lifetime)
+ {
+ var services = new ServiceCollection();
+
+ var resServices = services.AddIgniteClientGroup(CreateGroupConfig(),
lifetime);
+ Assert.AreSame(services, resServices);
+
+ var servicesDescriptors = services
+ .Where(sd => sd.ServiceType == typeof(IgniteClientGroup))
+ .ToList();
+
+ Assert.AreEqual(1, servicesDescriptors.Count);
+ Assert.AreEqual(lifetime, servicesDescriptors.First().Lifetime);
+ }
+
+ [Test]
+ public void TestRegisterScopesConfigurationFunc([Values]ServiceLifetime
lifetime)
+ {
+ var services = new ServiceCollection();
+
+ var resServices = services.AddIgniteClientGroup(_ =>
CreateGroupConfig(), lifetime);
+ Assert.AreSame(services, resServices);
+
+ var servicesDescriptors = services
+ .Where(sd => sd.ServiceType == typeof(IgniteClientGroup))
+ .ToList();
+
+ Assert.AreEqual(1, servicesDescriptors.Count);
+ Assert.AreEqual(lifetime, servicesDescriptors.First().Lifetime);
+ }
+
+ [Test]
+ public void TestRegisterScopesConfigurationKeyed([Values] ServiceLifetime
lifetime)
+ {
+ ValidateKeyedRegistrationScope(
+ lifetime,
+ (s, key) => s.AddIgniteClientGroupKeyed(key, CreateGroupConfig(),
lifetime));
+ }
+
+ [Test]
+ public void TestRegisterScopesConfigurationFuncKeyed([Values]
ServiceLifetime lifetime)
+ {
+ ValidateKeyedRegistrationScope(
+ lifetime,
+ (s, key) => s.AddIgniteClientGroupKeyed(key, _ =>
CreateGroupConfig(), lifetime));
+ }
+
+ [Test]
+ public void TestRegisterScopesConfigurationFuncWithKeyKeyed([Values]
ServiceLifetime lifetime)
+ {
+ ValidateKeyedRegistrationScope(
+ lifetime,
+ (s, key) => s.AddIgniteClientGroupKeyed(key, (_, _) =>
CreateGroupConfig(), lifetime));
+ }
+
+ private static async Task
ValidateRegisterSingleClient(Action<ServiceCollection> register,
Func<IServiceProvider, IgniteClientGroup?> resolve)
+ {
+ var services = new ServiceCollection();
+
+ register(services);
+
+ var serviceProvider = services.BuildServiceProvider();
+
+ var group = resolve(serviceProvider);
+ var group2 = resolve(serviceProvider);
+
+ Assert.That(group, Is.Not.Null);
+ Assert.That(group2, Is.Not.Null);
+ Assert.AreSame(group, group2);
+
+ IIgnite client = await group.GetIgniteAsync();
+ IIgnite client2 = await group.GetIgniteAsync();
+ IIgnite client3 = await group2.GetIgniteAsync();
+
+ Assert.That(client, Is.Not.Null);
+ Assert.AreSame(client, client2);
+ Assert.AreSame(client, client2);
+ Assert.AreSame(client, client3);
+
+ await client.Tables.GetTablesAsync();
+ }
+
+ private static async Task ValidateRegisterRoundRobin(
+ int count,
+ Action<ServiceCollection> register,
+ Func<IServiceProvider, IgniteClientGroup?> resolve)
+ {
+ var services = new ServiceCollection();
+
+ register(services);
+
+ var serviceProvider = services.BuildServiceProvider();
+
+ var group = resolve(serviceProvider);
+
+ Assert.That(group, Is.Not.Null);
+
+ var clients = await Task.WhenAll(
+ Enumerable.Range(0, count + 1).Select(async _ => await
group.GetIgniteAsync()));
+
+ var uniqueClients = clients.Distinct().ToArray();
+
+ Assert.That(uniqueClients.Length, Is.EqualTo(count));
+
+ await clients.First().Tables.GetTablesAsync();
+ }
+
+ private static void ValidateKeyedRegistrationScope(ServiceLifetime
lifetime, Action<ServiceCollection, object> register)
+ {
+ var services = new ServiceCollection();
+ var keys = new[] { "key1", "key2" };
+
+ foreach (var key in keys)
+ {
+ register(services, key);
+ }
+
+ var servicesDescriptors = services
+ .Where(sd => sd.ServiceType == typeof(IgniteClientGroup))
+ .ToList();
+
+ var actualKeys = servicesDescriptors.Select(s =>
s.ServiceKey).ToArray();
+
+ Assert.AreEqual(keys.Length, servicesDescriptors.Count);
+ foreach (var key in keys)
+ {
+ CollectionAssert.Contains(actualKeys, key);
+ }
+
+ Assert.AreEqual(1, servicesDescriptors.Select(sd =>
sd.Lifetime).Distinct().Count());
+ Assert.AreEqual(lifetime, servicesDescriptors.First().Lifetime);
+ }
+
+ private IgniteClientGroupConfiguration CreateGroupConfig(int size = 1) =>
+ new()
+ {
+ Size = size,
+ ClientConfiguration = new
IgniteClientConfiguration(_server.Endpoint)
+ };
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Apache.Ignite.csproj
b/modules/platforms/dotnet/Apache.Ignite/Apache.Ignite.csproj
index 8df6c0517d..5837e8dae6 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Apache.Ignite.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite/Apache.Ignite.csproj
@@ -38,6 +38,7 @@
<PackageReference Include="NodaTime" Version="[3.*,)" />
<PackageReference Include="Remotion.Linq" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions"
Version="[6.*,)" />
+ <PackageReference
Include="Microsoft.Extensions.DependencyInjection.Abstractions"
Version="[8.*,)" />
</ItemGroup>
<ItemGroup>
diff --git
a/modules/platforms/dotnet/Apache.Ignite/IgniteServiceCollectionExtensions.cs
b/modules/platforms/dotnet/Apache.Ignite/IgniteServiceCollectionExtensions.cs
new file mode 100644
index 0000000000..ef95a77bc0
--- /dev/null
+++
b/modules/platforms/dotnet/Apache.Ignite/IgniteServiceCollectionExtensions.cs
@@ -0,0 +1,148 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace Apache.Ignite;
+
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+
+/// <summary>
+/// Extension methods for setting up Apache Ignite services
+/// in an <see
cref="Microsoft.Extensions.DependencyInjection.IServiceCollection" />.
+/// </summary>
+public static class IgniteServiceCollectionExtensions
+{
+ /// <summary>
+ /// Registers an <see cref="IgniteClientGroup" />.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection" /> to add
services to.</param>
+ /// <param name="configuration">
+ /// <see cref="IgniteClientGroupConfiguration" /> instance.
+ /// </param>
+ /// <param name="clientGroupLifetime">
+ /// The lifetime with which to register the <see cref="IgniteClientGroup"
/> service in the container.
+ /// Defaults to <see cref="ServiceLifetime.Singleton" />.
+ /// </param>
+ /// <returns>Original service collection to chain multiple calls.</returns>
+ public static IServiceCollection AddIgniteClientGroup(
+ this IServiceCollection services,
+ IgniteClientGroupConfiguration configuration,
+ ServiceLifetime clientGroupLifetime = ServiceLifetime.Singleton) =>
+ AddIgniteClientGroupCore(services, (_, _) => configuration,
clientGroupLifetime);
+
+ /// <summary>
+ /// Registers an <see cref="IgniteClientGroup" />.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection" /> to add
services to.</param>
+ /// <param name="configure">
+ /// Function to build the <see cref="IgniteClientGroupConfiguration" />.
+ /// </param>
+ /// <param name="clientGroupLifetime">
+ /// The lifetime with which to register the <see cref="IgniteClientGroup"
/> service in the container.
+ /// Defaults to <see cref="ServiceLifetime.Singleton" />.
+ /// </param>
+ /// <returns>Original service collection to chain multiple calls.</returns>
+ public static IServiceCollection AddIgniteClientGroup(
+ this IServiceCollection services,
+ Func<IServiceProvider, IgniteClientGroupConfiguration> configure,
+ ServiceLifetime clientGroupLifetime = ServiceLifetime.Singleton) =>
+ AddIgniteClientGroupCore(services, (sp, _) => configure(sp),
clientGroupLifetime);
+
+ /// <summary>
+ /// Registers an <see cref="IgniteClientGroup" />.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection" /> to add
services to.</param>
+ /// <param name="serviceKey">
+ /// The <see cref="ServiceDescriptor.ServiceKey"/> of the client group.
+ /// </param>
+ /// <param name="configuration">
+ /// <see cref="IgniteClientGroupConfiguration" /> instance.
+ /// </param>
+ /// <param name="clientGroupLifetime">
+ /// The lifetime with which to register the <see cref="IgniteClientGroup"
/> service in the container.
+ /// Defaults to <see cref="ServiceLifetime.Singleton" />.
+ /// </param>
+ /// <returns>Original service collection to chain multiple calls.</returns>
+ public static IServiceCollection AddIgniteClientGroupKeyed(
+ this IServiceCollection services,
+ object? serviceKey,
+ IgniteClientGroupConfiguration configuration,
+ ServiceLifetime clientGroupLifetime = ServiceLifetime.Singleton) =>
+ AddIgniteClientGroupCore(services, (_, _) => configuration,
clientGroupLifetime, serviceKey);
+
+ /// <summary>
+ /// Registers an <see cref="IgniteClientGroup" />.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection" /> to add
services to.</param>
+ /// <param name="serviceKey">
+ /// The <see cref="ServiceDescriptor.ServiceKey"/> of the client group.
+ /// </param>
+ /// <param name="configure">
+ /// Function to build the <see cref="IgniteClientGroupConfiguration" />.
+ /// </param>
+ /// <param name="clientGroupLifetime">
+ /// The lifetime with which to register the <see cref="IgniteClientGroup"
/> service in the container.
+ /// Defaults to <see cref="ServiceLifetime.Singleton" />.
+ /// </param>
+ /// <returns>Original service collection to chain multiple calls.</returns>
+ public static IServiceCollection AddIgniteClientGroupKeyed(
+ this IServiceCollection services,
+ object? serviceKey,
+ Func<IServiceProvider, IgniteClientGroupConfiguration> configure,
+ ServiceLifetime clientGroupLifetime = ServiceLifetime.Singleton) =>
+ AddIgniteClientGroupCore(
+ services,
+ (sp, _) => configure(sp),
+ clientGroupLifetime,
+ serviceKey);
+
+ /// <summary>
+ /// Registers an <see cref="IgniteClientGroup" />.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection" /> to add
services to.</param>
+ /// <param name="serviceKey">
+ /// The <see cref="ServiceDescriptor.ServiceKey"/> of the client group.
+ /// </param>
+ /// <param name="configure">
+ /// Function to build the <see cref="IgniteClientGroupConfiguration" />.
+ /// </param>
+ /// <param name="clientGroupLifetime">
+ /// The lifetime with which to register the <see cref="IgniteClientGroup"
/> service in the container.
+ /// Defaults to <see cref="ServiceLifetime.Singleton" />.
+ /// </param>
+ /// <returns>Original service collection to chain multiple calls.</returns>
+ public static IServiceCollection AddIgniteClientGroupKeyed(
+ this IServiceCollection services,
+ object? serviceKey,
+ Func<IServiceProvider, object?, IgniteClientGroupConfiguration>
configure,
+ ServiceLifetime clientGroupLifetime = ServiceLifetime.Singleton) =>
+ AddIgniteClientGroupCore(services, configure, clientGroupLifetime,
serviceKey);
+
+ private static IServiceCollection AddIgniteClientGroupCore(
+ IServiceCollection services,
+ Func<IServiceProvider, object?, IgniteClientGroupConfiguration>
configure,
+ ServiceLifetime clientGroupLifetime = ServiceLifetime.Singleton,
+ object? key = null)
+ {
+ services.TryAdd(new ServiceDescriptor(
+ typeof(IgniteClientGroup),
+ key,
+ (sp, innerKey) => new IgniteClientGroup(configure(sp, innerKey)),
+ clientGroupLifetime));
+
+ return services;
+ }
+}
diff --git a/modules/platforms/dotnet/DEVNOTES.md
b/modules/platforms/dotnet/DEVNOTES.md
index bbc5e07864..36ce144a54 100644
--- a/modules/platforms/dotnet/DEVNOTES.md
+++ b/modules/platforms/dotnet/DEVNOTES.md
@@ -1,5 +1,5 @@
## Prerequisites
-* .NET 6 SDK
+* .NET 8 SDK
* Java 11 SDK
## Build Java