I am writing a .Net Core windows service and here is a snippet of code:
internal static class Program
{
public static async Task Main(string[] args)
{
var isService = !(Debugger.IsAttached || args.Contains("--console"));
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<IntegrationService>();
});
if (isService)
{
await builder.RunAsServiceAsync();
}
else
{
await builder.RunConsoleAsync();
}
}
}
I want to pass some parameters to my service i.e. IntegrationService - how I can send parameters to my service?
Small update on Joelius answer for .Net Core 3
Given an HostedService with this constructor mixing parameters (TimeSpan) and services (ILogger<StatusService>, IHttpClientFactory)
public StatusService(
TimeSpan cachePeriod,
ILogger<StatusService> logger,
IHttpClientFactory clientFactory)
You can in your Startup.cs add it to your HostedService like this :
services.AddHostedService
(serviceProvider =>
new StatusService(
TimeSpan.FromDays(1),
serviceProvider.GetService<ILogger<StatusService>>(),
serviceProvider.GetService<IHttpClientFactory>()));
While the answers above are correct, they do have the downside that you can't use DI in the Services Constructor anymore.
What I did instead was:
class Settings {
public string Url { get; set; }
}
class SomeService : IHostedService {
public SomeService (string instanceId, IOptionsMonitor<Settings> optionsMonitor) {
var settings = optionsMonitor.Get(instanceId);
}
}
services.Configure<Settings>("Instance1", (s) => s.Url = "http://google.com");
services.Configure<Settings>("Instance2", (s) => s.Url = "http://facebook.com");
services.AddSingleton<IHostedService>(x =>
ActivatorUtilities.CreateInstance<SomeService>(x, "Instance1")
);
services.AddSingleton<IHostedService>(x =>
ActivatorUtilities.CreateInstance<SomeService>(x, "Instance2")
);
This creates named settings for each instance and passes the named settings name to the HostedService.
If you want multiple Services with the same Class and different parameters make sure to use AddSingleton instead of AddHostedService as AddHostedService will add only one instance of the same Type which will result in only one instance being started!
What Joelius answered is correct although there is another way of doing this
services.AddSingleton<IHostedService>(provider => new IntegrationService("Test"));
Before .net core 3 you can use a config class which you can inject into the service via DI.
Your config class could look like this:
class IntegrationConfig
{
public int Timeout { get; set; }
public string Name { get; set; }
}
Then you need to add this config to the DI-system:
services.AddSingleton(new IntegrationConfig
{
Timeout = 1234,
Name = "Integration name"
});
In the class IntegrationService you need to add a constructor which takes an object of the config:
public IntegrationService(IntegrationConfig config)
{
// setup with config or simply store config
}
That's basically all you need. It's not the prettiest solution in my opinion and in .net core 3
you can simply use a factory func to add the HostedService but I think something like this is the best choice
if you're on .net core 2.2 or below.
EDIT:
In the comments Kirk Larkin mentions this:
You can emulate the overload. It's just a wrapper around AddTransient(), which of course does support the factory func approach.
For this you might want to look at the current overload which is accessable here:
/// <summary>
/// Add an <see cref="IHostedService"/> registration for the given type.
/// </summary>
/// <typeparam name="THostedService">An <see cref="IHostedService"/> to register.</typeparam>
/// <param name="services">The <see cref="IServiceCollection"/> to register with.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>The original <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection AddHostedService<THostedService>(this IServiceCollection services, Func<IServiceProvider, THostedService> implementationFactory)
where THostedService : class, IHostedService
{
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService>(implementationFactory));
return services;
}
Note that the last commit that changed this file was on June 3rd and is tagged for preview6 and preview7 of .net core 3. Because I've never heard of TryAddEnumerable and am no microsoft employee, I don't know if you can directly translate that.
Just from looking at the current implementation of AddTransient and going down the rabbit hole a few files more, I sadly can't draw the lines well enough to be able to give you the exact functionality you're currently able to get with .net core 3.
The workaround I gave still works and seems acceptable depending on the situation.
Related
I'm writing a console app in Dotnet Core 3.1. It is already configured to use dependency injection using Microsoft.Extensions.DependencyInjection in the following way:
public static class Program
{
public static IServiceProvider ServiceProvider { get; private set; }
public static int Main(string[] args)
{
// ...
ServiceProvider = ConfigureServices().BuildServiceProvider();
// ...
}
public static IServiceCollection ConfigureServices()
{
return new ServiceCollection()
.AddLogging(cfg =>
{
// ...
}
// ...
}
}
I'm trying to set up a simple HTTP API to provide some basic control of the app. I'd like to avoid ASP.Net MVC or anything too heavy. I just need to be able to issue simple instructions and get basic status. It will all be JSON - no need for Razor or anything like that.
I have another two (unfinished) classes:
public class ApiRunner
{
public IWebHost WebHost { get; }
public ApiRunner()
{
WebHost = new WebHostBuilder()
.UseKestrel()
.UseUrls("http://*:5000")
.UseStartup<ApiStartup>()
.Build();
}
public void Start()
{
Task.Run(() => WebHost.Run());
}
public void Stop()
{
WebHost.StopAsync();
}
}
and
public class ApiStartup
{
public void Configure(IApplicationBuilder app)
{
app.UseRouter(r =>
{
r.MapGet("/", async (request, response, routeData) =>
{
response.Headers["content-type"] = "text/plan";
response.WriteAsync("Hello World!");
});
}
}
}
The above does not work unless I add to my ApiStartup class:
public void ConfigureServices(IServiceCollection services)
{
services.AddRouting();
}
but this seems like there are two DI stacks running on top of one another: one for the main program, and one for the API. I did try to add services.AddRouting(); to the main DI configuration in Program.cs, but (1) that didn't work - I got the same exception as when I didn't have it at all, leading me to believe that the API is wanting to use its own DI, and (2) I don't necessarily want to pollute my main DI with an API-specific service that I see as a somewhat separate module.
All I need is a lightweight HTTP server running in my console app that allows me to issue simple commands and get status. Can I please have some pointers how I can achieve this? Thank you.
First, every ASP.NET Core app is a console app and only becomes a web app with DI and relvant services registered.
Second, you are not following the standard pattern for the service registration; there is no need to instantiate a service collection yourself, the WebHostBuilder already does it first. Only register services in the ApiStartup class. So yes, you are registering in two places. See example with the added benefit of logging config demo:
https://github.com/akovac35/Logging.Samples/tree/master/WebApp
As far as I know, if you used the WebHostBuilder, it will add some common service to your application.
The WebHostBuilder build method will register the common service service like the logger, route or else by calling the BuildCommonServices method().
In my opinion, there is no need to create a service ServiceProvider again, since the asp.core has already done the same thing(Startup.cs configure service.). If you don't want other service like razor or else, you could not add the razor service inside the Startup configure service method just use services.AddControllers(); method or you could create a custom api service which you could use for your web api which doesn't contain any razor related result.
Below is some part of the source codes for the webhost.
The web builder source codes:
public IWebHost Build()
{
if (this._webHostBuilt)
throw new InvalidOperationException(Resources.WebHostBuilder_SingleInstance);
this._webHostBuilt = true;
AggregateException hostingStartupErrors;
IServiceCollection serviceCollection1 = this.BuildCommonServices(out hostingStartupErrors);
IServiceCollection serviceCollection2 = serviceCollection1.Clone();
IServiceProvider providerFromFactory = GetProviderFromFactory(serviceCollection1);
.....
WebHost webHost = new WebHost(serviceCollection2, providerFromFactory, this._options, this._config, hostingStartupErrors);
try
{
webHost.Initialize();
return (IWebHost) webHost;
}
catch
{
webHost.Dispose();
throw;
}
IServiceProvider GetProviderFromFactory(IServiceCollection collection)
{
ServiceProvider serviceProvider = collection.BuildServiceProvider();
IServiceProviderFactory<IServiceCollection> service = ((IServiceProvider) serviceProvider).GetService<IServiceProviderFactory<IServiceCollection>>();
if (service == null)
return (IServiceProvider) serviceProvider;
using (serviceProvider)
return service.CreateServiceProvider(service.CreateBuilder(collection));
}
}
The BuildCommonServices:
private IServiceCollection BuildCommonServices(
out AggregateException hostingStartupErrors)
{
.....
ServiceCollection services = new ServiceCollection();
services.AddTransient<IApplicationBuilderFactory, ApplicationBuilderFactory>();
services.AddTransient<IHttpContextFactory, HttpContextFactory>();
services.AddScoped<IMiddlewareFactory, MiddlewareFactory>();
services.AddOptions();
services.AddLogging();
services.AddTransient<IStartupFilter, AutoRequestServicesStartupFilter>();
services.AddTransient<IServiceProviderFactory<IServiceCollection>, DefaultServiceProviderFactory>();
.....
foreach (Action<WebHostBuilderContext, IServiceCollection> servicesDelegate in this._configureServicesDelegates)
servicesDelegate(this._context, (IServiceCollection) services);
return (IServiceCollection) services;
}
How to register the startup.cs:
/// <summary>Specify the startup type to be used by the web host.</summary>
/// <param name="hostBuilder">The <see cref="T:Microsoft.AspNetCore.Hosting.IWebHostBuilder" /> to configure.</param>
/// <param name="startupType">The <see cref="T:System.Type" /> to be used.</param>
/// <returns>The <see cref="T:Microsoft.AspNetCore.Hosting.IWebHostBuilder" />.</returns>
public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder,Type startupType)
{
string name = startupType.GetTypeInfo().Assembly.GetName().Name;
return hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, name).ConfigureServices((Action<IServiceCollection>) (services =>
{
if (typeof (IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
ServiceCollectionServiceExtensions.AddSingleton(services, typeof (IStartup), startupType);
else
ServiceCollectionServiceExtensions.AddSingleton(services, typeof (IStartup), (Func<IServiceProvider, object>) (sp =>
{
IHostingEnvironment requiredService = sp.GetRequiredService<IHostingEnvironment>();
return (object) new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, requiredService.EnvironmentName));
}));
}));
}
/// <summary>Specify the startup type to be used by the web host.</summary>
/// <param name="hostBuilder">The <see cref="T:Microsoft.AspNetCore.Hosting.IWebHostBuilder" /> to configure.</param>
/// <typeparam name="TStartup">The type containing the startup methods for the application.</typeparam>
/// <returns>The <see cref="T:Microsoft.AspNetCore.Hosting.IWebHostBuilder" />.</returns>
public static IWebHostBuilder UseStartup<TStartup>(this IWebHostBuilder hostBuilder)
where TStartup : class
{
return hostBuilder.UseStartup(typeof (TStartup));
}
Tl;dr: I want an options class that uses non-nullable types for its members with no defaults.
C# 8.0 introduces Nullable Reference Types.
I've found that using nullable reference types with the ASP.Net Options Pattern is rather difficult, incomplete, or that I am missing something. I am experiencing the same issue described in this stack over flow post.
We don't want to make Name nullable as then we need to place traditional null checks everywhere (which is against the purpose of non-nullable reference types)
We can't create a constructor to enforce the MyOptions class to be created with a non-nullable name value as the Configure method construct the options instance for us
We can't use the null-forgiving operator trick (public string name { get; set; } = null!;) as then we can't ensure the Name property is set and we can end up with a null in the Name property where this would not be expected (inside the services)
I want an options class that uses non-nullable types for its members with no defaults. The answers in that post end up using nullable types anyway (which I am trying to avoid) or defaults (which I am also trying to avoid).
The comments about the options validation bring up good points and look promising, but it turns out that the Validate method still needs an options object to validate, which defeats the purpose if you already have to pass the options object into it.
public ValidateOptionsResult Validate(string name, MyOptions options)
// Pointless if MyOptions options is being passed in here
This is pointless because I have determined that the only way to enforce an options class with all non-nullable members and no defaults is to have a constructor. Take the code sample below for example.
namespace SenderServer.Options
{
using System;
using Microsoft.Extensions.Configuration;
/// <summary>
/// Configuration options for json web tokens.
/// </summary>
public class JwtOptions
{
/// <summary>
/// The secret used for signing the tokens.
/// </summary>
public String Secret { get; }
/// <summary>
/// The length of time in minutes tokens should last for.
/// </summary>
public Int32 TokenExpirationInMinutes { get; }
/// <summary>
/// Configuration options for json web tokens.
/// </summary>
/// <param name="secret"> The secret used for signing the tokens.</param>
/// <param name="tokenExpirationInMinutes">The length of time in minutes tokens should last for.</param>
public JwtOptions(String secret, Int32 tokenExpirationInMinutes)
{
Secret = secret;
TokenExpirationInMinutes = tokenExpirationInMinutes;
}
/// <summary>
/// Create a JwtOptions instance from a configuration section.
/// </summary>
/// <param name="jwtConfiguration">The configuration section.</param>
/// <returns>A validated JwtOptions instance.</returns>
public static JwtOptions FromConfiguration(IConfiguration jwtConfiguration)
{
// Validate the secret
String? secret = jwtConfiguration[nameof(Secret)];
if (secret == null)
{
throw new ArgumentNullException(nameof(Secret));
}
// Validate the expiration length
if (!Int32.TryParse(jwtConfiguration[nameof(TokenExpirationInMinutes)], out Int32 tokenExpirationInMinutes))
{
throw new ArgumentNullException(nameof(TokenExpirationInMinutes));
}
if (tokenExpirationInMinutes < 0)
{
throw new ArgumentOutOfRangeException(nameof(TokenExpirationInMinutes));
}
return new JwtOptions(secret, tokenExpirationInMinutes);
}
}
}
So if I need a constructor with the parameters for the class, then I can instantiate it on my own with something like:
// Configure the JWT options
IConfiguration jwtConfiguration = Configuration.GetSection("Authentication:JwtOptions");
JwtOptions jwtOptions = JwtOptions.FromConfiguration(jwtConfiguration); // This performs validation as well
but then where do I put the jwtOptions? None of the services.Configure<JwtOptions>(jwtOptions); and variants just take in an already-instantiated object (or at least none that I've seen). And lastly, even if they did, you can't use a dependency-injected options class that doesn't have a public parameter-less constructor.
public JwtService(IOptions<JwtOptions> jwtOptions)
I want an options class that uses non-nullable types for its members with no defaults.
Then unfortunately, Microsoft.Extensions.Options simply isn’t for you. The way the Options works is by having a configuration pipeline of multiple sources, actions, and validators that all work with the same options object. Since there is no explicit beginning of this pipeline, and any configuration source can be at any position in the pipeline, the construction of the options object is handled by the framework and comes before any of the configuration sources is invoked.
This is strictly necessary in order for Options to allow the different kind of use cases it has: You can configure options from configuration (Microsoft.Extensions.Configuration), you can configure them through configuration actions, you can configure them through services that have additional dependencies, etc. And all of those can run in any order.
So since the construction of the object happens by the framework, there also need to be defaults that the options object gets created with: Usually, these are just the type’s default value but you can also choose different defaults through the object’s constructor.
If you want to enforce that specific parameters have been configured after the pipeline, you can use post-configure actions to enforce a configuration, or options validation to validate the configured options. But since this all runs in the pipeline, you need to have defaults.
So basically, if you need to have non-nullable properties without default values, then you cannot use Options. At least not out of the box. If you want to do this in order to safely reference the options in your services, then there would be a different way to approach this: Instead of injecting IOptions<T>, inject a non-nullable options object T directly. And have that provided through a factory:
services.AddSingleton<MySafeOptions>(sp =>
{
var options = sp.GetService<IOptions<MyUnsafeOptions>>();
return new MySafeOptions(options.Value);
});
services.Configure<MyUnsafeOptions>(Configuration.GetSection("MyOptions"));
Another option building on #poke's answer would be to pass the IConfiguration in to your singleton and use the ConfigurationBinder.Bind directly. If you add the correct attributes you no longer need an options object to pass into your singleton. So with a class like this:
public class JwtConfiguration
{
public JwtConfiguration(IConfiguration configuration)
{
ConfigurationBinder.Bind(configuration, this);
// ensure the fields are not null so that the attributes are not
// a lie
_ = this.Secret ?? throw new ArgumentException(
$"{nameof(this.Secret)} required",
nameof(configuration));
_ = this.TokenExpirationInMinutes ?? throw new ArgumentException(
$"{nameof(this.TokenExpirationInMinutes)} required",
nameof(configuration));
}
[DisallowNull]
[NotNull]
public string? Secret { get; set; }
[DisallowNull]
[NotNull]
public int32? TokenExpirationInMinutes { get; set; }
}
Then to wire it together:
.ConfigureServices(
(context, services) => services
.AddSingleton<JwtConfiguration>(
(service) => new JwtConfiguration(
context.Configuration.GetSection("JwtConfig")))
.AddSingleton<JwtService, JwtService>());
And consume:
public class JwtService
{
public JwtService(JwtConfiguration configuration)
{
I'm trying to write an ASP.NET Core 2.2 integration test, where the test setup decorates a specific service that would normally be available to the API as a dependency. The decorator would give me some additional powers I'd need in my integration tests to intercept calls to the underlying service, but I can't seem to properly decorate a normal service in ConfigureTestServices, as my current setup will give me:
An exception of type 'System.InvalidOperationException' occurred in Microsoft.Extensions.DependencyInjection.Abstractions.dll but was not handled in user code
No service for type 'Foo.Web.BarService' has been registered.
To reproduce this, I've just used VS2019 to create a fresh ASP.NET Core 2.2 API Foo.Web project...
// In `Startup.cs`:
services.AddScoped<IBarService, BarService>();
public interface IBarService
{
string GetValue();
}
public class BarService : IBarService
{
public string GetValue() => "Service Value";
}
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly IBarService barService;
public ValuesController(IBarService barService)
{
this.barService = barService;
}
[HttpGet]
public ActionResult<string> Get()
{
return barService.GetValue();
}
}
...and a companion xUnit Foo.Web.Tests project I utilize a WebApplicationfactory<TStartup>...
public class DecoratedBarService : IBarService
{
private readonly IBarService innerService;
public DecoratedBarService(IBarService innerService)
{
this.innerService = innerService;
}
public string GetValue() => $"{innerService.GetValue()} (decorated)";
}
public class IntegrationTestsFixture : WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.ConfigureTestServices(servicesConfiguration =>
{
servicesConfiguration.AddScoped<IBarService>(di
=> new DecoratedBarService(di.GetRequiredService<BarService>()));
});
}
}
public class ValuesControllerTests : IClassFixture<IntegrationTestsFixture>
{
private readonly IntegrationTestsFixture fixture;
public ValuesControllerTests(IntegrationTestsFixture fixture)
{
this.fixture = fixture;
}
[Fact]
public async Task Integration_test_uses_decorator()
{
var client = fixture.CreateClient();
var result = await client.GetAsync("/api/values");
var data = await result.Content.ReadAsStringAsync();
result.EnsureSuccessStatusCode();
Assert.Equal("Service Value (decorated)", data);
}
}
The behavior kind of makes sense, or at least I think it does: I suppose that the little factory lambda function (di => new DecoratedBarService(...)) in ConfigureTestServices cannot retrieve the concrete BarService from the di container because it's in the main service collection, not in the test services.
How can I make the default ASP.NET Core DI container provide decorator instances that have the original concrete type as their inner service?
Attempted solution 2:
I've tried the following:
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.ConfigureTestServices(servicesConfiguration =>
{
servicesConfiguration.AddScoped<IBarService>(di
=> new DecoratedBarService(Server.Host.Services.GetRequiredService<BarService>()));
});
}
But this surprisingly runs into the same problem.
Attempted solution 3:
Asking for IBarService instead, like this:
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.ConfigureTestServices(servicesConfiguration =>
{
servicesConfiguration.AddScoped<IBarService>(di
=> new DecoratedBarService(Server.Host.Services.GetRequiredService<IBarService>()));
});
}
Gives me a different error:
System.InvalidOperationException: 'Cannot resolve scoped service 'Foo.Web.IBarService' from root provider.'
Workaround A:
I can work around the issue in my small repro like this:
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.ConfigureTestServices(servicesConfiguration =>
{
servicesConfiguration.AddScoped<IBarService>(di
=> new DecoratedBarService(new BarService()));
});
}
But this hurts a lot in my actual application, because BarService doesn't have a simple parameterless constructor: it has a moderately complex dependency graph, so I really would like to resolve instances from the Startup's DI container.
PS. I've tried to make this question fully self-contained, but there's also a clone-and-run rep(r)o for your convenience.
Contrary to popular belief, the decorator pattern is fairly easy to implement using the built-in container.
What we generally want is to overwrite the registration of the regular implementation by the decorated one, making use of the original one as a parameter to the decorator. As a result, asking for an IDependency should lead to a DecoratorImplementation wrapping the OriginalImplementation.
(If we merely want to register the decorator as a different TService than the original, things are even easier.)
public void ConfigureServices(IServiceCollection services)
{
// First add the regular implementation
services.AddSingleton<IDependency, OriginalImplementation>();
// Wouldn't it be nice if we could do this...
services.AddDecorator<IDependency>(
(serviceProvider, decorated) => new DecoratorImplementation(decorated));
// ...or even this?
services.AddDecorator<IDependency, DecoratorImplementation>();
}
The above code works once we add the following extension methods:
public static class DecoratorRegistrationExtensions
{
/// <summary>
/// Registers a <typeparamref name="TService"/> decorator on top of the previous registration of that type.
/// </summary>
/// <param name="decoratorFactory">Constructs a new instance based on the the instance to decorate and the <see cref="IServiceProvider"/>.</param>
/// <param name="lifetime">If no lifetime is provided, the lifetime of the previous registration is used.</param>
public static IServiceCollection AddDecorator<TService>(
this IServiceCollection services,
Func<IServiceProvider, TService, TService> decoratorFactory,
ServiceLifetime? lifetime = null)
where TService : class
{
// By convention, the last registration wins
var previousRegistration = services.LastOrDefault(
descriptor => descriptor.ServiceType == typeof(TService));
if (previousRegistration is null)
throw new InvalidOperationException($"Tried to register a decorator for type {typeof(TService).Name} when no such type was registered.");
// Get a factory to produce the original implementation
var decoratedServiceFactory = previousRegistration.ImplementationFactory;
if (decoratedServiceFactory is null && previousRegistration.ImplementationInstance != null)
decoratedServiceFactory = _ => previousRegistration.ImplementationInstance;
if (decoratedServiceFactory is null && previousRegistration.ImplementationType != null)
decoratedServiceFactory = serviceProvider => ActivatorUtilities.CreateInstance(
serviceProvider, previousRegistration.ImplementationType, Array.Empty<object>());
if (decoratedServiceFactory is null) // Should be impossible
throw new Exception($"Tried to register a decorator for type {typeof(TService).Name}, but the registration being wrapped specified no implementation at all.");
var registration = new ServiceDescriptor(
typeof(TService), CreateDecorator, lifetime ?? previousRegistration.Lifetime);
services.Add(registration);
return services;
// Local function that creates the decorator instance
TService CreateDecorator(IServiceProvider serviceProvider)
{
var decoratedInstance = (TService)decoratedServiceFactory(serviceProvider);
var decorator = decoratorFactory(serviceProvider, decoratedInstance);
return decorator;
}
}
/// <summary>
/// Registers a <typeparamref name="TService"/> decorator on top of the previous registration of that type.
/// </summary>
/// <param name="lifetime">If no lifetime is provided, the lifetime of the previous registration is used.</param>
public static IServiceCollection AddDecorator<TService, TImplementation>(
this IServiceCollection services,
ServiceLifetime? lifetime = null)
where TService : class
where TImplementation : TService
{
return AddDecorator<TService>(
services,
(serviceProvider, decoratedInstance) =>
ActivatorUtilities.CreateInstance<TImplementation>(serviceProvider, decoratedInstance),
lifetime);
}
}
This seems like a limitation of the servicesConfiguration.AddXxx method which will first remove the type from the IServiceProvider passed to the lambda.
You can verify this by changing servicesConfiguration.AddScoped<IBarService>(...) to servicesConfiguration.TryAddScoped<IBarService>(...) and you'll see that the original BarService.GetValue is getting called during the test.
Additionally, you can verify this because you can resolve any other service inside the lambda except the one you're about to create/override. This is probably to avoid weird recursive resolve loops which would lead to a stack-overflow.
There's actually a few things here. First, when you register a service with an interface, you can only inject that interface. You are in fact saying: "when you see IBarService inject an instance of BarService". The service collection doesn't know anything about BarService itself, so you cannot inject BarService directly.
Which leads to the second issue. When you add your new DecoratedBarService registration, you now have two registered implementations for IBarService. There's no way for it to know which to actually inject in place of IBarService, so again: failure. Some DI containers have specialized functionality for this type of scenario, allowing you to specify when to inject which, Microsoft.Extensions.DependencyInjection does not. If you truly need this functionality, you can use a more advanced DI container instead, but considering this is only for testing, that would like be a mistake.
Third, you have a bit of a circular dependency here, as DecoratedBarService itself takes a dependency on IBarService. Again, a more advanced DI container can handle this sort of thing; Microsoft.Extensions.DependencyInjection cannot.
Your best bet here is to use an inherited TestStartup class and factor out this dependency registration into a protected virtual method you can override. In your Startup class:
protected virtual void AddBarService(IServiceCollection services)
{
services.AddScoped<IBarService, BarService>();
}
Then, where you were doing the registration, call this method instead:
AddBarService(services);
Next, in your test project create a TestStartup and inherit from your SUT project's Startup. Override this method there:
public class TestStartup : Startup
{
protected override void AddBarService(IServiceCollection services)
{
services.AddScoped(_ => new DecoratedBarService(new BarService()));
}
}
If you need to get dependencies in order to new up any of these classes, then you can use the passed in IServiceProvider instance:
services.AddScoped(p =>
{
var dep = p.GetRequiredService<Dependency>();
return new DecoratedBarService(new BarService(dep));
}
Finally, tell your WebApplicationFactory to use this TestStartup class. This will need to be done via the UseStartup method of the builder, not the generic type param of WebApplicationFactory. That generic type param corresponds to the entry point of the application (i.e. your SUT), not which startup class is actually used.
builder.UseStartup<TestStartup>();
All the other answers were very helpful:
#ChrisPratt clearly explains the underlying problem, and offers a solution where Startup makes the service registration virtual and then overrides that in a TestStartup that is forced upon the IWebHostBuilder
#huysentruitw answers as well that this is a limitation of the underlying default DI container
#KirkLarkin offers a pragmatic solution where you register BarService itself in Startup and then use that to overwrite the IBarService registration completely
And still, I'd like to offer yet another answer.
The other answers helped me find the right terms to Google for. Turns out, there is the "Scrutor" NuGet package which adds the needed decorator support to the default DI container. You can test this solution yourself as it simply requires:
builder.ConfigureTestServices(servicesConfiguration =>
{
// Requires "Scrutor" from NuGet:
servicesConfiguration.Decorate<IBarService, DecoratedBarService>();
});
Mentioned package is open source (MIT), and you can also just adapt only the needed features yourself, thus answering the original question as it stood, without external dependencies or changes to anything except the test project:
public class IntegrationTestsFixture : WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.ConfigureTestServices(servicesConfiguration =>
{
// The chosen solution here is adapted from the "Scrutor" NuGet package, which
// is MIT licensed, and can be found at: https://github.com/khellang/Scrutor
// This solution might need further adaptation for things like open generics...
var descriptor = servicesConfiguration.Single(s => s.ServiceType == typeof(IBarService));
servicesConfiguration.AddScoped<IBarService>(di
=> new DecoratedBarService(GetInstance<IBarService>(di, descriptor)));
});
}
// Method loosely based on Scrutor, MIT licensed: https://github.com/khellang/Scrutor/blob/68787e28376c640589100f974a5b759444d955b3/src/Scrutor/ServiceCollectionExtensions.Decoration.cs#L319
private static T GetInstance<T>(IServiceProvider provider, ServiceDescriptor descriptor)
{
if (descriptor.ImplementationInstance != null)
{
return (T)descriptor.ImplementationInstance;
}
if (descriptor.ImplementationType != null)
{
return (T)ActivatorUtilities.CreateInstance(provider, descriptor.ImplementationType);
}
if (descriptor.ImplementationFactory != null)
{
return (T)descriptor.ImplementationFactory(provider);
}
throw new InvalidOperationException($"Could not create instance for {descriptor.ServiceType}");
}
}
There's a simple alternative to this that just requires registering BarService with the DI container and then resolving that when performing the decoration. All it takes is updating ConfigureTestServices to first register BarService and then use the instance of IServiceProvider that's passed into ConfigureTestServices to resolve it. Here's the complete example:
builder.ConfigureTestServices(servicesConfiguration =>
{
servicesConfiguration.AddScoped<BarService>();
servicesConfiguration.AddScoped<IBarService>(di =>
new DecoratedBarService(di.GetRequiredService<BarService>()));
});
Note that this doesn't require any changes to the SUT project. The call to AddScoped<IBarService> here effectively overrides the one provided in the Startup class.
Creating Dependency Injection with ASP.NET Core is fairly easy. The documentation explains it very well here and this guy has a killer video to explain it.
However, I want to do the same thing with my ASP.NET MVC 5 project. How can handle dependency injection with ASP.MVC 5?
Also, is Dependency injection limited to controllers only or can it work with any class?
In ASP.Net MVC you can use the .Net Core DI from NuGet rather than one of the third-party alternatives:-
using Microsoft.Extensions.DependencyInjection
For the MVC Start/Configuration class:-
public void Configuration(IAppBuilder app)
{
// We will use Dependency Injection for all controllers and other classes, so we'll need a service collection
var services = new ServiceCollection();
// configure all of the services required for DI
ConfigureServices(services);
// Configure authentication
ConfigureAuth(app);
// Create a new resolver from our own default implementation
var resolver = new DefaultDependencyResolver(services.BuildServiceProvider());
// Set the application resolver to our default resolver. This comes from "System.Web.Mvc"
//Other services may be added elsewhere through time
DependencyResolver.SetResolver(resolver);
}
My project uses Identity User and I've replaced the OWIN start-up configuration to follow a service-based approach instead. The default Identity User classes use static factory methods to create instances. I've moved that code into the constructors and relied on DI to provide the appropriate injection. It is still work in progress but here is where I am at:-
public void ConfigureServices(IServiceCollection services)
{
//====================================================
// Create the DB context for the IDENTITY database
//====================================================
// Add a database context - this can be instantiated with no parameters
services.AddTransient(typeof(ApplicationDbContext));
//====================================================
// ApplicationUserManager
//====================================================
// instantiation requires the following instance of the Identity database
services.AddTransient(typeof(IUserStore<ApplicationUser>), p => new UserStore<ApplicationUser>(new ApplicationDbContext()));
// with the above defined, we can add the user manager class as a type
services.AddTransient(typeof(ApplicationUserManager));
//====================================================
// ApplicationSignInManager
//====================================================
// instantiation requires two parameters, [ApplicationUserManager] (defined above) and [IAuthenticationManager]
services.AddTransient(typeof(Microsoft.Owin.Security.IAuthenticationManager), p => new OwinContext().Authentication);
services.AddTransient(typeof(ApplicationSignInManager));
//====================================================
// ApplicationRoleManager
//====================================================
// Maps the rolemanager of identity role to the concrete role manager type
services.AddTransient<RoleManager<IdentityRole>, ApplicationRoleManager>();
// Maps the role store role to the implemented type
services.AddTransient<IRoleStore<IdentityRole, string>, RoleStore<IdentityRole>>();
services.AddTransient(typeof(ApplicationRoleManager));
//====================================================
// Add all controllers as services
//====================================================
services.AddControllersAsServices(typeof(Startup).Assembly.GetExportedTypes()
.Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
.Where(t => typeof(IController).IsAssignableFrom(t)
|| t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)));
}
The Account Controller class has the single constructor:-
[Authorize]
public class AccountController : Controller
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
private RoleManager<IdentityRole> _roleManager;
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, RoleManager<IdentityRole> roleManager)
{
UserManager = userManager;
SignInManager = signInManager;
RoleManager = roleManager;
}
}
My Default Dependency Resolver:
/// <summary>
/// Provides the default dependency resolver for the application - based on IDependencyResolver, which hhas just two methods
/// </summary>
public class DefaultDependencyResolver : IDependencyResolver
{
/// <summary>
/// Provides the service that holds the services
/// </summary>
protected IServiceProvider serviceProvider;
/// <summary>
/// Create the service resolver using the service provided (Direct Injection pattern)
/// </summary>
/// <param name="serviceProvider"></param>
public DefaultDependencyResolver(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
/// <summary>
/// Get a service by type - assume you get the first one encountered
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
public object GetService(Type serviceType)
{
return this.serviceProvider.GetService(serviceType);
}
/// <summary>
/// Get all services of a type
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
public IEnumerable<object> GetServices(Type serviceType)
{
return this.serviceProvider.GetServices(serviceType);
}
}
For this answer I downloaded a Microsoft Example of WebApi project as a basis for the example and added DI services to it as follows,
Update the Target Framework to 4.6.1
NuGet the DI package :- Microsoft.Extensions.DependencyInjection
After the standard MapHttpRoute configuration, add code to register which services you need
using's
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using Microsoft.Extensions.DependencyInjection;
using System.Web.Http.Dependencies;
using ProductsApp.Controllers;
WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// create the DI services and make the default resolver
var services = new ServiceCollection();
services.AddTransient(typeof(DefaultProduct));
services.AddTransient(typeof(ProductsController));
var resolver = new MyDependencyResolver(services.BuildServiceProvider());
config.DependencyResolver = resolver;
}
}
DefaultProduct
public class DefaultProduct : ProductsApp.Models.Product
{
public DefaultProduct()
{
this.Category = "Computing";
this.Id = 999;
this.Name = "Direct Injection";
this.Price = 99.99M;
}
}
MyDependencyResolver
/// <summary>
/// Provides the default dependency resolver for the application - based on IDependencyResolver, which hhas just two methods.
/// This is combined dependency resolver for MVC and WebAPI usage.
/// </summary>
public class MyDependencyResolver : System.Web.Mvc.IDependencyResolver, System.Web.Http.Dependencies.IDependencyResolver
{
protected IServiceProvider serviceProvider;
protected IServiceScope scope = null;
public MyDependencyResolver(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public MyDependencyResolver(IServiceScope scope)
{
this.scope = scope;
this.serviceProvider = scope.ServiceProvider;
}
public IDependencyScope BeginScope()
{
return new MyDependencyResolver(serviceProvider.CreateScope());
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
scope?.Dispose();
}
public object GetService(Type serviceType)
{
return this.serviceProvider.GetService(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return this.serviceProvider.GetServices(serviceType);
}
}
ServiceProviderExtensions
public static class ServiceProviderExtensions
{
public static IServiceCollection AddControllersAsServices(this IServiceCollection services, IEnumerable<Type> serviceTypes)
{
foreach (var type in serviceTypes)
{
services.AddTransient(type);
}
return services;
}
}
I then amended the existing controller to take the DI type (note there is just the one ctor)
using ProductsApp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace ProductsApp.Controllers
{
public class ProductsController : ApiController
{
DefaultProduct _dp = null;
public ProductsController(DefaultProduct dp)
{
_dp = dp;
//
products.Add(dp);
}
List<Product> products = new List<Product>()
{
new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
};
public IEnumerable<Product> GetAllProducts()
{
return products;
}
public IHttpActionResult GetProduct(int id)
{
var product = products.FirstOrDefault((p) => p.Id == id);
if (product == null)
{
return NotFound();
}
return Ok(product);
}
}
}
My Default Dependency Resolver
/// <summary>
/// Provides the default dependency resolver for the application - based on IDependencyResolver, which hhas just two methods
/// </summary>
public class DefaultDependencyResolver : IDependencyResolver
{
/// <summary>
/// Provides the service that holds the services
/// </summary>
protected IServiceProvider serviceProvider;
/// <summary>
/// Create the service resolver using the service provided (Direct Injection pattern)
/// </summary>
/// <param name="serviceProvider"></param>
public DefaultDependencyResolver(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
/// <summary>
/// Get a service by type - assume you get the first one encountered
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
public object GetService(Type serviceType)
{
return this.serviceProvider.GetService(serviceType);
}
/// <summary>
/// Get all services of a type
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
public IEnumerable<object> GetServices(Type serviceType)
{
return this.serviceProvider.GetServices(serviceType);
}
}
I recommend you use Autofac, there are anothers fwk like unity, ninject, the benchmarks autofac has excelent perfomance.
http://www.palmmedia.de/blog/2011/8/30/ioc-container-benchmark-performance-comparison
Here is the integration with MVC (and works with all class)
http://docs.autofac.org/en/latest/integration/mvc.html
The simplest way to implements Dependency Injection in ASP.NET MVC 5 is to use the tool developed by Microsoft itself, called Unity.
You can find many resources on the internet about it, and you can start by reading the official documentation available here: Developer's Guide to Dependency Injection Using Unity
Also, is Dependency injection limited to controllers only or can it work with any class?
It works with any class, in any project, as long as you register the Interface related to the Implementation (if you want to take profit of the IoC pattern), all you have to do then is to add the Interface instantiation in your constructor.
In this video a Microsoft MVP demos dependency injection in MVC5 with AutoFac. Very clear explanation on how to set it up:
Dependency Injection MVC5 Demo
Source code is available on GitHub
From here https://scottdorman.blog/2016/03/17/integrating-asp-net-core-dependency-injection-in-mvc-4/
this line saved me.
services.AddControllersAsServices(typeof(Startup).Assembly.GetExportedTypes()
.Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
.Where(t => typeof(IController).IsAssignableFrom(t)
|| t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)));
I recommend using Windsor, by installing the nuget package Castle Windsor MVC Bootstrapper, then you can create a service that implements IWindsorInstaller, something like this:
public class ServiceRegister : IWindsorInstaller
{
public void Install(Castle.Windsor.IWindsorContainer container,
Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
SomeTypeRequiredByConstructor context = new SomeTypeRequiredByConstructor ();
container.Register(
Component
.For<IServiceToRegister>()
.ImplementedBy<ServiceToRegister>().
DependsOn(Dependency.OnValue<SomeTypeRequiredByConstructor>(context))//This is in case your service has parametrize constructoe
.LifestyleTransient());
}
}
Then inside your controller something like this:
public class MyController
{
IServiceToRegister _serviceToRegister;
public MyController (IServiceToRegister serviceToRegister)
{
_serviceToRegister = serviceToRegister;//Then you can use it inside your controller
}
}
And by default the library will handle sending the right service to your controller by calling the install() of ServiceRegister at start up because it implements IWindsorInstaller
Having started from this thread to figure out how to use Microsoft.Extensions.DependencyInjection in my ASP.NET MVC 5 project, and reading and trying and failing, I finally came up with a solution that I wanted to shamelessly offer to the rest of you.
I pieced together a gist from David Fowler, the example code from Scott Dorman, and added in a bit of my own spice to create library that allows you to simulate ASP.NET Core's Startup in ASP.NET MVC "Classic".
For more information, please take a look at the GitHub repository for Arex388.AspNet.Mvc.Startup. If you're interested you can also read through my blog post about it, here (if it doesn't load, refresh until it does, the server's been giving me troubles and I haven't had time to investigate...). Hope it helps someone!
I have two objects that should be registered together, and also share the same lifetime scoping. I would like to provide a registration extension to encapsulate this and retain the registration fluency, but I need some help. Here's the situation I'm in:
public static IRegistrationBuilder<?, ?, ?>
RegisterChannel<T>(this ContainerBuilder builder, Func<IComponentContext, ChannelFactory<T>> #delegate)
{
// channelfactory and sharedchannel should have same lifetime configuration
var channelfactoryreg = builder.Register(c => #delegate(c));
var sharereg = builder.RegisterType<Wcf.SharedChannel<T>>();
// is it possible to combine them and return?
return ???;
}
How do I fill in the blanks so that I can write (e.g.) builder.RegisterTwo().SingleInstance()? Is it possible to directly or indirectly "union" two IRegistrationBuilderTLAR objects, so that configuring the result configures all the underlying registrations, or is there another way to do this?
More generally: is there a primer out there for working with the Autofac internals?
Thanks for your time.
You should use a module to encapsulate registrations that make sense together.
IMHO, you should call .SingleInstance() in both registration. If they are two services they should be configured twice. If they share more commonalities in registration, you could register them with Assembly Scanning.
I find that I make increasing use of the IRegistrationSource interface for grouping together any related registrations. In your case, they would enable you to use the same registration code for an arbitrary set of types:
public class MyRegistrationSource : IRegistrationSource
{
/// <summary>
/// Gets a value indicating whether the registrations provided by this source are 1:1 adapters on top
/// of other components (I.e. like Meta, Func or Owned.)
/// </summary>
public bool IsAdapterForIndividualComponents
{
get
{
return false;
}
}
/// <summary>
/// Retrieve registrations for an unregistered service, to be used
/// by the container.
/// </summary>
/// <param name="service">The service that was requested.</param>
/// <param name="registrationAccessor">A function that will return existing registrations for a service.</param>
/// <returns>
/// Registrations providing the service.
/// </returns>
public IEnumerable<IComponentRegistration> RegistrationsFor(
Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var swt = service as IServiceWithType;
if (swt != null)
{
// Register requested service types that pass some test or other
if (swt.ServiceType.HasAttribute<SomeAttribute>(true) ||
typeof(SomeType).IsAssignableFrom(swt.ServiceType))
{
var registration = RegistrationBuilder.ForType(swt.ServiceType)
.InstancePerDependency()
.CreateRegistration();
yield return registration;
}
}
}
}
Gosh, I hate answering my own Q, but all suggested alternatives seemed off-point or too complicated. Nick's comment came the closest to engaging, but only succeeded in scaring me off Autofac internals entirely ;). I investigated, but that was overkill for my situation.
I ended up splitting the difference (as implied in my comment above), abandoning the fluent interface, but still allowing for flexible continuation of the registration by accepting the config into the method itself:
public static void RegisterSharedChannel<T>(this ContainerBuilder builder, Func<IComponentContext, ChannelFactory<T>> #delegate,
Action<Autofac.Builder.IRegistrationBuilder<object, Autofac.Builder.IConcreteActivatorData, Autofac.Builder.SingleRegistrationStyle>> config)
{
builder.Register(c => c.Resolve<Wcf.ISharedChannel<T>>().GetChannel()).ExternallyOwned();
//would be really nice to be able to retain the fluency of the interface, but: http://stackoverflow.com/questions/8608415/fluent-configuration-of-multiple-registrations
//this should suffice for now...
var facreg = builder.Register(c => #delegate(c));
var sharereg = builder.RegisterType<Wcf.SharedChannel<T>>().AsImplementedInterfaces();
config(facreg);
config(sharereg);
}
The calling syntax isn't as pretty (e.g.:
builder.RegisterSharedChannel(c => BuildChannelFactory(...), r => r.SingleInstance());
but all (most of?) the flexibility is still there.