How do I add a LinkGenerator object to my IServiceCollection for DI in the Startup.cs ConfigureServices method?
public MyService(LinkGenerator linkGenerator) { }
Have tried:
public static void AddLinkGenerator(this IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddScoped<IUrlHelper, UrlHelper>(implementationFactory =>
{
var actionContext = implementationFactory.GetService<IActionContextAccessor>().ActionContext;
return new UrlHelper(actionContext);
});
}
As far as I know, the LinkGenerator servides will be registered when you call the services.AddRouting(); methods and this codes will be called when you run .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); in the program.cs methods.
So if you use configure the asp.net core application as a web host, there is no need to call services.AddRouting(); methods again in your ConfigureServices method. This service will be registered before startup.cs's ConfigureServices method.
You could refer to below source codes to know how it has been registerd in RoutingServiceCollectionExtensions class.
Notice: Since the DefaultLinkGenerator is internal class, we couldn't use services.TryAddSingleton<LinkGenerator, DefaultLinkGenerator>(); to just register the LinkGenerator class.
public static IServiceCollection AddRouting(this IServiceCollection services)
{
//....
// Link generation related services
services.TryAddSingleton<LinkGenerator, DefaultLinkGenerator>();
services.TryAddSingleton<IEndpointAddressScheme<string>, EndpointNameAddressScheme>();
services.TryAddSingleton<IEndpointAddressScheme<RouteValuesAddress>, RouteValuesAddressScheme>();
services.TryAddSingleton<LinkParser, DefaultLinkParser>();
//....
}
Related
In C#.NET Core you can create a generic host using the following code:
IHostBuilder builder = new HostBuilder()
.ConfigureServices((context, collection) => {
collection.AddSingleton<IMyClass, MyClass>();
collection.AddHostedService<MyService>();
});
await builder.RunConsoleAsync();
This creates a new instance of MyService with the default DI container.
Now, say that I want to create a new host inside MyService. This is easy enough (a web host in this case):
IWebHost webHost = WebHost.CreateDefaultBuilder()
.UseStartup<MyStartup>()
.Build();
.RunAsync();
This webhost will have its own Dependency Injection container, so it will not have access to all dependencies I've already added to the generic host container: i.e. it will not be able to have IMyClass injected into MyStartup.
I've also tried adding a custom IServiceProviderFactory<> using the following code (based on the .UseDefaultServiceProvider() code where they use IServiceCollection as the builder type):
public class CustomServiceProviderFactory : IServiceProviderFactory<IServiceCollection>
{
private readonly IServiceProvider _provider;
public CustomServiceProviderFactory(IServiceProvider provider)
{
_provider = provider;
}
public IServiceCollection CreateBuilder(IServiceCollection services)
{
return services;
}
public IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder)
{
return _provider;
}
}
Then in my HostBuilder I added it through .UseServiceProviderFactory(new CustomServiceProviderFactory(_serviceProvider)), but for some reason the HostedService is instantiated before this is created, causing DI exceptions about not finding the required objects.
However, seeing as WebHost.CreateDefaultBuilder() is now the preferred way to create a webhost (in .NET Core 3.0), and an IWebHostBuilder does not have an option to set a custom IServiceProviderFactory this does seem like a dead end.
How can I have the webhost use the same DI container as the initial generic host?
I've tried to do the same thing and this is what I have landed on. Not fully tested but it does appear to work.
First, in my base/first HostBuilder, add the service collection as a service so an IServiceCollection can be resolved via DI later on.
IHostBuilder builder = new HostBuilder()
.ConfigureServices((ctx, services) =>
{
services.AddSingleton<IMyService, MyService>();
services.AddHostedService<MyApp>();
services.AddSingleton(services);
});
In IHostedService.StartAsync() I create the WebHost. I copied the use of services.Replace from the functionality inside UseDefaultServiceProvider():
IWebHost host = WebHost
.CreateDefaultBuilder()
.ConfigureServices(services =>
{
var options = new ServiceProviderOptions();
services.Replace(ServiceDescriptor.Singleton<IServiceProviderFactory<IServiceCollection>>(new CustomServiceProviderFactory(_services, options)));
})
.UseStartup<MyStartup>()
.Build();
In the constructor of my CustomServicesProvider, I also need to remove any IHostedService services or else it appears you enter an infinite loop of the service starting. When creating the service provider, I add everything from the constructor-passed service collection to the local service collection.
class CustomServiceProviderFactory : IServiceProviderFactory<IServiceCollection>
{
private readonly IServiceCollection _baseServices;
private readonly ServiceProviderOptions _options;
public CustomServiceProviderFactory(IServiceCollection baseServices, ServiceProviderOptions options)
{
_baseServices = baseServices;
_options = options;
_baseServices.RemoveAll<IHostedService>();
}
public IServiceCollection CreateBuilder(IServiceCollection services)
{
return services;
}
public IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder)
{
foreach (var service in _baseServices)
{
containerBuilder.Add(service);
}
return containerBuilder.BuildServiceProvider(_options);
}
}
I was then able to create a Controller after adding app.UseRouting() and app.UseEndpoints(...) in my startup class. Injecting IMyService was successfully resolved and I could use it as normal.
You could also test it by just adding app.ApplicationServices.GetRequiredService<IMyService>() in your Startup.Configure() method and see that the correct service is returned.
In ASP.NET Core 2.0, I used ConfigureServices method on Startup class to wire-up Autofac, wrap existing services registrations and add additional ones.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Standard service registrations (ex: services.AddMvc())
// ...
// Autofac
var builder = new ContainerBuilder();
builder.Populate(services); // wrap service registrations
builder.RegisterModule<MyModule>(); // add extra registrations
this.ApplicationContainer = builder.Build();
return new AutofacServiceProvider(this.ApplicationContainer);
}
Since ConfigureService method is void in ASP.NET Core 3.0 and no longer supports return parameter IServiceProvider, how do I wire up Autofac?
ASP.NET Core 3.0 IHostBuilder has extension method UseServiceProviderFactory, which can be used to register AutofacServiceProviderFactory:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => {
webBuilder.UseStartup<Startup>();
})
.UseServiceProviderFactory(new AutofacServiceProviderFactory());
}
Registered provider will automatically wrap all standard services registered in void ConfigureServices(IServiceCollection services) method.
There is no need for lines:
public void ConfigureContainer(ContainerBuilder builder)
{
// ...
builder.Populate(services); // Not needed!
// ...
return new AutofacServiceProvider(this.ApplicationContainer); // Not needed!
}
To add additional Autofac-specific registrations, ConfigureContainer(ContainerBuilder builder) method on startup class can be used:
public partial class Startup
{
public void ConfigureContainer(Autofac.ContainerBuilder builder)
{
builder.RegisterModule<MyModule>();
}
}
Additional info can be found in this Github issue.
I am trying to use latest Autofac with asp.net core 2.2 project. However documentation of autofac seems to be outdated.
I am trying to use autofac without ConfigureContainer:
// ConfigureServices is where you register dependencies. This gets
// called by the runtime before the Configure method, below.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add services to the collection.
services.AddMvc();
// Create the container builder.
var builder = new ContainerBuilder();
// Register dependencies, populate the services from
// the collection, and build the container.
//
// Note that Populate is basically a foreach to add things
// into Autofac that are in the collection. If you register
// things in Autofac BEFORE Populate then the stuff in the
// ServiceCollection can override those things; if you register
// AFTER Populate those registrations can override things
// in the ServiceCollection. Mix and match as needed.
builder.Populate(services);
builder.RegisterType<MyType>().As<IMyType>();
this.ApplicationContainer = builder.Build();
// Create the IServiceProvider based on the container.
return new AutofacServiceProvider(this.ApplicationContainer);
}
but the ConfigureServices method signature is different in asp.net core 2.2
public void ConfigureServices(IServiceCollection services)
{
}
there is not return type.
Anyone knows how to use autofac in asp.net core 2.2?
You need to use this package:
Autofac.Extensions.DependencyInjection
In your program.cs, just use these lines of code:
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureServices(services => services.AddAutofac());
}
In your startup.cs, create a method called
public void ConfigureContainer(ContainerBuilder builder)
{
}
And use "builder" to register whatever you want. Autofac will find this method itself.
You don't need to call any builder.Build() anymore.
Following remarks in order to execute reccurent code with injected values, you need to add:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHostedService<MyHostedService>();
...
}
public class MyHostedService : IHostedService
{
private Timer _timer;
private IInjectedService _myInjectedService;
public MyHostedService(IServiceProvider services)
{
var scope = services.CreateScope();
_myInjectedService = scope.ServiceProvider.GetRequiredService<IInjectedService>();
}
public Task StartAsync(CancellationToken cancellationToken)
{
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
private async void DoWork(object state)
{
_myInjectedService.DoStuff();
}
}
I'm creating a Nuget Package for Asp.Net Core. I want to make it simple to configure. So I decided to give a fluent way approach to add my Nuget to the service collection in ConfigureServices() in Asp.Net Core.
This is what I'm planning to do:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options => options....);
services.AddMyNugetPackageName();
}
Inside my AddMyNugetPackageName() method,
public static IServiceCollection AddMyNugetPackageName(this IServiceCollection services)
{
services
.AddMvc(options => options.ModelBinderProviders.Insert(0, new MyModelBinderProvider()))
.AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());
return services;
}
So now if people start using my Nuget package, will the AddMvc() inside my AddMyNugetPackageName() replace the AddMvc() in their ConfigureServices()? Will this cause any trouble to the end users?
If this will replace the user's AddMvc() in their ConfigureServices() then what is the best approach or how to handle this?
You can use the IConfigureOptions pattern to inject a MvcOptions configuration from your nuget package
public class MyConfigureOptions : IConfigureOptions<MvcOptions>
{
public void Configure(MvcOptions options)
{
options.ModelBinderProviders.Insert(0,new MyModelBinderProvider());
}
}
Use ConfigureOptions to register your options
public static IServiceCollection AddMyNugetPackageName(this IServiceCollection services)
{
services.ConfigureOptions<MyConfigureOptions>();
}
Add your nuget services to the service collection
services.AddMvc();
services.AddMyNugetPackageName();
I need to pass instance of IOptions to a method as parameter, like below: any idea?
services.SetWaitAndRetryPolicy<CustomHttpClient>(); //how to retrieve and pass instance of IOptions<MyConfig>
I follow the links below and at the bottom:
How to read AppSettings values from .json file in ASP.NET Core
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// Add functionality to inject IOptions<T>
services.AddOptions();
// Add our Config object so it can be injected
services.Configure<MyConfig>(Configuration.GetSection("MyConfig"));
services.SetWaitAndRetryPolicy<CustomHttpClient>(); //how to retrieve and pass instance of IOptions<MyConfig>
}
public static class IServiceCollectionExtension
{
public static void SetWaitAndRetryPolicy<T>(this IServiceCollection services, IOptions<MyConfig> config) where T : class
{
}
}
How to get an instance of IConfiguration in asp.net core?
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-2.2
ASP.NET CORE 2.2
If you are using the extension method to register your CustomHttpClient class then you access the options within the configure method.
public static class IServiceCollectionExtension
{
public static void SetWaitAndRetryPolicy<T>(this IServiceCollection services) where T : class
{
services.AddHttpClient<T>((sp, client) =>
{
var options = sp.GetService<IOptions<MyConfig>>();
...
});
}
}
One of the parameters to the configure action is the IServiceProvider. From here you can get access to any of the registered services, in this case the IOptions<MyConfig> settings.