Skip to content

Commit cdf0459

Browse files
committed
Introduce keyed service resolution for RocksDb stores
1 parent a03c279 commit cdf0459

7 files changed

Lines changed: 106 additions & 7 deletions

File tree

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,15 @@ rocksDbBuilder.AddStore<string, User, UsersStore>("users-store");
7373

7474
This registers an instance of `UsersStore` with RocksDb under the name "users-store".
7575

76+
#### Keyed Service Resolution
77+
You can also resolve your store as a keyed service using the column family name:
78+
79+
```csharp
80+
var usersStore = serviceProvider.GetRequiredKeyedService<UsersStore>("users-store");
81+
```
82+
83+
This approach allows you to register and retrieve multiple stores of the same type, each differentiated by their column family name.
84+
7685
### Use your store
7786

7887
Once you have registered your store, you can use it to add, get, and remove data from RocksDb. For example:

src/RocksDb.Extensions/IRocksDbBuilder.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,18 @@ public interface IRocksDbBuilder
88
/// <summary>
99
/// Adds a RocksDB store to the builder for the specified column family.
1010
/// </summary>
11-
/// <param name="columnFamily"></param>
11+
/// <param name="columnFamily">The name of the column family to associate with the store.</param>
1212
/// <typeparam name="TKey">The type of the store's key.</typeparam>
1313
/// <typeparam name="TValue">The type of the store's value.</typeparam>
1414
/// <typeparam name="TStore">The type of the store to add.</typeparam>
15-
/// <returns>The builder instance for method chaining</returns>
15+
/// <returns>The builder instance for method chaining.</returns>
16+
/// <exception cref="InvalidOperationException">Thrown if the specified column family is already registered.</exception>
1617
/// <remarks>
1718
/// The <typeparamref name="TStore"/> type must be a concrete implementation of the abstract class
18-
/// <see cref="RocksDbStore{TKey,TValue}"/>.
19+
/// <see cref="RocksDbStore{TKey,TValue}"/>. Each store is registered uniquely based on its column family name.
20+
///
21+
/// Stores can also be resolved as keyed services using their associated column family name.
22+
/// Use <c>GetRequiredKeyedService&lt;TStore&gt;(columnFamily)</c> to retrieve a specific store instance.
1923
/// </remarks>
2024
IRocksDbBuilder AddStore<TKey, TValue, TStore>(string columnFamily) where TStore : RocksDbStore<TKey, TValue>;
2125
}

src/RocksDb.Extensions/RocksDb.Extensions.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
<ItemGroup>
2727
<PackageReference Include="CommunityToolkit.HighPerformance" Version="8.2.2" />
28-
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
28+
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
2929
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" />
3030
<PackageReference Include="RocksDB" Version="8.11.3.46984" />
3131
</ItemGroup>

src/RocksDb.Extensions/RocksDbBuilder.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Reflection;
22
using Microsoft.Extensions.DependencyInjection;
3+
using Microsoft.Extensions.DependencyInjection.Extensions;
34
using Microsoft.Extensions.Options;
45

56
namespace RocksDb.Extensions;
@@ -22,8 +23,8 @@ public IRocksDbBuilder AddStore<TKey, TValue, TStore>(string columnFamily) where
2223
}
2324

