Dependency injection failure in ASP.NET Core RC2 - c#

I am unable to start up my asp.net core application after trying to port it to 1.0 RC2 from 1.0 RC1. The startup error I get is this:
Unhandled Exception: System.Exception: Could not resolve a service of type 'Microsoft.Extensions.Configuration.IConfiguration' for the parameter 'configuration' of method 'Configure' on type 'MYCOMPANY.MYTHING.Api.Startup'.
at Microsoft.AspNetCore.Hosting.Startup.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
at Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
at MYCOMPANY.MYTHING.Api.Program.Main(String[] args)
I can't figure out how this is supposed to work.
The parts I do understand are:
My startup.cs code looks correct to me, in Startup(IHostingEnvironment env) I create a ConfigurationBuilder and set Startup.Configuration equal to builder.Build(). The ConfigureServices method is also reached and Configuration object exists there.
When the host.Run() code is reached in Program.cs, I get a crash.
Configure(app,env,loggerFactory,configuration,respository...) method is NEVER reached.
In RC1 there used to be a line like this:
services.AddInstance<IConfiguration>(Configuration);
In RC2 that doesn't exist anymore in a new application (I am reading a new application which works and trying to compare line by line).
I have been looking for how to make this work, and how to understand dependency injection of the IConfigurationRoot, but I don't see any attribute or code that seems to be responsible for this injection.
Update: It seems that I was using some strange pattern in my RC1 code that is no longer supported in RC2. After I removed some additional parameters from my Configure() method it was once again invoked by the .net core startup code.

It looks like you have already fixed the issue, but the restriction with respect to the configure method is not as hard and fast as you suggest.
The restriction is you must have a public, instance or static method named ConfigureDevelopment, where Development is the environment name, or a method named Configure, which will be used if an environment specific configure method does not exist.
Obviously, you shouldn't need to inject your IConfiguration in to the Configure method as it will be set on your Startup class, but if you need something else injected (and you've configured it in ConfigureServices then you can do so. For example, the following would be perfectly valid.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ISuperSecretClass, SuperSecretClass>();
}
public void Configure(IApplicationBuilder app, ISuperSecretClass instance)
{
//do something with instance
}
For reference, I checked in the StartupLoader source for the Configure restrictions.

If you want to inject Configuration instance you can do:
services.AddSingleton((provider)=>
{
return Configuration;
});

Related

SimpleInjector Creates Controller Multiple Times and Creates Unnecessary Controller

I noticed a strange behavior when using SimpleInjector with ASP.NET Web APIs, so I reduced it to its basics. Steps to reproduce:
create ASP.NET Core's default Web API project. I used .NET Core 3.1, which is out of support now but I don't think that has anything to do with the issue. You will have a single controller: WeatherForecastController
create another API controller descending from ControllerBase with a default ctor. I have the default Index() method in it but changed the return value to string.
put a breakpoint in the ctor of both controllers
run the app, and call the weather/get endpoint: the breakpoint shows that the weather controller is created once, as expected. The other controller's code is untouched, also as expected.
add these SimpleInjector nuget packages to the project:
SimpleInjector: I add it directly because this is what the documentation suggests
SimpleInjector.Integration.AspNetCore.Mvc.Core
initialize SI in Startup.cs as follows
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSimpleInjector(container, options =>
{
options
.AddAspNetCore()
.AddControllerActivation();
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.ApplicationServices.UseSimpleInjector(container);
// the rest of the default code
}
// instantiated in ctor in my code but I do it here for brevity
private readonly Container container = new Container();
run the app, and call the weather/get endpoint: the breakpoints show that the second controller is created once, and the weather controller is created twice. Then the weather/get endpoint is called.
The issue is the above unexpected controller creation. The same thing happens in my actual project as in this reproduction test project. The unnecessary controller creation concerns me. Why is that, and how can it be avoided? This surely cannot be normal but I cannot imagine what I could have mistaken in this simple configuration.
EDIT: this strange behavior occurs only for the first weather/get call
Simple Injector contains an auto verification feature which will, when the first component is resolved, trigger verification and construct all types known to Simple Injector as a verification measure. That means that at the first call, its expected for the controller to be created once. Every following request, the controller will be created just once.
When the other controller is created just once, it could mean that it isn't created by Simple Injector. You can inspect this by placing a break point in the constructor and inspecting the stack trace. Why this is the case is unclear to me from the code you posted.
More information about verification and auto-verification can be found here.

How and Who calling the ConfigureServices and Configure method of startup class in .net core

As everyone know that Main method of Program.cs is the entry point of application. As you can see in the .net core default code created when we create any project.
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
And in startup class we have two In-build method i.e ConfigureServices and Configure as shown below.
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
}
I just want to know that how these methods is invoked. As we know that to invoked any method we have to create a object of the class and using that object we can execute the method, then how these(ConfigureServices and Configure) methods execute without creating any object.
Please help me out to understand in deep.
As an overly simplified explanation,
WebHost.CreateDefaultBuilder(args)
method call returns an object for default webhost builder which implements IWebHostBuilder. Then UseStartup() extension method configures created webhost builder using the Startup class you provide. UseStartup() method can identify your startup class since you specify as the generic argument. UseStartup() cantains the implementation to invoke ConfigureServices and Configure methods which you provide by using the reflection. Note that to invoke a method one can use reflection also other than creating an instance of a class.
Those methods are called by the ASP.NET Core framework. Note that in your Main method you have this call:
.UseStartup<Startup>()
Where you specify the class to use for the startup, in this case Startup. The ConfigureServices and Configure methods are called by convention. If those methods are found in the class specified in the UseStartup extension, they will be called.
Lets take a step back and try to understand what are we trying to do here -
We are setting Up the Host and Host originates the request pipeline. Now both Configure and Configure services are like the rooms where the requests are configured through middleware or services.
Now as you built the Host and Ran it from Program.cs and injected StartUp as already said, using reflection, stitches up the Request Pipleline origin and configured with these two methods so that it can handle all the requests
Try to comment out both of these methods and you'll get exceptions as a result:
"System.InvalidOperationException: A public method named 'ConfigureDevelopment' or 'Configure' could not be found in the 'Your_WebApplication.Startup' type".
System.InvalidOperationException: 'Unable to find the required services. Please add all the required services by calling 'IServiceCollection.AddAuthorization' inside the call to 'ConfigureServices(...)' in the application startup code.'
Configure - This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
ConfigureServices - This method gets called by the runtime. Use this method to add services to the container.

