Skip to content

Commit 5e9d7cd

Browse files
committed
Add advanced channel configuration options and update docs
Expanded the channel configuration options in the dependency injection library, allowing more control over channel settings. Bounded and unbounded channels can now be created and configured with various parameters. Updated documentation in README.md to include examples of advanced usage of the channels. Introduced a new ChannelSettings class for handling these configurations. Also, refactored some parts of service collection extension methods for better code organization and efficiency.
1 parent d201fdb commit 5e9d7cd

File tree

5 files changed

+148
-25
lines changed

5 files changed

+148
-25
lines changed

Frank.Channels.DependencyInjection/ChannelFactory.cs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,37 @@
33

44
namespace Frank.Channels.DependencyInjection;
55

6-
public class ChannelFactory : IChannelFactory
6+
internal class ChannelFactory : IChannelFactory
77
{
88
private readonly ConcurrentDictionary<string, object> _cache = new();
99

10-
public Channel<T> CreateChannel<T>() where T : class => _cache.GetOrAdd(typeof(T).Name, Value<T>) as Channel<T> ?? throw new InvalidOperationException($"Channel<{typeof(T).Name}> not found");
10+
/// <inheritdoc />
11+
public Channel<T> CreateUnboundedChannel<T>(ChannelSettings? options = null) where T : class => _cache.GetOrAdd(typeof(T).Name, name => CreateUnbounded<T>(name, options ?? new ChannelSettings())) as Channel<T> ?? throw new InvalidOperationException($"Channel<{typeof(T).Name}> not found");
1112

12-
private static Channel<T> Value<T>(string arg) where T : class =>
13+
/// <inheritdoc />
14+
public Channel<T> CreateBoundedChannel<T>(ChannelSettings? options = null) where T : class => _cache.GetOrAdd(typeof(T).Name, name => CreateBounded<T>(name, options ?? new ChannelSettings())) as Channel<T> ?? throw new InvalidOperationException($"Channel<{typeof(T).Name}> not found");
15+
16+
private static Channel<T> CreateBounded<T>(string name, ChannelSettings options) where T : class =>
17+
Channel.CreateBounded<T>(new BoundedChannelOptions(options.BoundedCapacity)
18+
{
19+
FullMode = options.BoundedFullMode
20+
});
21+
22+
private static Channel<T> CreateUnbounded<T>(string arg, ChannelSettings options) where T : class =>
1323
Channel.CreateUnbounded<T>(new UnboundedChannelOptions()
1424
{
15-
SingleReader = true,
16-
SingleWriter = true
25+
SingleReader = options.SingleReader,
26+
SingleWriter = options.SingleWriter
1727
});
1828
}
29+
30+
public class ChannelSettings
31+
{
32+
public bool SingleReader { get; set; } = true;
33+
34+
public bool SingleWriter { get; set; } = true;
35+
36+
public int BoundedCapacity { get; set; } = 100;
37+
38+
public BoundedChannelFullMode BoundedFullMode { get; set; } = BoundedChannelFullMode.Wait;
39+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace Frank.Channels.DependencyInjection;
2+
3+
public enum ChannelType
4+
{
5+
Unbounded,
6+
Bounded
7+
}

Frank.Channels.DependencyInjection/IChannelFactory.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
namespace Frank.Channels.DependencyInjection;
44

5-
public interface IChannelFactory
5+
internal interface IChannelFactory
66
{
7-
Channel<T> CreateChannel<T>() where T : class;
7+
Channel<T> CreateUnboundedChannel<T>(ChannelSettings? options = null) where T : class;
8+
9+
Channel<T> CreateBoundedChannel<T>(ChannelSettings? options = null) where T : class;
810
}

Frank.Channels.DependencyInjection/ServiceCollectionExtensions.cs

Lines changed: 91 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,8 @@ namespace Frank.Channels.DependencyInjection;
55

66
public static class ServiceCollectionExtensions
77
{
8-
internal static bool Contains<TService>(this IServiceCollection services)
9-
{
10-
return services.Any(descriptor => descriptor.ServiceType == typeof(TService));
11-
}
12-
13-
internal static IServiceCollection AddSingletonIfNotExists<TService, TImplementation>(this IServiceCollection services) where TService : class where TImplementation : class, TService
14-
{
15-
if (!services.Contains<TService>())
16-
services.AddSingleton<TService, TImplementation>();
17-
return services;
18-
}
19-
208
/// <summary>
21-
/// Adds a channel of type <typeparamref name="T"/> to the service collection.
9+
/// Adds an unbounded channel of type <typeparamref name="T"/> to the service collection, with default settings.
2210
/// </summary>
2311
/// <remarks>
2412
/// The channel is added as a singleton with its reader and writer as singletons, and injected as follows:
@@ -31,12 +19,97 @@ internal static IServiceCollection AddSingletonIfNotExists<TService, TImplementa
3119
/// <param name="services"></param>
3220
/// <typeparam name="T"></typeparam>
3321
/// <returns></returns>
34-
public static IServiceCollection AddChannel<T>(this IServiceCollection services) where T : class
22+
public static IServiceCollection AddChannel<T>(this IServiceCollection services) where T : class =>
23+
services.AddChannel<T>(ChannelType.Unbounded, new ChannelSettings());
24+
25+
/// <summary>
26+
/// Adds a channel of type <typeparamref name="T"/> to the IServiceCollection.
27+
/// </summary>
28+
/// <typeparam name="T">The type of the channel.</typeparam>
29+
/// <param name="services">The IServiceCollection to add the channel to.</param>
30+
/// <param name="channelType">The type of channel to add.</param>
31+
/// <returns>The same instance of the IServiceCollection after the channel has been added.</returns>
32+
/// <remarks>
33+
/// This method adds a channel of type <typeparamref name="T"/> to the IServiceCollection.
34+
/// It allows specifying the channel type (unbounded or bounded) and optional settings for the channel.
35+
/// After the channel is added, it will be available for injection as a singleton instance of Channel{T}.
36+
/// The respective ChannelReader{T} and ChannelWriter{T} instances are also added as singletons.
37+
/// </remarks>
38+
public static IServiceCollection AddChannel<T>(this IServiceCollection services, ChannelType channelType) where T : class =>
39+
services.AddChannel<T>(channelType, new ChannelSettings());
40+
41+
/// <summary>
42+
/// Adds an unbounded channel of type <typeparamref name="T"/> to the IServiceCollection.
43+
/// </summary>
44+
/// <typeparam name="T">The type of the channel.</typeparam>
45+
/// <param name="services">The IServiceCollection to add the channel to.</param>
46+
/// <returns>The same instance of the IServiceCollection after the channel has been added.</returns>
47+
public static IServiceCollection AddUnboundedChannel<T>(this IServiceCollection services) where T : class =>
48+
services.AddChannel<T>(ChannelType.Unbounded, new ChannelSettings());
49+
50+
/// <summary>
51+
/// Adds an unbounded channel of type <typeparamref name="T"/> to the IServiceCollection.
52+
/// </summary>
53+
/// <typeparam name="T">The type of the channel.</typeparam>
54+
/// <param name="services">The IServiceCollection to add the channel to.</param>
55+
/// <returns>The same instance of the IServiceCollection after the channel has been added.</returns>
56+
public static IServiceCollection AddUnboundedChannel<T>(this IServiceCollection services, ChannelSettings settings) where T : class =>
57+
services.AddChannel<T>(ChannelType.Unbounded, settings);
58+
59+
/// <summary>
60+
/// Adds a bounded channel of type <typeparamref name="T"/> to the IServiceCollection.
61+
/// </summary>
62+
/// <typeparam name="T">The type of the channel.</typeparam>
63+
/// <param name="services">The IServiceCollection to add the channel to.</param>
64+
/// <returns>The same instance of the IServiceCollection after the channel has been added.</returns>
65+
public static IServiceCollection AddBoundedChannel<T>(this IServiceCollection services) where T : class =>
66+
services.AddChannel<T>(ChannelType.Bounded, new ChannelSettings());
67+
68+
/// <summary>
69+
/// Adds a bounded channel of type <typeparamref name="T"/> to the IServiceCollection.
70+
/// </summary>
71+
/// <typeparam name="T">The type of the channel.</typeparam>
72+
/// <param name="services">The IServiceCollection to add the channel to.</param>
73+
/// <param name="settings">The settings for the channel.</param>
74+
/// <returns>The same instance of the IServiceCollection after the channel has been added.</returns>
75+
public static IServiceCollection AddBoundedChannel<T>(this IServiceCollection services, ChannelSettings settings) where T : class =>
76+
services.AddChannel<T>(ChannelType.Bounded, settings);
77+
78+
/// <summary>
79+
/// Adds a channel of type <typeparamref name="T"/> to the IServiceCollection.
80+
/// </summary>
81+
/// <typeparam name="T">The type of the channel.</typeparam>
82+
/// <param name="services">The IServiceCollection to add the channel to.</param>
83+
/// <param name="channelType">The type of channel to add.</param>
84+
/// <param name="settings">The settings for the channel.</param>
85+
/// <returns>The same instance of the IServiceCollection after the channel has been added.</returns>
86+
public static IServiceCollection AddChannel<T>(this IServiceCollection services, ChannelType channelType, ChannelSettings settings) where T : class =>
87+
services
88+
.ThrowIfContains<Channel<T>>()
89+
.AddSingletonIfNotExists<IChannelFactory, ChannelFactory>()
90+
.AddSingleton<Channel<T>>(provider => channelType switch
91+
{
92+
ChannelType.Unbounded => provider.GetRequiredService<IChannelFactory>().CreateUnboundedChannel<T>(settings),
93+
ChannelType.Bounded => provider.GetRequiredService<IChannelFactory>().CreateBoundedChannel<T>(settings),
94+
_ => throw new ArgumentOutOfRangeException(nameof(channelType), channelType, null)
95+
})
96+
.AddSingleton<ChannelReader<T>>(provider => provider.GetRequiredService<Channel<T>>().Reader)
97+
.AddSingleton<ChannelWriter<T>>(provider => provider.GetRequiredService<Channel<T>>().Writer);
98+
99+
private static IServiceCollection AddSingletonIfNotExists<TService, TImplementation>(this IServiceCollection services) where TService : class where TImplementation : class, TService
35100
{
36-
services.AddSingletonIfNotExists<IChannelFactory, ChannelFactory>();
37-
services.AddSingleton<Channel<T>>(provider => provider.GetRequiredService<IChannelFactory>().CreateChannel<T>());
38-
services.AddSingleton<ChannelReader<T>>(provider => provider.GetRequiredService<Channel<T>>().Reader);
39-
services.AddSingleton<ChannelWriter<T>>(provider => provider.GetRequiredService<Channel<T>>().Writer);
101+
if (!services.Contains<TService>())
102+
services.AddSingleton<TService, TImplementation>();
40103
return services;
41104
}
105+
106+
private static IServiceCollection ThrowIfContains<TService>(this IServiceCollection services) where TService : class
107+
{
108+
if (services.Contains<TService>())
109+
throw new InvalidOperationException($"Service of type {typeof(TService).Name} already exists in the service collection.");
110+
return services;
111+
}
112+
113+
private static bool Contains<TService>(this IServiceCollection services)
114+
=> services.Any(descriptor => descriptor.ServiceType == typeof(TService));
42115
}

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,26 @@ using Frank.Channels.DependencyInjection;
2323
// Register the channel of a type as a dependency:
2424
services.AddChannel<string>();
2525

26+
// Use the channel as a dependency in various ways:
27+
var channel = provider.GetRequiredService<Channel<string>>();
28+
var channelWriter = provider.GetRequiredService<ChannelWriter<string>>();
29+
var channelReader = provider.GetRequiredService<ChannelReader<string>>();
30+
```
31+
32+
## Advanced usage
33+
34+
```csharp
35+
using Frank.Channels.DependencyInjection;
36+
37+
// Register the channel of a type as a dependency with a custom configuration:
38+
services.AddChannel<string>(options =>
39+
{
40+
options.BoundedCapacity = 100;
41+
options.FullMode = BoundedChannelFullMode.Wait;
42+
options.SingleReader = true;
43+
options.SingleWriter = true;
44+
});
45+
2646
// Use the channel as a dependency in various ways:
2747
var channel = provider.GetRequiredService<Channel<string>>();
2848
var channelWriter = provider.GetRequiredService<ChannelWriter<string>>();

0 commit comments

Comments
 (0)