Skip to content

Commit b727556

Browse files
committed
feat: add Fluent API support for all Redis implementations
Add fluent API builders for all Redis-backed Bloom Filter implementations: 1. FilterRedisBuilder (StackExchange.Redis): - WithRedisConnection(string/ConfigurationOptions/IConnectionMultiplexer) - WithRedisBitOperate(IRedisBitOperate) - WithRedisKey(string) - BuildRedis() / BuildRedisWithCapacity() 2. FilterCSRedisBuilder (CSRedisCore): - WithRedisClient(CSRedisClient) - WithRedisKey(string) - BuildCSRedis() / BuildCSRedisWithCapacity() 3. FilterFreeRedisBuilder (FreeRedis): - WithRedisClient(RedisClient) - WithRedisKey(string) - BuildFreeRedis() / BuildFreeRedisWithCapacity() 4. FilterEasyCachingBuilder (EasyCaching): - WithRedisCachingProvider(IRedisCachingProvider) - WithRedisKey(string) - BuildEasyCaching() / BuildEasyCachingWithCapacity() All builders inherit from FilterBuilder to support common configuration: - WithName() - Set filter name - ExpectingElements() - Set expected element count - WithErrorRate() - Set false positive rate - UsingHashMethod() / UsingCustomHash() - Configure hash functions - WithSerializer() - Set custom serializer (FilterBuilder only) Changed FilterBuilder base class fields from private to protected to enable inheritance by Redis builders. Example usage: var filter = FilterRedisBuilder.Create() .WithRedisConnection("localhost:6379") .WithRedisKey("bloom:users") .WithName("UserFilter") .ExpectingElements(10_000_000) .WithErrorRate(0.001) .UsingHashMethod(HashMethod.XXHash3) .BuildRedis(); All 239 tests passing ✅
1 parent b61bc2b commit b727556

5 files changed

Lines changed: 519 additions & 8 deletions