How to override DI registration from other container in ASP.NET Core integration test

I have the following registration in asp.net core startup.cs file:
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterType<UserService>().As<IUserService>();
}
This is to configure the Autofac container. And I have another integration test project where I have a CustomWebApplicationFactory class and I'm trying to replace the implementation of IUserService interface.
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
services.AddSingleton<IUserService, TestUsersService>();
});
}
It seems not work when I debug the test project and the implementation of IUserService is still UserService.
I tried registering UserService directly in Startup.ConfigureServices method with ASP.NET Core built-in IServiceCollection and it worked when debugging:
services.AddSingleton<IUserService, UserService>();
So, how can I fix the problem when I use Autofac as the IoC container and the integration test project will work properly as I expect?
You are likely running into an order of operations problem. In general, last in wins. That goes for Autofac and for the base Microsoft DI container.
Assuming you've read through the docs on Autofac ASP.NET Core integration you'll see that when ConfigureContainer is in place the order of operations is roughly:
WebHost specific ConfigureServices
Startup class ConfigureServices
Startup class ConfigureContainer
When adding ConfigureTestServices in place, it looks like (though I haven't stepped through) it runs after the WebHost and Startup class ConfigureServices... but it's still running before ConfigureContainer.
This would be easy enough to test - create a service interface with three different implementations. Register a different implementation at each level. Resolve the interface in a controller. Which one did you get? That was the last one to run. Now remove that registration from the app and try again. What's the next one you get? That's the second to the last one. And so on.
Autofac takes a pre-built IServicesCollection and cycles through it, adding that to the native Autofac container. Once that's happened, it doesn't matter if you modify the collection. Autofac has no control over the order of the execution of the startup mechanism in ASP.NET Core; it just knows that ASP.NET Core says, "Here's the final service collection to go ahead and import!" If that's not happening at the right stage, you'll have to do one of two things:
Move the registration you need to override out of ConfigureContainer and into one of the ConfigureServices methods, using the Microsoft registration language instead of native Autofac.
Perform the override some other way like using an ASPNETCORE_ENVIRONMENT setting of Test and providing a ConfigureTestContainer method. (Examples of environment specific registration methods are in the docs.)
When using ContainerBuilder like this:
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterType<UserService>().As<IUserService>();
}
You must use ConfigureTestContainer rather than ConfigureTestServices as follows:
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestContainer<ContainerBuilder>(containerBuilder =>
{
containerBuilder.RegisterType<TestUsersService>().As<IUserService>();
});
}
This is executed after the call to ConfigureContainer and will correctly override IUserService with TestUsersService
For the ones coming from google I'd like to add to Michael's excellent answer that ConfigureTestContainer does not work with the generic host recommended over the web host by Microsoft as of .NET Core 3.0. There is however a workaround proposed by Alistair Evans from the Autofac team. Unfortunately, it relies on the deprecated IStartupConfigureContainerFilter that will probably be removed in .NET 5.0.
This means that currently in .NET 5.0 there is no way to mock dependencies injected by an external DI container in integration tests when using the generic host.
Luckily, David Fowler from the ASP.NET team is looking into the issue.

Multitenancy in ASP.NET Core 2.0+

