I'm building a small library for internal use that relies on Flurl for handling all outgoing HTTP calls.
I want to provide the ability for consumers of this library to opt-in to our HTTP call tracing infrastrucure like so:
Startup.cs:
...
public void ConfigureServices(IServiceCollection services)
{
....
services.AddTracing(options => ...);
....
}
...
My current implementation of AddTracing() looks like this:
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddTracing(this IServiceCollection services, Action<TracingOptions> configureOptions)
{
var tracingOptions = new TracingOptions();
configureOptions(tracingOptions);
// Make these options available in DI container
services.AddSingleton(tracingOptions);
FlurlHttp.Configure(settings =>
{
settings.HttpClientFactory = new TracingHttpClientFactory(tracingOptions.ApplicationName);
});
return services;
}
}
And the current implementation of TracingHttpClientFactory looks like this:
public class TracingHttpClientFactory : DefaultHttpClientFactory
{
private readonly string _applicationName;
public TracingHttpClientFactory(string applicationName)
{
_applicationName = applicationName;
}
// override to customize how HttpMessageHandler is created/configured
public override HttpMessageHandler CreateMessageHandler()
{
var tracingHandler = new TracingHandler(_applicationName, base.CreateMessageHandler());
return tracingHandler;
}
}
This works, but the issue I'm facing is that the documentation for Configure() reads: Should only be called once at application startup.
As a result, I've "wasted" my call to Configure() by adding tracing (which is optional). In scenarios where tracing is used, I also still need to call Configure() afterwards.
An example of when I might need to call configure afterwards would be in Startup.cs:
...
public void ConfigureServices(IServiceCollection services)
{
....
// Configure() is being called inside AddTracing()
services.AddTracing(options => ...);
....
// This is a second call to Configure()
FlurlHttp.Configure(settings => {
var jsonSettings = new JsonSerializerSettings
{
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
};
settings.JsonSerializer = new NewtonsoftJsonSerializer(jsonSettings);
});
}
...
Point being - each consumer of AddTracing() should be able to configure Flurl as they see fit. The point of AddTracing() is to simply "supercharge" Flurl with some extra, optional functionality. It isn't meant to take over Configure() - it's meant to extend it.
I've been reading through the docs here, and despite there being a number of places where configuration can occur, I can't work out a way to get my TracingHandler (which is a HttpMessageHander) into every request without calling Configure() somewhere.
Is there a suitable implementation for the scenario I've described?
The reason for the "call once at startup" advice is it touches global scope. If you start messing with the global settings in lots of different places, perhaps on different threads (e.g. in a controller action method), you could end up with weird race conditions and other unpredictable behavior.
In your case, yes, you're calling Configure twice, but the calls are sequential, non-conflicting, and properly done where "startup" code belongs in an ASP.NET Core app. Most importantly, they're done before any calls are made with Flurl. So what you've done here is fine.
Related
Core2 has a hook for validating options read from appsettings.json:
services.PostConfigure<MyConfig>(options => {
// do some validation
// maybe throw exception if appsettings.json has invalid data
});
This validation code triggers on first use of MyConfig, and every time after that. So I get multiple runtime errors.
However it is more sensible to run validation during startup - if config validation fails I want the app to fail immediately. The docs imply that is how it works, but that is not what happens.
So am I doing it right? If so and this is by design, then how can I change what I'm doing so it works the way I want?
(Also, what is the difference between PostConfigure and PostConfigureAll? There is no difference in this case, so when should I use either one?)
This has been discussed in this dotnet/runtime issue since 2018.
In .NET 6, a ValidateOnStart extension method has been added to Microsoft.Extensions.Hosting
You can use it this way:
services.AddOptions<MyOptions>()
.ValidateDataAnnotations()
.ValidateOnStart(); // Support eager validation
However, ValidateDataAnnotations still does not validate nested properties and this won't be fixed soon (Microsoft issue).
This NuGet package provides a ConfigureAndValidate<TOptions> extension method which validates options at startup.
It is based on Microsoft.Extensions.Options.DataAnnotations. But unlike Microsoft's package, it can even validate nested properties.
It is compatible with .NET Standard 2.0, .NET Core 3.1, .NET 5, .NET 6 and .NET 7.
Documentation & source code (GitHub)
TL;DR
Create your options class(es)
Decorate your options with data annotations
Call ConfigureAndValidate<T>(Action<T> configureOptions) on your IServiceCollection
ConfigureAndValidate will configure your options (calling the base Configure method), but will also check that the built configuration respects the data annotations, otherwise an OptionsValidationException (with details) is thrown as soon as the application is started. No misconfiguration surprise at runtime!
Use
ServiceCollection extension
services.ConfigureAndValidate<TOptions>(configureOptions)
Is syntactic sugar for
services
.AddOptions<TOptions>()
.Configure(configureOptions) // Microsoft
.ValidateDataAnnotationsRecursively() // based on Microsoft's ValidateDataAnnotations, but supports nested properties
.ValidateOnStart() // or ValidateEagerly() in previous versions
.Services
OptionsBuilder extensions
ValidateDataAnnotationsRecursively
This method register this options instance for validation of its DataAnnotations at the first dependency injection. Nested objects are supported.
ValidateOnStart (or ValidateEagerly in previous versions)
This method validates this options instance at application startup rather than at the first dependency injection.
Custom validation
You can combine with your own option validations:
services
.AddOptions<TOptions>()
.Configure(configureOptions)
//...
.Validate(options => { /* custom */ }, message)
.Validate<TDependency1, TDependency2>((options, dependency1, dependency2) =>
{
// custom validation
},
"Custom error message")
//...
.ValidateDataAnnotationsRecursively()
.ValidateOnStart()
Microsoft options validation documentation
There is no real way to run a configuration validation during startup. As you already noticed, post configure actions run, just like normal configure actions, lazily when the options object is being requested. This completely by design, and allows for many important features, for example reloading configuration during run-time or also options cache invalidation.
What the post configuration action is usually being used for is not a validation in terms of “if there’s something wrong, then throw an exception”, but rather “if there’s something wrong, fall back to sane defaults and make it work”.
For example, there’s a post configuration step in the authentication stack, that makes sure that there’s always a SignInScheme set for remote authentication handlers:
options.SignInScheme = options.SignInScheme ?? _authOptions.DefaultSignInScheme ?? _authOptions.DefaultScheme;
As you can see, this will not fail but rather just provides multiple fallbacks.
In this sense, it’s also important to remember that options and configuration are actually two separate things. It’s just that the configuration is a commonly used source for configuring options. So one might argue that it is not actually the job of the options to validate that the configuration is correct.
As such it might make more sense to actually check the configuration in the Startup, before configuring the options. Something like this:
var myOptionsConfiguration = Configuration.GetSection("MyOptions");
if (string.IsNullOrEmpty(myOptionsConfiguration["Url"]))
throw new Exception("MyOptions:Url is a required configuration");
services.Configure<MyOptions>(myOptionsConfiguration);
Of course this easily becomes very excessive, and will likely force you to bind/parse many properties manually. It will also ignore the configuration chaining that the options pattern supports (i.e. configuring a single options object with multiple sources/actions).
So what you could do here is keep your post configuration action for validation, and simply trigger the validation during startup by actually requesting the options object. For example, you could simply add IOptions<MyOptions> as a dependency to the Startup.Configure method:
public void Configure(IApplicationBuilder app, IOptions<MyOptions> myOptions)
{
// all configuration and post configuration actions automatically run
// …
}
If you have multiple of these options, you could even move this into a separate type:
public class OptionsValidator
{
public OptionsValidator(IOptions<MyOptions> myOptions, IOptions<OtherOptions> otherOptions)
{ }
}
At that time, you could also move the logic from the post configuration action into that OptionsValidator. So you could trigger the validation explicitly as part of the application startup:
public void Configure(IApplicationBuilder app, OptionsValidator optionsValidator)
{
optionsValidator.Validate();
// …
}
As you can see, there’s no single answer for this. You should think about your requirements and see what makes the most sense for your case. And of course, this whole validation only makes sense for certain configurations. In particular, you will have difficulties when working configurations that will change during run-time (you could make this work with a custom options monitor, but it’s probably not worth the hassle). But as most own applications usually just use cached IOptions<T>, you likely don’t need that.
As for PostConfigure and PostConfigureAll, they both register an IPostConfigure<TOptions>. The difference is simply that the former will only match a single named option (by default the unnamed option—if you don’t care about option names), while PostConfigureAll will run for all names.
Named options are for example used for the authentication stack, where each authentication method is identified by its scheme name. So you could for example add multiple OAuth handlers and use PostConfigure("oauth-a", …) to configure one and PostConfigure("oauth-b", …) to configure the other, or use PostConfigureAll(…) to configure them both.
On an ASP.NET Core 2.2 project I got this working doing eager validation by following these steps...
Given an Options class like this one:
public class CredCycleOptions
{
[Range(1753, int.MaxValue, ErrorMessage = "Please enter a valid integer Number.")]
public int VerifiedMinYear { get; set; }
[Range(1753, int.MaxValue, ErrorMessage = "Please enter a valid integer Number.")]
public int SignedMinYear { get; set; }
[Range(1753, int.MaxValue, ErrorMessage = "Please enter a valid integer Number.")]
public int SentMinYear { get; set; }
[Range(1753, int.MaxValue, ErrorMessage = "Please enter a valid integer Number.")]
public int ConfirmedMinYear { get; set; }
}
In Startup.cs add these lines to ConfigureServices method:
services.AddOptions();
// This will validate Eagerly...
services.ConfigureAndValidate<CredCycleOptions>("CredCycle", Configuration);
ConfigureAndValidate is an extension method from here.
public static class OptionsExtensions
{
private static void ValidateByDataAnnotation(object instance, string sectionName)
{
var validationResults = new List<ValidationResult>();
var context = new ValidationContext(instance);
var valid = Validator.TryValidateObject(instance, context, validationResults);
if (valid)
return;
var msg = string.Join("\n", validationResults.Select(r => r.ErrorMessage));
throw new Exception($"Invalid configuration for section '{sectionName}':\n{msg}");
}
public static OptionsBuilder<TOptions> ValidateByDataAnnotation<TOptions>(
this OptionsBuilder<TOptions> builder,
string sectionName)
where TOptions : class
{
return builder.PostConfigure(x => ValidateByDataAnnotation(x, sectionName));
}
public static IServiceCollection ConfigureAndValidate<TOptions>(
this IServiceCollection services,
string sectionName,
IConfiguration configuration)
where TOptions : class
{
var section = configuration.GetSection(sectionName);
services
.AddOptions<TOptions>()
.Bind(section)
.ValidateByDataAnnotation(sectionName)
.ValidateEagerly();
return services;
}
public static OptionsBuilder<TOptions> ValidateEagerly<TOptions>(this OptionsBuilder<TOptions> optionsBuilder) where TOptions : class
{
optionsBuilder.Services.AddTransient<IStartupFilter, StartupOptionsValidation<TOptions>>();
return optionsBuilder;
}
}
I plumbed ValidateEargerly extension method right inside ConfigureAndValidate. It makes use of this other class from here:
public class StartupOptionsValidation<T> : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return builder =>
{
var options = builder.ApplicationServices.GetService(typeof(IOptions<>).MakeGenericType(typeof(T)));
if (options != null)
{
// Retrieve the value to trigger validation
var optionsValue = ((IOptions<object>)options).Value;
}
next(builder);
};
}
}
This allows us to add data annotations to the CredCycleOptions and get nice error feedback right at the moment the app starts making it an ideal solution.
If an option is missing or have a wrong value, we don't want users to catch these errors at runtime. That would be a bad experience.
There are easy way to validating with using IStartupFilter and IValidateOptions.
You can just put below code your ASP.NET Core project.
public static class OptionsBuilderExtensions
{
public static OptionsBuilder<TOptions> ValidateOnStartupTime<TOptions>(this OptionsBuilder<TOptions> builder)
where TOptions : class
{
builder.Services.AddTransient<IStartupFilter, OptionsValidateFilter<TOptions>>();
return builder;
}
public class OptionsValidateFilter<TOptions> : IStartupFilter where TOptions : class
{
private readonly IOptions<TOptions> _options;
public OptionsValidateFilter(IOptions<TOptions> options)
{
_options = options;
}
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
_ = _options.Value; // Trigger for validating options.
return next;
}
}
}
And just chain the ValidateOnStartup method on OptionsBuilder<TOptions>.
services.AddOptions<SampleOption>()
.Bind(Configuration)
.ValidateDataAnnotations()
.ValidateOnStartupTime();
If you want to create custom validator for options class, checkout this article.
This has been implemented in .NET 6. Now you can just write the following:
services.AddOptions<SampleOption>()
.Bind(Configuration)
.ValidateDataAnnotations()
.ValidateOnStart(); // works in .NET 6
No need for external NuGet Packages or extra code.
See OptionsBuilderExtensions.ValidateOnStart<TOptions>
Below is a generic ConfigureAndValidate method to validate immediately and "fail fast".
To summarize the steps:
Call serviceCollection.Configure for your options
Do serviceCollection.BuildServiceProvider().CreateScope()
Get the options instance with scope.ServiceProvider.GetRequiredService<IOptions<T>> (remember to take the .Value)
Validate it using Validator.TryValidateObject
public static class ConfigExtensions
{
public static void ConfigureAndValidate<T>(this IServiceCollection serviceCollection, Action<T> configureOptions) where T : class, new()
{
// Inspired by https://blog.bredvid.no/validating-configuration-in-asp-net-core-e9825bd15f10
serviceCollection.Configure(configureOptions);
using (var scope = serviceCollection.BuildServiceProvider().CreateScope())
{
var options = scope.ServiceProvider.GetRequiredService<IOptions<T>>();
var optionsValue = options.Value;
var configErrors = ValidationErrors(optionsValue).ToArray();
if (!configErrors.Any())
{
return;
}
var aggregatedErrors = string.Join(",", configErrors);
var count = configErrors.Length;
var configType = typeof(T).FullName;
throw new ApplicationException($"{configType} configuration has {count} error(s): {aggregatedErrors}");
}
}
private static IEnumerable<string> ValidationErrors(object obj)
{
var context = new ValidationContext(obj, serviceProvider: null, items: null);
var results = new List<ValidationResult>();
Validator.TryValidateObject(obj, context, results, true);
foreach (var validationResult in results)
{
yield return validationResult.ErrorMessage;
}
}
}
I have a legacy Asp.net Core 3.1 application which uses Kestrel server and all our GET and POST calls works fine. We have bunch of middlewares already on my legacy application and we use each of those middlewares for different purpose depending on what is the endpoint.
This is how our legacy application is setup as shown below. I have tried to keep things simple by keeping only important things.
Below is our BaseMiddleware class which is extended by bunch of other middlewares we have. Approx we have 10+ middlewares extending BaseMiddleware class -
BaseMiddleware.cs
public abstract class BaseMiddleware {
protected static ICatalogService catalogService;
protected static ICustomerService customerService;
private static IDictionary <string, Object> requiredServices;
private readonly RequestDelegate _next;
public abstract bool IsCorrectEndpoint(HttpContext context);
public abstract string GetEndpoint(HttpContext context);
public abstract Task HandleRequest(HttpContext context);
public BaseMiddleware(RequestDelegate next) {
var builder = new StringBuilder("");
var isMissingService = false;
foreach(var service in requiredServices) {
if (service.Value == null) {
isMissingService = true;
builder.Append(service.Key).Append(", ");
}
}
if (isMissingService) {
var errorMessage = builder.Append("cannot start server.").ToString();
throw new Exception(errorMessage);
}
_next = next;
}
public async Task Invoke(HttpContext context) {
if (IsCorrectEndpoint(context)) {
try {
await HandleRequest(context);
} catch (Exception ex) {
// handle exception here
return;
}
return;
}
await _next.Invoke(context);
}
public static void InitializeDependencies(IServiceProvider provider) {
requiredServices = new Dictionary<string, Object>();
var catalogServiceTask = Task.Run(() => provider.GetService<ICatalogService>());
var customerServiceTask = Task.Run(() => provider.GetService<ICustomerService>());
// .... few other services like above approx 10+ again
Task.WhenAll(catalogServiceTask, landingServiceTask, customerServiceTask).Wait();
requiredServices[nameof(catalogService)] = catalogService = catalogServiceTask.Result;
requiredServices[nameof(customerService)] = customerService = customerServiceTask.Result;
// ....
}
}
ICatalogService and ICustomerService are normal interfaces with some methods in them that their implementation class implements.
Below is one of our middlewares example that extend BaseMiddleware. All other middlewares follow same logic as below one -
FirstServiceMiddleware.cs
public class FirstServiceMiddleware : BaseMiddleware
{
public FirstServiceMiddleware(RequestDelegate next) : base(next) { }
public override bool IsCorrectEndpoint(HttpContext context)
{
return context.Request.Path.StartsWithSegments("/first");
}
public override string GetEndpoint(HttpContext context) => "/first";
public override async Task HandleRequest(HttpContext context)
{
context.Response.StatusCode = 200;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync("Hello World!");
}
}
public static class FirstServiceMiddlewareExtension
{
public static IApplicationBuilder UseFirstService(this IApplicationBuilder builder)
{
return builder.UseMiddleware<FirstServiceMiddleware>();
}
}
Below is how my Startup class is configured -
Startup.cs
private static ILoggingService _loggingService;
public Startup(IHostingEnvironment env) {
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services) {
services.AddResponseCompression(options =>
{
options.Providers.Add<GzipCompressionProvider>();
});
services.Configure<GzipCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.Fastest;
});
DependencyBootstrap.WireUpDependencies(services);
var provider = services.BuildServiceProvider();
if (_loggingService == null) _loggingService = provider.GetService<ILoggingService>();
//.. some other code here
BaseMiddleware.InitializeDependencies(provider);
}
public void Configure(IApplicationBuilder app, IHostApplicationLifetime lifetime) {
// old legacy middlewares
app.UseFirstService();
// .. few other middlewares here
}
And below is my DependencyBootstrap class -
DependencyBootstrap.cs
public static class DependencyBootstrap
{
//.. some constants here
public static void WireUpDependencies(IServiceCollection services)
{
ThreadPool.SetMinThreads(100, 100);
var provider = services.BuildServiceProvider();
var loggingService = provider.GetService<ILoggingService>();
// ... some other code here
try
{
WireUp(services, loggingService);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
private static void WireUp(IServiceCollection services, ILoggingService loggingService)
{
// adding services here
services.AddSingleton<....>();
services.AddSingleton<....>();
//....
var localProvider = services.BuildServiceProvider();
if (IS_DEVELOPMENT)
{
processClient = null;
}
else
{
processClient = localProvider.GetService<IProcessClient>();
}
services.AddSingleton<IData, DataImpl>();
services.AddSingleton<ICatalogService, CatalogServiceImpl>();
services.AddSingleton<ICustomerService, CustomerServiceImpl>();
//.. some other services and singleton here
}
}
Problem Statement
I have recently started working with C# and asp.net core framework. I have done my reading and it looks like -
Our legacy application doesn't use Dependency Injection properly as we have lot of places using BuildServiceProvider method which causes that warning. I am not sure why we have to do it.
Also do we really need InitializeDependencies method in BaseMiddleware class? If not then how we can initialize dependencies properly? It looks like we are trying to initialize all the dependencies during server startup so that they all are ready when the call comes for any middleware. I'd like to keep this logic as it is if possible.
Currently I am confuse what is the best way to use DI in asp.net core and if my application is doing it wrong then how can I do it the right way? Above code works fine in our application from a very long time but looks like we might be using it totally wrong way of DI.
Calling BuildServiceProvider multiple times can cause serious trouble, because each call to BuildServiceProvider results in a new container instance with its own cache. This means that registrations that are expected to have the Singleton lifestyle, suddenly are created more than once. This is a problem called Ambiguous Lifestyle.
Some Singletons are stateless and for them there is no difference in creating one or a thousand. But other components that are registered as Singleton might have state, and the working of the application might (indirectly) depend on that state not being duplicated.
To make matters worse, while your application might work correctly today, this might change any time in the future when one of the third-party or framework components you depend on makes a change to one of their components in such way that it becomes a problem when that component is created multiple times.
In your example, you are resolving both ILoggingService and IProcessClient from a service provider. If the resolved components are stateless objects without stateful dependencies, there is no real harm done. But this might change when they become stateful. Again, this can happen by a change of one of its indirect dependencies, so this is something you might not be aware of. This can cause you or your team to waste many hours; such problem will likely not be easily found.
This means that the answer "simply" is to prevent calling BuildServiceProvider() to create intermediate container instances. But this might be easier said than done. In your case, however, you seem to require a dependency on ILoggerService before all dependencies are wired. A typical way to achieve this is to split the registration phase into two separate steps:
One step where you manually create those few singletons that you need before hand
Add them to your container builder (IServiceCollection)
Add all other registrations
For instance:
private ILoggingService _loggingService;
public Startup(Confiration config)
{
_loggingService = new MySpecialLoggingService(config.LogPath);
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(_loggingService);
// More stuf here.
...
}
Advantage of this structure is that when a dependency is added to the constructor of this manually built MySpecialLoggingService, your code stops compiling and you're forced to look at this code. When that constructor depends on some other framework abstraction or application abstraction that isn't yet available, you know you're in trouble and need to rethink your design.
Final note, calling BuildServiceProvider multiple times isn't a bad thing per se. It is okay when you explicitly want to have multiple isolated modules in your application that each have their own state and run independently of one another. For instance when running multiple end points for multiple bounded contexts within the same process.
UPDATE
I think I am starting to understand what it is you are trying to achieve in your BaseMiddleware. It is a 'convenient' helper class that holds all the dependencies that its derivatives might need. This is probably an old design and you might be aware of this, but this base class is quite problematic. Base classes with dependencies are hardly ever a good idea, because they tend to become big, ever changing, and obfuscate the fact that their derivatives become too complex. In your case, even, you are using the Service Locator anti-pattern which is never a good idea.
Besides this, there is a lot going on in that BaseMiddleware class that—to me—makes little sense, such as:
It contains complex logic to verify whether all dependencies exist, while there are more effective ways to do so. The most effective way is to apply Constructor Injection because it will guarantee that its necessary dependencies are always available. On top of that, you can validate the IServiceCollection on build. This gives you even greater guarantees over the correctness of your DI configuration than your BaseMiddleware currently provides.
It resolves all its services in background threads, which implies that construction of those components is either heavy on CPU or I/O, which is a problem. Instead composition should be fast, because injection constructors should be simple, which allows you to compose object graph with confidence.
You do exception handling in the base class, while it is better suited to be applied at a higher level; for instance, using an outer-most piece of middleware. For the sake of simplicity, though, my next example keeps exception handling inside the base class. That's because I have no idea what kind of things you are doing in there, that could influence my answer.
As the base class is resolving from the root container, middleware classes are only able to make use of Singleton dependencies. Connecting to the database through Entity Framework, for instance, will be a problem, because DbContext classes should not be captured in Singleton consumers.
So, with the observations and advice above, I would suggest reducing the BaseMiddleware class to the following:
// Your middleware classes should implement IMiddleware; this allows middleware
// classes to be transient and have scoped dependencies.
public abstract class ImprovedBaseMiddleware : IMiddleware
{
public abstract bool IsCorrectEndpoint(HttpContext context);
public abstract string GetEndpoint(HttpContext context);
public abstract Task HandleRequest(HttpContext context);
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (IsCorrectEndpoint(context)) {
try {
await HandleRequest(context);
}
catch (Exception ex) {
// handle exception here
return;
}
return;
}
await next(context);
}
}
Now based on this new base class, create your middleware implementations similar to this next example:
public class ImprovedFirstServiceMiddleware : ImprovedBaseMiddleware
{
private readonly ICatalogService _catalogService;
// Add all dependencies required by this middleware in the constructor.
public FirstServiceMiddleware(ICatalogService catalogService)
{
_catalogService = catalogService;
}
public override bool IsCorrectEndpoint(HttpContext context) =>
context.Request.Path.StartsWithSegments("/first");
public override string GetEndpoint(HttpContext context) => "/first";
public override async Task HandleRequest(HttpContext context)
{
context.Response.StatusCode = 200;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync("Hello from "
+ _catalogService.SomeValue());
}
}
In your application, you can register your middleware classes as follows:
public void ConfigureServices(IServiceCollection services) {
// When middleware implements IMiddleware, it must be registered. But
// that's okay, because it allows the middleware with its
// dependencies to be 'verified on build'.
services.AddTransient<ImprovedFirstServiceMiddleware>();
// If you have many middleware classes, you can use
// Auto-Registration instead. e.g.:
var middlewareTypes =
from type in typeof(HomeController).Assembly.GetTypes()
where !type.IsAbstract && !type.IsGenericType
where typeof(IMiddleware).IsAssignableFrom(type)
select type;
foreach (var middlewareType in middlewareTypes)
services.AddTransient(middlewareType);
...
}
public void Configure(
IApplicationBuilder app, IHostApplicationLifetime lifetime)
{
// Add your middleware in the correct order as you did previously.
builder.UseMiddleware<ImprovedFirstServiceMiddleware>();
}
TIP: If you start to notice that a middleware classes get big constructors, that's likely because such class does too much and gets too complex. This means it should be refactored into multiple smaller classes. In that case, your class is exhibiting the Constructor Over-Injection code smell. There are many possible refactoring patterns and design patterns available that can get you out of this situation.
I want to remove duplicated singleton service for BuildServiceProvider method. I understand I should use the existing DI service but I can't access the GetService method. I am new to DI and I appreciate it if someone could say how to access the GetService method without getting new service. Code below. Thanks.
MESSAGE: Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Consider alternatives such as dependency injecting services as parameters to 'Configure'.
public void ConfigureServices(IServiceCollection services){
services.AddAuthorization(options =>
{
var sp = services.BuildServiceProvider();//CODE ISSUE HERE
var permissionService = sp.GetService<IPermissionService>();
if (permissionService != null)
{
foreach (var permission in permissionService.GetPrivilegePermissions().Select(x => x.Name)
.Distinct())
{
options.AddPolicy(permission, policy => policy.Requirements.Add(new
PermissionRequirement(permission)));
}
}
});
}
You have written a lambda method to configure an object. However, since all Microsoft services follow the options pattern, you could instead write a service to configure it. Injecting any other service you want. You may implement any number of IConfigureOptions<T> services for any options type.
public class ConfigureAuthorization : IConfigureOptions<AuthorizationOptions>{
public ConfigureAuthorization( ... ){}
public void Configure(AuthorizationOptions options){
// ...
}
}
services.AddAuthorization();
services.AddSingleton<IConfigureOptions<AuthorizationOptions>, ConfigureAuthorization>();
Note, beware of any potential scoping issues.
Started building the infrastructure for a multi-tenant ASP.NET Core 2.1 application which is and will be composed of many modules.
The idea is to enable any modules which are to be added in the future, to be just plugged into the system, registering their own required dependencies on app startup and using the already registered dependencies (dependencies registered by other modules) if needed.
Let's first take a look at the sample code of how I imagined this would look like - from the top of my head.
Let's say we've got some kind of a module manager which we're gonna name just that - ModuleManager.
public class ModuleManager
{
// Let's store all of our module types here
private List<Type> moduleTypes;
void RegisterModules(Type webHostModuleType, IServiceCollection services)
{
// Find all module dependencies recursively
// (i.e. all modules that are specified in some, let's say 'DependsOn' attribute
// which decorates webHostModuleType)
moduleTypes = FindDependencies();
// Now we need to register all dependencies
foreach (Type moduleType in moduleTypes)
{
services.AddSingleton(moduleType);
}
// ... and we shouldn't forget to register the webHostModuleType too
services.AddSingleton(webHostModuleType);
}
}
Let's stop there for now and first define the module type. I think it is expected to presume that each module type can have different properties and fields based on their needs, but we will have to make all of them derive from a base module type. Let's call it BaseModule and this is how I imagined it to be:
public abstract class BaseModule
{
// I've currently got no idea on how this method should be defined
// Because it's supposed to register the concrete module's dependencies
// after the module has been instantiated... is that even possible?
// Should it be some kind of a factory method rather than void
// and serve as a lazy initializer? Is that also even possible?
// Something that should make use of IOptions<>?
public virtual void Init()
{
}
// Maybe some post-init code will be needed too
public virtual void PostInit()
{
}
}
And then we will have some concrete module types defined like this:
public class CoreModule : BaseModule
{
// some dependencies that need to be injected...
private readonly IHostingEnvironment hostingEnvironment;
private readonly IDontKnowSomeOtherDependency someOtherDependency;
public CoreModule(IHostingEnvironment hostingEnvironment, IDontKnowSomeOtherDependency someOtherDependency)
{
this.hostingEnvironment = hostingEnvironment;
this.someOtherDependency = someOtherDependency;
}
public override void Init()
{
// Somehow register additional services defined by this module,
// after it's been instantiated
}
}
And let's say, for the completeness sake, our webHostModuleType is defined like this:
[DependsOn(typeof(CoreModule))]
public class WebHostModule : BaseModule
{
private readonly ISomeDependencyRegisteredInCoreModule someDependency;
public WebHostModule(ISomeDependencyRegisteredInCoreModule someDependency)
{
this.someDependency = someDependency;
}
public override void Init()
{
// Register some other service based on something from 'someDependency' field
}
}
And finally, let's go back to the module manager. Now it should have another method, executed after RegisterModules, which should instantiate each module in the correct order and then call Init() and PostInit() in the correct order, starting with the CoreModule and ending with WebHostModule. Something akin to this:
public void(?) LoadModules()
{
// Sort our modules first so dependencies come in first and webHostModuleType the last
SortEm(moduleTypes);
// Now we need to instantiate them.
// Can't do it manually as all of them might have different constructors
// So need to do it using our service collection
IServiceProvider serviceProvider = serviceCollection.BuildServicePRovider();
foreach (Type moduleType in moduleTypes)
{
BaseModule moduleInstance = serviceProvider.GetRequiredService(moduleType) as BaseModule;
// This is where we can register everything needed by the module instance.
// But can we?
moduleInstance.Init();
moduleInstance.PostInit();
}
// Maybe return the IServiceProvider instance we've built
// so we can return in to the `ConfigureServices` and return to ASP.NET Core from there?
}
As you can see, this approach raises many questions. Am I going in the right direction at all? Is there a way to register services from withing modules' Init() and PostInit() methods correctly?
If I call BuildServiceProvider() and then instantiate singleton instances, I would have to return that IServiceProvider instance back to ConfigureServices() so ASP.NET Core can use it. If I don't, it will build a new one and then all these singletons will be instantiated again.
But if I call ConfigureServices() then I won't be able to add new services, which I have to do after modules have been instantiated. What is the approach, if it's possible at all? Any opinions, ideas?
Wow such wall of text, thanks for reading at all!
Well, just one more opinion, but we are using the same standard ad the Net Core team, creating extension methods, so we just need to add the services were they are required:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddVtaeCommonServices();
services.AddMyServiceMX(Configuration);
services.AddOtherService(Configuration);
return VtaeConfig.ConfigVtae(services);
}
}
With this extension methods for example:
public static class CommonServicesExtension
{
public static IServiceCollection AddVtaeCommonServices(this IServiceCollection services)
{
services.AddNodaDateTime();
services.AddMemoryCache();
services.AddScoped<AuthorizedIpFilter>();
services.AddScoped<HttpContextManager>();
services.AddTransient<ProspectService>();
services.AddTransient<TokenService>();
services.AddTransient<TokenGenerator>();
services.AddTransient<ProspectsRepository>();
services.AddSingleton<UniqueIDGenerator>();
services.AddSingleton<SchedulerService>();
services.AddSingleton<ChecksumService>();
return services;
}
}
This way we can extract our functions to nuget packages and then simply re utilize them by adding the AddXXXService() in our startup
I created a ViewComponent class which call a REST API using the HttpClient, this is the code:
public class ProductsViewComponent : ViewComponent
{
private readonly HttpClient _client;
public ProductsViewComponent(HttpClient client)
{
_client = client ?? throw new ArgumentNullException(nameof(client));
}
public async Task<IViewComponentResult> InvokeAsync(string date)
{
using(var response = await _client.GetAsync($"/product/get_products/{date}"))
{
response.EnsureSuccessStatusCode();
var products = await response.Content.ReadAsAsync<List<Products>>();
return View(products);
}
}
}
I get this error:
InvalidOperationException: Unable to resolve service for type 'System.Net.Http.HttpClient' while attempting to activate MyApp.ViewComponents.ProductsViewComponent'
I injected the HttpClient in the ConfigureService method available in Startup in this way:
services.AddHttpClient<FixturesViewComponent>(options =>
{
options.BaseAddress = new Uri("http://80.350.485.118/api/v2");
});
UPDATE:
I registered the ProductsViewComponent too, same error.
I had a similar problem - the problem was in double registration:
services.AddHttpClient<Service>();
services.AddSingleton<Service>(); // fixed by removing this line
Similar examples [just adding to clarify that it's not specific to AddSingleton, nor related to the order.]
services.AddScoped<IService, Service>(); // fixed by removing this line
services.AddHttpClient<IService, Service>();
TLDR;
ViewComponents do not support typed clients out of the box. To resolve this, add a call to AddViewComponentsAsServices() onto the end of the call to services.AddMvc(...).
After a pretty long chat that ran off the back of being able to reproduce your issue, we determined initially that the problem being observed is specific to ViewComponents. Even with a call to IServiceCollection.AddHttpClient<SomeViewComponent>(), passing an instance of HttpClient into SomeViewComponents constructor just refused to work.
However, sitting a new class (SomeService) between SomeComponent and HttpClient works as expected. This is what the docs refer to as a typed client. The code looks a bit like this:
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<SomeService>();
// ...
}
// SomeService.cs
public class SomeService
{
public SomeService(HttpClient httpClient)
{
// ...
}
}
// SomeViewComponent.cs
public class SomeViewComponent
{
public SomeViewComponent(SomeService someService)
{
// ...
}
}
As I've already stated, this approach works - the ASP.NET Core DI system is very happy to create the instance of SomeService and its typed HttpClient instance.
To restate the original problem, take the following example code:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<SomeViewComponent>();
// ...
}
public class SomeViewComponent
{
public SomeViewComponent(HttpClient httpClient)
{
// ...
}
}
In this case, the ASP.NET Core DI system refuses to create an instance of SomeViewComponent due to not being able to resolve HttpClient. It turns out that this is not specific just to ViewComponents: it also applies to Controllers and TagHelpers (thanks to Chris Pratt for confirming for TagHelpers).
Interestingly, the following also works:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<SomeViewComponent>();
// ...
}
public class SomeViewComponent
{
public SomeViewComponent(IHttpClientFactory httpClientFactory)
{
var httpClient = httpClientFactory.CreateClient("SomeViewComponent")
// ...
}
}
In this example, we're taking advantage of the fact that the call to AddHttpClient<SomeViewComponent> registered a named client for us.
In order to be able to inject HttpClient directly into a ViewComponent, we can add a call to AddViewComponentsAsServices when we register MVC with DI:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(...)
.AddViewComponentsAsServices();
// ...
}
AddControllersAsServices and AddTagHelpersAsServices can also be called to add the same support for Controllers and TagHelpers respectively.
If we look at the docs more closely, it's clear that none of the examples there inject a HttpClient into Controllers et al - there's simply no mention of this approach at all.
Unfortunately, I don't know enough about the ASP.NET Core DI system in order to be able to explain exactly why this works the way it does: The information I've provided above simply explains the what along with a solution. Chris Pratt has opened an issue in Github for the docs to be updated to expand upon this.
I was getting a similar error in my Azure Function Version 2. As per this document, we should be able to add the IHttpClientFactory as a dependency. After adding this DI in my Azure Function, I was getting the error mentioned below.
Microsoft.Extensions.DependencyInjection.Abstractions: Unable to
resolve service for type 'System.Net.Http.IHttpClientFactory' while
attempting to activate
'OServiceBus.Adapter.FetchDataFromSubscription1'
The issue was that I had not override the Configure function to add the HttpClient as a registered dependency. So I just created a class called Statup in the root directory of my Azure Function as follows.
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
[assembly: FunctionsStartup(typeof(ServiceBus.Adapter.Startup))]
namespace ServiceBus.Adapter {
public class Startup: FunctionsStartup {
public override void Configure(IFunctionsHostBuilder builder) {
builder.Services.AddHttpClient();
}
}
}
After adding this, my function started working properly. Hope it helps.
I had a similar error message trying to inject a wrapper for an external REST service to my controller as an interface. I needed to change the following in ConfigureServices:
services.AddHttpClient<IMyServiceWrapper>("MyServiceWrapper", client =>
{
client.BaseAddress = new Uri("http://some_service/api");
}
to
services.AddHttpClient<IMyServiceWrapper, MyServiceWrapper>("MyServiceWrapper", client =>
{
client.BaseAddress = new Uri("http://some_service/api");
}
in order to be able to use the interface in the constructor of my controller:
public MyController(IMyServiceWrapper myService)
{
_myService = myService;
}
Useful for testing myController using a mock service.
It seems that you've got two view components mixed up. You're registering the FixturesViewComponent as a "named HTTP client" yet you attempt to inject an HttpClient instance in the ProductsViewComponent.
Changing the HttpClient registration to ProductsViewComponent should help:
services.AddHttpClient<ProductsViewComponent>(options =>
{
options.BaseAddress = new Uri("http://80.350.485.118/api/v2");
});
Maybe it will help, but in my situation this worked:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMyService,MyService>(); // my usual DI injection of a service that can be mocked
services.AddHttpClient<IMyService,MyService>(client => {
client.BaseAddress = new Uri("https://myservice.com/api");
}); // notice that I use IMyService for the reference of the registration AND implementation to where it will be injected.
}
public class MyService
{
public MyService(HttpClient client)
{
// client.BaseAddress is properly set here
}
}
public class MyController : Controller
{
public MyController(IMyService service) // used by the interface
{}
}
I've tried services.AddHttpClient<IMyService>() as well, which would not resolve due to lack of it's constructor.
Also tried services.AddHttpClient<MyService>() as above, but it would not resolve the configured instance, as described above.
So the important part is that class that is used to reference the resolved type needs to be used. So this also works:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<MyService>(); // registering the type itself, not via interface
services.AddHttpClient<MyService>(client => {
client.BaseAddress = new Uri("https://myservice.com/api");
}); // it's ok here, since it will be resolved by it's own type name
}
public class MyService
{
public MyService(HttpClient client)
{
// client.BaseAddress is properly set here
}
}
public class MyController : Controller
{
public MyController(MyService service) // used by the type directly
{}
}
It kind of makes sense, but documentation and examples could be better.