File tree

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
using CSRedis;
2+
using System;
3+
4+
namespace BloomFilter.CSRedis;
5+
6+
/// <summary>
7+
/// BloomFilter CSRedis Builder - Supports both static factory methods and fluent API
8+
/// </summary>
9+
/// <example>
10+
/// Fluent API:
11+
/// <code>
12+
/// var filter = FilterCSRedisBuilder.Create()
13+
/// .WithRedisClient(csredisClient)
14+
/// .WithRedisKey("bloom:users")
15+
/// .WithName("UserFilter")
16+
/// .ExpectingElements(10_000_000)
17+
/// .WithErrorRate(0.001)
18+
/// .UsingHashMethod(HashMethod.XXHash3)
19+
/// .BuildCSRedis();
20+
/// </code>
21+
/// </example>
22+
public class FilterCSRedisBuilder : FilterBuilder
23+
{
24+
#region Fluent API - Instance Members
25+
26+
private string? _redisKey;
27+
private CSRedisClient? _client;
28+
29+
/// <summary>
30+
/// Sets the Redis key for the Bloom Filter
31+
/// </summary>
32+
/// <param name="redisKey">The Redis key</param>
33+
/// <returns>The builder for method chaining</returns>
34+
/// <exception cref="ArgumentException">Thrown when redisKey is null or whitespace</exception>
35+
public FilterCSRedisBuilder WithRedisKey(string redisKey)
36+
{
37+
if (string.IsNullOrWhiteSpace(redisKey))
38+
throw new ArgumentException("Redis key cannot be null or whitespace", nameof(redisKey));
39+
_redisKey = redisKey;
40+
return this;
41+
}
42+
43+
/// <summary>
44+
/// Sets the CSRedis client
45+
/// </summary>
46+
/// <param name="client">The CSRedisClient instance</param>
47+
/// <returns>The builder for method chaining</returns>
48+
/// <exception cref="ArgumentNullException">Thrown when client is null</exception>
49+
public FilterCSRedisBuilder WithRedisClient(CSRedisClient client)
50+
{
51+
#if NET6_0_OR_GREATER
52+
ArgumentNullException.ThrowIfNull(client);
53+
#else
54+
if (client == null)
55+
throw new ArgumentNullException(nameof(client));
56+
#endif
57+
_client = client;
58+
return this;
59+
}
60+
61+
/// <summary>
62+
/// Builds a CSRedis-backed Bloom Filter with the configured settings
63+
/// </summary>
64+
/// <returns>A new IBloomFilter instance backed by CSRedis</returns>
65+
/// <exception cref="InvalidOperationException">Thrown when required configuration is missing</exception>
66+
public IBloomFilter BuildCSRedis()
67+
{
68+
if (string.IsNullOrWhiteSpace(_redisKey))
69+
throw new InvalidOperationException("Redis key must be specified using WithRedisKey()");
70+
71+
if (_client == null)
72+
throw new InvalidOperationException("CSRedis client must be specified using WithRedisClient()");
73+
74+
// Get hash function from base class configuration
75+
var hashFunction = _customHashFunction ?? HashFunction.Functions[_method];
76+
77+
return new FilterCSRedis(_name, _client, _redisKey!, _expectedElements, _errorRate, hashFunction);
78+
}
79+
80+
/// <summary>
81+
/// Builds a CSRedis-backed Bloom Filter with explicit capacity and hash count (advanced usage)
82+
/// </summary>
83+
/// <param name="capacity">The bit array capacity</param>
84+
/// <param name="hashes">The number of hash functions</param>
85+
/// <returns>A new IBloomFilter instance backed by CSRedis</returns>
86+
/// <exception cref="InvalidOperationException">Thrown when required configuration is missing</exception>
87+
public IBloomFilter BuildCSRedisWithCapacity(long capacity, int hashes)
88+
{
89+
if (string.IsNullOrWhiteSpace(_redisKey))
90+
throw new InvalidOperationException("Redis key must be specified using WithRedisKey()");
91+
92+
if (_client == null)
93+
throw new InvalidOperationException("CSRedis client must be specified using WithRedisClient()");
94+
95+
// Get hash function from base class configuration
96+
var hashFunction = _customHashFunction ?? HashFunction.Functions[_method];
97+
98+
return new FilterCSRedis(_name, _client, _redisKey!, capacity, hashes, hashFunction);
99+
}
100+
101+
#endregion
102+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
using EasyCaching.Core;
2+
using System;
3+
4+
namespace BloomFilter.EasyCaching;
5+
6+
/// <summary>
7+
/// BloomFilter EasyCaching Builder - Supports both static factory methods and fluent API
8+
/// </summary>
9+
/// <example>
10+
/// Fluent API:
11+
/// <code>
12+
/// var filter = FilterEasyCachingBuilder.Create()
13+
/// .WithRedisCachingProvider(provider)
14+
/// .WithRedisKey("bloom:users")
15+
/// .WithName("UserFilter")
16+
/// .ExpectingElements(10_000_000)
17+
/// .WithErrorRate(0.001)
18+
/// .UsingHashMethod(HashMethod.XXHash3)
19+
/// .BuildEasyCaching();
20+
/// </code>
21+
/// </example>
22+
public class FilterEasyCachingBuilder : FilterBuilder
23+
{
24+
#region Fluent API - Instance Members
25+
26+
private string? _redisKey;
27+
private IRedisCachingProvider? _provider;
28+
29+
/// <summary>
30+
/// Sets the Redis key for the Bloom Filter
31+
/// </summary>
32+
/// <param name="redisKey">The Redis key</param>
33+
/// <returns>The builder for method chaining</returns>
34+
/// <exception cref="ArgumentException">Thrown when redisKey is null or whitespace</exception>
35+
public FilterEasyCachingBuilder WithRedisKey(string redisKey)
36+
{
37+
if (string.IsNullOrWhiteSpace(redisKey))
38+
throw new ArgumentException("Redis key cannot be null or whitespace", nameof(redisKey));
39+
_redisKey = redisKey;
40+
return this;
41+
}
42+
43+
/// <summary>
44+
/// Sets the EasyCaching Redis caching provider
45+
/// </summary>
46+
/// <param name="provider">The IRedisCachingProvider instance</param>
47+
/// <returns>The builder for method chaining</returns>
48+
/// <exception cref="ArgumentNullException">Thrown when provider is null</exception>
49+
public FilterEasyCachingBuilder WithRedisCachingProvider(IRedisCachingProvider provider)
50+
{
51+
#if NET6_0_OR_GREATER
52+
ArgumentNullException.ThrowIfNull(provider);
53+
#else
54+
if (provider == null)
55+
throw new ArgumentNullException(nameof(provider));
56+
#endif
57+
_provider = provider;
58+
return this;
59+
}
60+
61+
/// <summary>
62+
/// Builds an EasyCaching-backed Bloom Filter with the configured settings
63+
/// </summary>
64+
/// <returns>A new IBloomFilter instance backed by EasyCaching</returns>
65+
/// <exception cref="InvalidOperationException">Thrown when required configuration is missing</exception>
66+
public IBloomFilter BuildEasyCaching()
67+
{
68+
if (string.IsNullOrWhiteSpace(_redisKey))
69+
throw new InvalidOperationException("Redis key must be specified using WithRedisKey()");
70+
71+
if (_provider == null)
72+
throw new InvalidOperationException("Redis caching provider must be specified using WithRedisCachingProvider()");
73+
74+
// Get hash function from base class configuration
75+
var hashFunction = _customHashFunction ?? HashFunction.Functions[_method];
76+
77+
return new FilterEasyCachingRedis(_name, _provider, _redisKey!, _expectedElements, _errorRate, hashFunction);
78+
}
79+
80+
/// <summary>
81+
/// Builds an EasyCaching-backed Bloom Filter with explicit capacity and hash count (advanced usage)
82+
/// </summary>
83+
/// <param name="capacity">The bit array capacity</param>
84+
/// <param name="hashes">The number of hash functions</param>
85+
/// <returns>A new IBloomFilter instance backed by EasyCaching</returns>
86+
/// <exception cref="InvalidOperationException">Thrown when required configuration is missing</exception>
87+
public IBloomFilter BuildEasyCachingWithCapacity(long capacity, int hashes)
88+
{
89+
if (string.IsNullOrWhiteSpace(_redisKey))
90+
throw new InvalidOperationException("Redis key must be specified using WithRedisKey()");
91+
92+
if (_provider == null)
93+
throw new InvalidOperationException("Redis caching provider must be specified using WithRedisCachingProvider()");
94+
95+
// Get hash function from base class configuration
96+
var hashFunction = _customHashFunction ?? HashFunction.Functions[_method];
97+
98+
return new FilterEasyCachingRedis(_name, _provider, _redisKey!, capacity, hashes, hashFunction);
99+
}
100+
101+
#endregion
102+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
using FreeRedis;
2+
using System;
3+
4+
namespace BloomFilter.FreeRedis;
5+
6+
/// <summary>
7+
/// BloomFilter FreeRedis Builder - Supports both static factory methods and fluent API
8+
/// </summary>
9+
/// <example>
10+
/// Fluent API:
11+
/// <code>
12+
/// var filter = FilterFreeRedisBuilder.Create()
13+
/// .WithRedisClient(redisClient)
14+
/// .WithRedisKey("bloom:users")
15+
/// .WithName("UserFilter")
16+
/// .ExpectingElements(10_000_000)
17+
/// .WithErrorRate(0.001)
18+
/// .UsingHashMethod(HashMethod.XXHash3)
19+
/// .BuildFreeRedis();
20+
/// </code>
21+
/// </example>
22+
public class FilterFreeRedisBuilder : FilterBuilder
23+
{
24+
#region Fluent API - Instance Members
25+
26+
private string? _redisKey;
27+
private RedisClient? _client;
28+
29+
/// <summary>
30+
/// Sets the Redis key for the Bloom Filter
31+
/// </summary>
32+
/// <param name="redisKey">The Redis key</param>
33+
/// <returns>The builder for method chaining</returns>
34+
/// <exception cref="ArgumentException">Thrown when redisKey is null or whitespace</exception>
35+
public FilterFreeRedisBuilder WithRedisKey(string redisKey)
36+
{
37+
if (string.IsNullOrWhiteSpace(redisKey))
38+
throw new ArgumentException("Redis key cannot be null or whitespace", nameof(redisKey));
39+
_redisKey = redisKey;
40+
return this;
41+
}
42+
43+
/// <summary>
44+
/// Sets the FreeRedis client
45+
/// </summary>
46+
/// <param name="client">The RedisClient instance</param>
47+
/// <returns>The builder for method chaining</returns>
48+
/// <exception cref="ArgumentNullException">Thrown when client is null</exception>
49+
public FilterFreeRedisBuilder WithRedisClient(RedisClient client)
50+
{
51+
#if NET6_0_OR_GREATER
52+
ArgumentNullException.ThrowIfNull(client);
53+
#else
54+
if (client == null)
55+
throw new ArgumentNullException(nameof(client));
56+
#endif
57+
_client = client;
58+
return this;
59+
}
60+
61+
/// <summary>
62+
/// Builds a FreeRedis-backed Bloom Filter with the configured settings
63+
/// </summary>
64+
/// <returns>A new IBloomFilter instance backed by FreeRedis</returns>
65+
/// <exception cref="InvalidOperationException">Thrown when required configuration is missing</exception>
66+
public IBloomFilter BuildFreeRedis()
67+
{
68+
if (string.IsNullOrWhiteSpace(_redisKey))
69+
throw new InvalidOperationException("Redis key must be specified using WithRedisKey()");
70+
71+
if (_client == null)
72+
throw new InvalidOperationException("FreeRedis client must be specified using WithRedisClient()");
73+
74+
// Get hash function from base class configuration
75+
var hashFunction = _customHashFunction ?? HashFunction.Functions[_method];
76+
77+
return new FilterFreeRedis(_name, _client, _redisKey!, _expectedElements, _errorRate, hashFunction);
78+
}
79+
80+
/// <summary>
81+
/// Builds a FreeRedis-backed Bloom Filter with explicit capacity and hash count (advanced usage)
82+
/// </summary>
83+
/// <param name="capacity">The bit array capacity</param>
84+
/// <param name="hashes">The number of hash functions</param>
85+
/// <returns>A new IBloomFilter instance backed by FreeRedis</returns>
86+
/// <exception cref="InvalidOperationException">Thrown when required configuration is missing</exception>
87+
public IBloomFilter BuildFreeRedisWithCapacity(long capacity, int hashes)
88+
{
89+
if (string.IsNullOrWhiteSpace(_redisKey))
90+
throw new InvalidOperationException("Redis key must be specified using WithRedisKey()");
91+
92+
if (_client == null)
93+
throw new InvalidOperationException("FreeRedis client must be specified using WithRedisClient()");
94+
95+
// Get hash function from base class configuration
96+
var hashFunction = _customHashFunction ?? HashFunction.Functions[_method];
97+
98+
return new FilterFreeRedis(_name, _client, _redisKey!, capacity, hashes, hashFunction);
99+
}
100+
101+
#endregion
102+
}

0 commit comments

Comments
 (0)