Bakground:
I want to develop a multi-tenant application in ASP.NET Core and have been looking into Ben Fosters Saaskit library which seems to provide good solutions for common problems in multitenancy applications.
Problem:
The SaasKit have a UsePerTenant method which is nice for doing different things per-request depending on current tenant.
My goal is to use the UsePerTenant method combined with different IOptions objects injected via dependency injection. This can be used in the authentication middleware like
AddAuthentication().AddCookie(..).AddOpenIdConnect(...)
Which is configured in the ConfigureServices method in Startup.cs
public class Startup
{
// Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
...
}
// Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
{
...
}
}
I can’t make the authentication middleware in ASP.NET 2.0+ use different IOptions objects per-request since the ConfigureServices method in the Startup.cs file only runs once every application startup and the UsePerTenant method should be used in the Configure method which is running for each incoming/outgoing request in the ASP.NET pipeline.
Question:
How to dynamically change cookie and OpenID Connect options in the ConfigureServices method based on current tenant?
I have found a good way to get per tenant options for any type of ASP.NET Core options, including cookie or openID Connect. I have wrapped this up into a framework called Finbuckle.MultiTenant.
It basically boils down to a setup that looks like this:
services.AddMultiTenant().
WithInMemoryStore()).
WithRouteStrategy().
WithPerTenantOptionsConfig<CookieAuthenticationOptions>((o, tenantContext) => o.Cookie.Name += tenantContext.Id);
See my here for more information if you are curious: https://www.finbuckle.com/MultiTenant
The following PR provides a solution for the above question.
https://github.com/saaskit/saaskit/pull/96
The PR have been merged with the "master" branch now.
It wasn't merged yet (November 2018)

How to use a controller in another assembly in ASP.NET Core MVC 2.0?

For the sake of modularity, I have created some controllers in different assemblies. Each assembly represents a bounded context (a module, a sub-system, a division, etc.) of the overall system.
Each module's controllers are developed by someone that knows nothing about other modules, and a central orchestrator is about to cover all these modules in one single application.
So, there is this module called school, and it has a TeacherController in it. The output of it is Contoso.School.UserService.dll.
The main orchestrator is called Education and it has a reference to Contoso.School.UserService.dll.
My program.cs is:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args).UseKestrel()
.UseStartup<Startup>()
.Build();
Yet for the routes of teacher controller, I get 404. How to use controllers in other assemblies?
Inside the ConfigureServices method of the Startup class you have to call the following:
services.AddMvc().AddApplicationPart(assembly).AddControllersAsServices();
Where assembly is the instance Assembly representing Contoso.School.UserService.dll.
You can load it either getting it from any included type or like this:
var assembly = Assembly.Load("Contoso.School.UserService");
For .NET Core 3.0 the API has been slightly changed and the easiest way to register controllers from external assembly in Startup.cs looks like:
public void ConfigureServices(IServiceCollection services)
{
var assembly = typeof(**AnyTypeFromRequiredAssembly**).Assembly;
services.AddControllers()
.PartManager.ApplicationParts.Add(new AssemblyPart(assembly));
}
I'm trying to resolve the controllers while migrating legacy unit tests from .NET Framework to aspnet core 3.1, this is the only way I got it working:
var startupAssembly = typeof(Startup).Assembly;
var services = new ServiceCollection();
// Add services
...
// Add Controllers
services
.AddControllers()
.AddApplicationPart(startupAssembly)
.AddControllersAsServices();
If I change the order of the three last lines it does not work.
Avoid doing this for unit testing unless you really have to, i.e. for legacy reasons. If you are not working with legacy code you are probably looking for integration tests.
There is nothing wrong with the above answers, I just use a 1 liners as I know the class that is included in the external assembly that I'd like to load.
The bellow sample comes from the ASP-WAF firewall and is used to load reporting endpoints and dashboard web pages in an existing web application in .net Core 3.1.
services.AddMvc(options =>
{
options.Filters.Add<Walter.Web.FireWall.Filters.FireWallFilter>();
options.Filters.Add<Walter.Web.FireWall.Filters.PrivacyPreferencesFilter>();
}).AddApplicationPart(Assembly.GetAssembly(typeof(Walter.Web.FireWall.DefaultEndpoints.ReportingController)));
so to answer your question, let's assume that the TeacherController is in namespace Contoso.School.UserService your implementation would be:
services.AddMvc(options =>
{
//any option }).AddApplicationPart(Assembly.GetAssembly(typeof(Contoso.School.UserService.TeacherController )));
or, if you do not have any options then just ignore them and use this:
services.AddMvc() .AddApplicationPart(Assembly.GetAssembly(typeof(Contoso.School.UserService.TeacherController)));
If you are not sure about the class in the assembly then us intelisence in your code starting with the namespace and look for a type to use or open object browser in visual studio
Let me know if you have any issues.
Martin above has the answer, thanks. However it is not immediately obvious how you pass an assembly to Startup.ConfigureServices. How I achieved this was... in the code where I create and start the webHost I call IWebHostBuilder.ConfigureServices and give it something containing the assembly (in my case a custom interface called IOutputProcess)
_webHost = CreateWebHostBuilder().ConfigureServices(e => e.AddSingleton(_outputProcess)).Build();
_webHost.Start();
then in my Startup.ConfigureServices I pull that instance back out of the IServiceCollection with...
public void ConfigureServices(IServiceCollection services)
{
var sp = services.BuildServiceProvider();
var outputProcess = sp.GetService<IOutputProcess>();
services.AddMvc().AddApplicationPart(outputProcess.ControllerAssembly).AddControllersAsServices();
}
I doubt instantiating a service provider purely for this purpose is the cleanest way to do things, but it does work. (I'm open to better ideas)

Categories

Resources