Skip to content

Commit 7ef5461

Browse files
committed
- restructure RemoteServices dependency injection extension methods
- add http client collection to service channel registry for more granular di registration checks
1 parent f987325 commit 7ef5461

File tree

5 files changed

+126
-116
lines changed

5 files changed

+126
-116
lines changed

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -366,8 +366,9 @@ builder.Services.AddRemoteServicesWithNewClient(
366366
{
367367
client.BaseAddress = new Uri(baseAddress);
368368
client.Timeout = TimeSpan.FromSeconds(5);
369-
}).AddTransientHttpErrorPolicy(
370-
policyBuilder => policyBuilder.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
369+
},
370+
clientBuilder => clientBuilder.AddTransientHttpErrorPolicy(
371+
policyBuilder => policyBuilder.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30))));
371372
```
372373

373374
or alternatively, register remote services one by one...
@@ -379,9 +380,9 @@ builder.Services.AddRemoteServiceWithNewClient<ListStoresRequest>(clientName,
379380
{
380381
client.BaseAddress = new Uri(baseAddress);
381382
client.Timeout = TimeSpan.FromSeconds(5);
382-
})
383-
.AddTransientHttpErrorPolicy(
384-
policyBuilder => policyBuilder.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
383+
},
384+
clientBuilder => clientBuilder.AddTransientHttpErrorPolicy(
385+
policyBuilder => policyBuilder.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30))));
385386
builder.Services.AddRemoteServiceToExistingClient<GetStoreByIdRequest>(clientName);
386387
builder.Services.AddRemoteServiceToExistingClient<DeleteStoreRequest>(clientName);
387388
builder.Services.AddRemoteServiceToExistingClient<CreateStoreRequest>(clientName);

samples/Client/Program.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
// {
1717
// client.BaseAddress = new Uri(baseAddress);
1818
// client.Timeout = TimeSpan.FromSeconds(5);
19-
// })
20-
// .AddTransientHttpErrorPolicy(
21-
// policyBuilder => policyBuilder.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
19+
// },
20+
// clientBuilder => clientBuilder.AddTransientHttpErrorPolicy(
21+
// policyBuilder => policyBuilder.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30))));
2222
//builder.Services.AddRemoteServiceToExistingClient<GetStoreByIdRequest>(clientName);
2323
//builder.Services.AddRemoteServiceToExistingClient<DeleteStoreRequest>(clientName);
2424
//builder.Services.AddRemoteServiceToExistingClient<CreateStoreRequest>(clientName);
@@ -30,8 +30,9 @@
3030
{
3131
client.BaseAddress = new Uri(baseAddress);
3232
client.Timeout = TimeSpan.FromSeconds(5);
33-
}).AddTransientHttpErrorPolicy(
34-
policyBuilder => policyBuilder.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
33+
},
34+
clientBuilder => clientBuilder.AddTransientHttpErrorPolicy(
35+
policyBuilder => policyBuilder.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30))));
3536

3637
using IHost host = builder.Build();
3738

src/ModEndpoints.RemoteServices/DependencyInjectionExtensions.cs

Lines changed: 93 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -6,155 +6,141 @@
66
namespace ModEndpoints.RemoteServices;
77
public static class DependencyInjectionExtensions
88
{
9-
public static IHttpClientBuilder AddRemoteServiceWithNewClient<TRequest>(
9+
public static IServiceCollection AddRemoteServiceWithNewClient<TRequest>(
1010
this IServiceCollection services,
11-
Action<IServiceProvider, HttpClient> configureClient)
11+
string baseAddress,
12+
TimeSpan timeout = default,
13+
Action<IHttpClientBuilder>? configureClientBuilder = null)
1214
where TRequest : IServiceRequestMarker
1315
{
1416
var clientName = DefaultClientName.Resolve<TRequest>();
15-
return services.AddRemoteServiceWithNewClient<TRequest>(clientName, configureClient);
17+
return services.AddRemoteServiceWithNewClient<TRequest>(
18+
clientName,
19+
baseAddress,
20+
timeout,
21+
configureClientBuilder);
1622
}
1723

18-
public static IHttpClientBuilder AddRemoteServiceWithNewClient<TRequest>(
24+
public static IServiceCollection AddRemoteServiceWithNewClient<TRequest>(
1925
this IServiceCollection services,
2026
string clientName,
21-
Action<IServiceProvider, HttpClient> configureClient)
27+
string baseAddress,
28+
TimeSpan timeout = default,
29+
Action<IHttpClientBuilder>? configureClientBuilder = null)
2230
where TRequest : IServiceRequestMarker
2331
{
24-
if (ServiceChannelRegistry.Instance.IsRegistered<TRequest>())
25-
{
26-
throw new InvalidOperationException($"A channel for request type {typeof(TRequest)} is already registered.");
27-
}
28-
if (ServiceChannelRegistry.Instance.IsRegistered(clientName))
29-
{
30-
throw new InvalidOperationException($"A channel with client name {clientName} is already registered.");
31-
}
32-
33-
services.AddRemoteServicesCore();
34-
35-
if (!ServiceChannelRegistry.Instance.Register<TRequest>(clientName))
32+
Action<IServiceProvider, HttpClient> configureClient = (sp, client) =>
3633
{
37-
throw new InvalidOperationException($"Channel couldn't be registered for request type {typeof(TRequest)} and client name {clientName}.");
38-
}
39-
var clientBuilder = services.AddHttpClient(clientName);
40-
clientBuilder.ConfigureHttpClient(configureClient);
41-
return clientBuilder;
34+
client.BaseAddress = new Uri(baseAddress);
35+
if (timeout != default)
36+
{
37+
client.Timeout = timeout;
38+
}
39+
};
40+
return services.AddRemoteServiceWithNewClient<TRequest>(
41+
clientName,
42+
configureClient,
43+
configureClientBuilder);
4244
}
4345

44-
public static IHttpClientBuilder AddRemoteServiceWithNewClient<TRequest>(
46+
public static IServiceCollection AddRemoteServiceWithNewClient<TRequest>(
4547
this IServiceCollection services,
46-
string baseAddress,
47-
TimeSpan timeout = default)
48+
Action<IServiceProvider, HttpClient> configureClient,
49+
Action<IHttpClientBuilder>? configureClientBuilder = null)
4850
where TRequest : IServiceRequestMarker
4951
{
5052
var clientName = DefaultClientName.Resolve<TRequest>();
51-
return services.AddRemoteServiceWithNewClient<TRequest>(clientName, baseAddress, timeout);
53+
return services.AddRemoteServiceWithNewClient<TRequest>(
54+
clientName,
55+
configureClient,
56+
configureClientBuilder);
5257
}
5358

54-
public static IHttpClientBuilder AddRemoteServiceWithNewClient<TRequest>(
59+
public static IServiceCollection AddRemoteServiceWithNewClient<TRequest>(
5560
this IServiceCollection services,
5661
string clientName,
57-
string baseAddress,
58-
TimeSpan timeout = default)
62+
Action<IServiceProvider, HttpClient> configureClient,
63+
Action<IHttpClientBuilder>? configureClientBuilder = null)
5964
where TRequest : IServiceRequestMarker
6065
{
61-
Action<IServiceProvider, HttpClient> configureClient = (sp, client) =>
66+
if (ServiceChannelRegistry.Instance.DoesClientExist(clientName))
6267
{
63-
client.BaseAddress = new Uri(baseAddress);
64-
if (timeout != default)
65-
{
66-
client.Timeout = timeout;
67-
}
68-
};
69-
return services.AddRemoteServiceWithNewClient<TRequest>(clientName, configureClient);
68+
throw new InvalidOperationException($"A client with name {clientName} already exists.");
69+
}
70+
71+
return services.AddRemoteServiceWithNewClientInternal(
72+
typeof(TRequest),
73+
clientName,
74+
configureClient,
75+
configureClientBuilder);
7076
}
7177

7278
public static IServiceCollection AddRemoteServiceToExistingClient<TRequest>(
7379
this IServiceCollection services,
7480
string clientName)
7581
where TRequest : IServiceRequestMarker
7682
{
77-
if (ServiceChannelRegistry.Instance.IsRegistered<TRequest>())
83+
if (!ServiceChannelRegistry.Instance.DoesClientExist(clientName))
7884
{
79-
throw new InvalidOperationException($"A channel for request type {typeof(TRequest)} is already registered.");
85+
throw new InvalidOperationException($"A client with name {clientName} does not exist.");
8086
}
81-
if (!ServiceChannelRegistry.Instance.IsRegistered(clientName))
82-
{
83-
throw new InvalidOperationException($"A channel with client name {clientName} is not registered.");
84-
}
85-
if (!ServiceChannelRegistry.Instance.Register<TRequest>(clientName))
86-
{
87-
throw new InvalidOperationException($"Channel couldn't be registered for request type {typeof(TRequest)} and client name {clientName}.");
88-
}
89-
return services;
87+
return services.AddRemoteServiceToExistingClientInternal(typeof(TRequest), clientName);
9088
}
9189

92-
public static IHttpClientBuilder AddRemoteServicesWithNewClient(
90+
public static IServiceCollection AddRemoteServicesWithNewClient(
9391
this IServiceCollection services,
9492
Assembly fromAssembly,
9593
string clientName,
9694
Action<IServiceProvider, HttpClient> configureClient,
97-
Func<Type, bool>? filterPredicate = null)
95+
Action<IHttpClientBuilder>? configureClientBuilder = null,
96+
Func<Type, bool>? requestFilterPredicate = null)
9897
{
99-
if (ServiceChannelRegistry.Instance.IsRegistered(clientName))
98+
if (ServiceChannelRegistry.Instance.DoesClientExist(clientName))
10099
{
101-
throw new InvalidOperationException($"A channel with client name {clientName} is already registered.");
100+
throw new InvalidOperationException($"A client with name {clientName} already exists.");
102101
}
103102
var requestTypes = fromAssembly
104103
.DefinedTypes
105104
.Where(type => type is { IsAbstract: false, IsInterface: false } &&
106105
type.IsAssignableTo(typeof(IServiceRequestMarker)));
107106

108-
if (filterPredicate is not null)
107+
if (requestFilterPredicate is not null)
109108
{
110-
requestTypes = requestTypes.Where(type => filterPredicate(type));
109+
requestTypes = requestTypes.Where(type => requestFilterPredicate(type));
111110
}
112111

113-
IHttpClientBuilder? clientBuilder = null;
114-
var requestTypeList = requestTypes.ToList();
115-
for (var i = 0; i < requestTypeList.Count; i++)
116-
{
117-
//first element
118-
if (i == 0)
119-
{
120-
clientBuilder = services.AddRemoteServiceWithNewClientInternal(
121-
requestTypeList[i],
122-
clientName,
123-
configureClient);
124-
}
125-
//rest
126-
else
127-
{
128-
services.AddRemoteServiceToExistingClientInternal(
129-
requestTypeList[i],
130-
clientName);
131-
}
132-
}
133-
if (clientBuilder is null)
112+
services.AddRemoteServicesCore();
113+
services.AddClientInternal(
114+
clientName,
115+
configureClient,
116+
configureClientBuilder);
117+
foreach (var requestType in requestTypes)
134118
{
135-
throw new InvalidOperationException($"Cannot create HttpClient with client name {clientName}.");
119+
services.AddRemoteServiceToExistingClientInternal(
120+
requestType,
121+
clientName);
136122
}
137-
return clientBuilder;
123+
return services;
138124
}
139125

140126
public static IServiceCollection AddRemoteServicesToExistingClient(
141127
this IServiceCollection services,
142128
Assembly fromAssembly,
143129
string clientName,
144-
Func<Type, bool>? filterPredicate = null)
130+
Func<Type, bool>? requestFilterPredicate = null)
145131
{
146-
if (!ServiceChannelRegistry.Instance.IsRegistered(clientName))
132+
if (!ServiceChannelRegistry.Instance.DoesClientExist(clientName))
147133
{
148-
throw new InvalidOperationException($"A channel with client name {clientName} is not registered.");
134+
throw new InvalidOperationException($"A client with name {clientName} does not exist.");
149135
}
150136
var requestTypes = fromAssembly
151137
.DefinedTypes
152138
.Where(type => type is { IsAbstract: false, IsInterface: false } &&
153139
type.IsAssignableTo(typeof(IServiceRequestMarker)));
154140

155-
if (filterPredicate is not null)
141+
if (requestFilterPredicate is not null)
156142
{
157-
requestTypes = requestTypes.Where(type => filterPredicate(type));
143+
requestTypes = requestTypes.Where(type => requestFilterPredicate(type));
158144
}
159145

160146
foreach (var requestType in requestTypes)
@@ -166,44 +152,58 @@ public static IServiceCollection AddRemoteServicesToExistingClient(
166152
return services;
167153
}
168154

169-
private static IHttpClientBuilder AddRemoteServiceWithNewClientInternal(
155+
private static IServiceCollection AddRemoteServiceWithNewClientInternal(
170156
this IServiceCollection services,
171157
Type requestType,
172158
string clientName,
173-
Action<IServiceProvider, HttpClient> configureClient)
159+
Action<IServiceProvider, HttpClient> configureClient,
160+
Action<IHttpClientBuilder>? configureClientBuilder)
174161
{
175-
if (ServiceChannelRegistry.Instance.IsRegistered(requestType))
162+
if (ServiceChannelRegistry.Instance.IsRequestRegistered(requestType))
176163
{
177164
throw new InvalidOperationException($"A channel for request type {requestType} is already registered.");
178165
}
179-
180166
services.AddRemoteServicesCore();
181-
182-
if (!ServiceChannelRegistry.Instance.Register(requestType, clientName))
167+
services.AddClientInternal(
168+
clientName,
169+
configureClient,
170+
configureClientBuilder);
171+
if (!ServiceChannelRegistry.Instance.RegisterRequest(requestType, clientName))
183172
{
184173
throw new InvalidOperationException($"Channel couldn't be registered for request type {requestType} and client name {clientName}.");
185174
}
186-
var clientBuilder = services.AddHttpClient(clientName);
187-
clientBuilder.ConfigureHttpClient(configureClient);
188-
return clientBuilder;
175+
return services;
189176
}
190177

191178
private static IServiceCollection AddRemoteServiceToExistingClientInternal(
192179
this IServiceCollection services,
193180
Type requestType,
194181
string clientName)
195182
{
196-
if (ServiceChannelRegistry.Instance.IsRegistered(requestType))
183+
if (ServiceChannelRegistry.Instance.IsRequestRegistered(requestType))
197184
{
198185
throw new InvalidOperationException($"A channel for request type {requestType} is already registered.");
199186
}
200-
if (!ServiceChannelRegistry.Instance.Register(requestType, clientName))
187+
if (!ServiceChannelRegistry.Instance.RegisterRequest(requestType, clientName))
201188
{
202189
throw new InvalidOperationException($"Channel couldn't be registered for request type {requestType} and client name {clientName}.");
203190
}
204191
return services;
205192
}
206193

194+
private static IServiceCollection AddClientInternal(
195+
this IServiceCollection services,
196+
string clientName,
197+
Action<IServiceProvider, HttpClient> configureClient,
198+
Action<IHttpClientBuilder>? configureClientBuilder)
199+
{
200+
var clientBuilder = services.AddHttpClient(clientName);
201+
clientBuilder.ConfigureHttpClient(configureClient);
202+
configureClientBuilder?.Invoke(clientBuilder);
203+
ServiceChannelRegistry.Instance.AddClient(clientName);
204+
return services;
205+
}
206+
207207
private static IServiceCollection AddRemoteServicesCore(
208208
this IServiceCollection services)
209209
{

src/ModEndpoints.RemoteServices/ServiceChannel.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public async Task<Result<TResponse>> SendAsync<TRequest, TResponse>(
2727
{
2828
return Result<TResponse>.Fail(requestUriResult);
2929
}
30-
if (!ServiceChannelRegistry.Instance.IsRegistered<TRequest>(out var clientName))
30+
if (!ServiceChannelRegistry.Instance.IsRequestRegistered<TRequest>(out var clientName))
3131
{
3232
return Result<TResponse>.CriticalError($"No channel registration found for request type {typeof(TRequest)}");
3333
}
@@ -63,7 +63,7 @@ public async Task<Result> SendAsync<TRequest>(
6363
{
6464
return Result.Fail(requestUriResult);
6565
}
66-
if (!ServiceChannelRegistry.Instance.IsRegistered<TRequest>(out var clientName))
66+
if (!ServiceChannelRegistry.Instance.IsRequestRegistered<TRequest>(out var clientName))
6767
{
6868
return Result.CriticalError($"No channel registration found for request type {typeof(TRequest)}");
6969
}

0 commit comments

Comments
 (0)