2425
_ = _serviceCollection.Configure<RocksDbOptions>(options => { options.ColumnFamilies.Add(columnFamily); });
25-
26-
_ = _serviceCollection.AddSingleton(provider =>
26+
27+
_serviceCollection.AddKeyedSingleton<TStore>(columnFamily, (provider, _) =>
2728
{
2829
var rocksDbContext = provider.GetRequiredService<RocksDbContext>();
2930
var columnFamilyHandle = rocksDbContext.Db.GetColumnFamily(columnFamily);
@@ -38,6 +39,9 @@ public IRocksDbBuilder AddStore<TKey, TValue, TStore>(string columnFamily) where
3839
);
3940
return ActivatorUtilities.CreateInstance<TStore>(provider, rocksDbAccessor);
4041
});
42+
43+
_serviceCollection.TryAddSingleton(typeof(TStore), provider => provider.GetRequiredKeyedService<TStore>(columnFamily));
44+
4145
return this;
4246
}
4347

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using NUnit.Framework;
3+
using RocksDb.Extensions.Protobuf;
4+
using RocksDb.Extensions.Tests.Utils;
5+
6+
namespace RocksDb.Extensions.Tests;
7+
8+
public class KeyedStoreTests
9+
{
10+
[Test]
11+
public void should_resolve_rocksdb_stores_as_keyed_services_when_registered_under_different_column_names()
12+
{
13+
// Arrange
14+
using var testFixture = TestFixture.Create(rockDb =>
15+
{
16+
_ = rockDb.AddStore<CacheKey, CacheValue, RocksDbGenericStore<CacheKey, CacheValue>>("my-store-1");
17+
_ = rockDb.AddStore<CacheKey, CacheValue, RocksDbGenericStore<CacheKey, CacheValue>>("my-store-2");
18+
}, options =>
19+
{
20+
options.SerializerFactories.Clear();
21+
options.SerializerFactories.Add(new ProtobufSerializerFactory());
22+
});
23+
24+
// Act
25+
var store1 = testFixture.ServiceProvider.GetRequiredKeyedService<RocksDbGenericStore<CacheKey, CacheValue>>("my-store-1");
26+
var store2 = testFixture.ServiceProvider.GetRequiredKeyedService<RocksDbGenericStore<CacheKey, CacheValue>>("my-store-2");
27+
28+
// Assert
29+
Assert.Multiple(() =>
30+
{
31+
Assert.That(store1, Is.Not.Null);
32+
Assert.That(store2, Is.Not.Null);
33+
Assert.That(ReferenceEquals(store1, store2), Is.False);
34+
});
35+
}
36+
37+
[Test]
38+
public void should_throw_when_resolving_non_existent_store()
39+
{
40+
// Arrange
41+
using var testFixture = TestFixture.Create(rockDb =>
42+
{
43+
_ = rockDb.AddStore<CacheKey, CacheValue, RocksDbGenericStore<CacheKey, CacheValue>>("my-store-1");
44+
}, options =>
45+
{
46+
options.SerializerFactories.Clear();
47+
options.SerializerFactories.Add(new ProtobufSerializerFactory());
48+
});
49+
50+
// Act & Assert
51+
Assert.Throws<InvalidOperationException>(() => testFixture.ServiceProvider.GetRequiredKeyedService<RocksDbGenericStore<CacheKey, CacheValue>>("non-existent-store"));
52+
}
53+
54+
[Test]
55+
public void should_resolve_default_store_as_first_registered_keyed_service()
56+
{
57+
// Arrange
58+
using var testFixture = TestFixture.Create(rockDb =>
59+
{
60+
_ = rockDb.AddStore<CacheKey, CacheValue, RocksDbGenericStore<CacheKey, CacheValue>>("my-store-1");
61+
_ = rockDb.AddStore<CacheKey, CacheValue, RocksDbGenericStore<CacheKey, CacheValue>>("my-store-2");
62+
}, options =>
63+
{
64+
options.SerializerFactories.Clear();
65+
options.SerializerFactories.Add(new ProtobufSerializerFactory());
66+
});
67+
68+
// Act
69+
var store1 = testFixture.ServiceProvider.GetRequiredKeyedService<RocksDbGenericStore<CacheKey, CacheValue>>("my-store-1");
70+
var store2 = testFixture.ServiceProvider.GetRequiredKeyedService<RocksDbGenericStore<CacheKey, CacheValue>>("my-store-2");
71+
var store = testFixture.ServiceProvider.GetRequiredService<RocksDbGenericStore<CacheKey, CacheValue>>();
72+
73+
// Assert
74+
Assert.Multiple(() =>
75+
{
76+
Assert.That(ReferenceEquals(store1, store), Is.True);
77+
Assert.That(ReferenceEquals(store2, store), Is.False);
78+
});
79+
}
80+
}

test/RocksDb.Extensions.Tests/RocksDb.Extensions.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<PrivateAssets>all</PrivateAssets>
1515
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1616
</PackageReference>
17-
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
17+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
1818
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
1919
<PackageReference Include="NScenario" Version="4.3.0" />
2020
<PackageReference Include="NUnit" Version="3.13.3" />

test/RocksDb.Extensions.Tests/Utils/TestFixture.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ public T GetStore<T>() where T : notnull
3232
return _serviceProvider.GetRequiredService<T>();
3333
}
3434

35+
public IServiceProvider ServiceProvider => _serviceProvider;
36+
3537
public void Dispose()
3638
{
3739
_serviceProvider.Dispose();

0 commit comments

Comments
 (0)