Weird exception when trying to use Dependency Injection in an Azure Function - asp.net-core

Given a File->New->Azure Functions v2 App, I'm trying to get a reference to either an ILoggerFactory or an ILogger<T>.
I'm doing this in the StartUp.cs class which is ran on the function app start.
Given the following code a weird exception is thrown:
var serviceProvider = builder.Services.BuildServiceProvider();
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
with the following exception:
A host error has occurred
[27/02/2019 8:21:22 AM] Microsoft.Extensions.DependencyInjection: Unable to resolve service for type 'Microsoft.Azure.WebJobs.Script.IFileLoggingStatusManager' while attempting to activate 'Microsoft.Azure.WebJobs.Script.Diagnostics.HostFileLoggerProvider'.
Value cannot be null.
Parameter name: provider
What is going on?
The full test repo/code can be found here on GitHub.

It seems that some infrastructure (e.g. IFileLoggingStatusManager) necessary for HostFileLoggerProvider is not set up yet at the time you are creating the dependency injection container and attempt to resolve the logger in the StartUp class. I think you should delay logging until after the application has fully started.
If you look at the startup code of WebJobs you'll see that the logger gets added first, after that the external startup gets executed and finally the required logging services gets added. Which is the wrong order for your case.

I've been looking for a solution for a while then I end up creating another service container for the startup class, but it'll need to register the required services to this container.
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var services = builder.Services;
IConfiguration configuration = services.BuildServiceProvider().GetService<IConfiguration>();
var tempServiceContainer = new ServiceCollection()
.AddScoped(_ => configuration);
tempServiceContainer.AddSingleton<IYourService, YourService>();
}
}
Or maybe you could try using lazy Dependency Injection, but I haven't test it yet on the cloud server.
service.AddSingleton<IYourService, YourService>();
service.AddSingleton(provider => new Lazy<IYourService>(provider.GetService<IYourService>));
var yourService = serviceProvider.GetService<Lazy<IYourService>>();

This doesn't work, at least not yet. (link)
The services getting registered at startup are not fully ready to be used inside the Configure method itself. Read the section 'Caveats' here:
https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection
"The startup class is meant for only setup and registration. Avoid
using services registered at startup during the startup process. For
instance, don't try to log a message in a logger that is being
registered during startup. This point of the registration process is
too early for your services to be available for use. After the
Configure method is run, the Functions runtime continues to register
additional dependencies, which can affect how your services operate."

Related

Minimal Hosting Model: Exited from Program.Main with exit code = '0'

After migrating an ASP.NET Core 6 app from the legacy Program/Startup to use the new minimal hosting model, I am receiving a non-descript set of errors.
Error Messages
I have explicitly enabled UseDeveloperExceptionPage() to be safe, but all I am receiving in the browser is the generic:
HTTP Error 500.30 - ASP.NET Core app failed to start
In the Event Viewer, Event 1011 gets logged:
Application '/LM/W3SVC/2/ROOT' with physical root 'C:\Code' has exited from Program.Main with exit code = '0'. Please check the stderr logs for more information.
Followed by Event 1007:
Application '/LM/W3SVC/2/ROOT' with physical root 'C:\Code' failed to load coreclr.
Exception message:
CLR worker thread exited prematurely
No further information is available in the debug console when running either IIS Express or Kestrel. I am, however, able to see other startup processes from my Program being logged, so know they're loading correctly.
Code
This is a simplified version of my Program file, with custom components removed:
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.ViewComponents;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
// Should be implicit in Debug mode, but explicitly including to be safe
app.UseDeveloperExceptionPage();
app.UseStaticFiles();
app.UseRouting();
app.MapControllers();
Previous Threads
As these are really general errors, there are quite a few previous threads available related to this issue—but none that seem applicable to this case. (Many have to do with the ASP.NET Core Hosting Bundle, which I am not using.)
Question
Are there common causes for this issue? Alternatively, are there other approaches for debugging this scenario?
The error messages aren't terribly intuitive, but this error essentially means that the application ran, and then immediately exited. A common cause of this is when you neglect to add the Run() command at the end of the Program:
app.Run();
Background
This is easy to miss if you're focused on migrating your ConfigureServices() and Configure() methods from your Startup class. While most of your previous configuration code likely lives in that class, the Run() method is one of a few pieces that needs to be migrated from your legacy Program class. You might recognize it from your legacy Main() method; e.g.,
public static void Main(string[] args) => CreateHostBuilder(args).Build().Run();
In the new ASP.NET Core 6 minimal hosting model, this gets translated to:
var builder = WebApplication.CreateBuilder(args);
// Configuration from Startup.ConfigureService()
var app = builder.Build();
// Configuration from Startup.Configure()
app.Run();
When it exits from Program.Main() with exit code 0, that's telling you that your Program ran correctly—and, thus, seeing logging for your configuration code—but then it stopped without ever actually launching your web application.
Simple fix once you know to look for it.

Hangfire cannot find registered service

I have an issue with Hangfire, most likely because of my ignorance about some topics.
I have a host/plugins infrastructure, where each plugin is loaded at runtime and it register its interfaces.
public void ConfigureServices(IServiceCollection services, IConfigurationRoot Configuration)
{
services.AddTransient<IManager, Manager>();
services.AddTransient<IAnotherManager, AnotherManager>();
this.AddControllers(services);
}
Some plugin may add jobs using Hangfire, which are also set during runtime
public void ScheduleJobs()
{
RecurringJob.AddOrUpdate<IManager>(n => n.SayHello(), Cron.Monthly);
}
The issue I have is, while any service registered directly in the host is correctly resolved in hangfire,
all the interfaces (ex IManager) that are defined in external assemblies aren't found.
I added a customer JobActivator where I'm passing the IServiceCollection and I can actually see that those external services are registered (and I can use them anywhere else but from Hangfire), but still
in the JobActivator, when Hangfire tries to resolve the external service, it fails.
public override object ActivateJob(Type type)
{
// _serviceCollection contains the IManager service
var _provider = _serviceCollection.BuildServiceProvider();
// this will throw an Exception => No service for type '[...].IManager' has been registered.
var implementation = _provider.GetRequiredService(type);
return implementation;
}
In the same example, if I use the Default JobActivator, then the exception I get is System.MissingMethodException: Cannot create an instance of an interface.
I could enqueue the job using the Class instead of the Interface, but that's not the point and anyway if the Class has services injected, those will not be resolved as well.
What am I missing?
The problem has been solved. The solution is to add a specific IoC Container for hangfire. I used Unity. In that way dependencies are resolved correctly.
Thanks Matteo for making it clear that HF requires its own IoC container. This link makes the point too:
Hangfire needs to have it's own container with dependencies registered independently of the global UnityContainer. The reason for this is twofold; Hangfire's dependencies need to be registered with the PerResolveLifetimeManager lifetime manager. This is so that you don't get concurrency issues between workers that have resolved a dependency to the same instance. For example; with the normal HierarchicalLifetimeManager, two workers needing the same repository dependency may resolve to the same instance and share a common db context. The workers are meant to each have their own db contexts. Secondly, when the OWIN bootstrapper is run, the global UnityContainer may or may not be initialised yet and Hangfire is unable to take in a reference to the container. So giving Hangfire it's own managed container is a clear separation of purpose and behaviour in how our dependencies are resolved.

Error creating bean named `conversionServicePostProcessor` when using spring-boot-admin-server

I was trying to enable Spring boot admin server for my application. The default settings work perfectly fine but when I attempt to enable security, I am getting following error:
APPLICATION FAILED TO START
Description:
The bean 'conversionServicePostProcessor', defined in class path
resource
[org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class],
could not be registered. A bean with that name has already been
defined in class path resource
[org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.class]
and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting
spring.main.allow-bean-definition-overriding=true
Process finished with exit code 1
I am using the latest SNAPSHOT version of spring-boot-admin-starter-server (2.2.0-SNAPSHOT). Here is my security configuration:
#EnableAdminServer
#EnableWebFluxSecurity
#Configuration(proxyBeanMethods = false)
class AdminServerSecurityConfigurations(val adminServerProperties: AdminServerProperties) {
#Bean
fun adminServerSecurityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain = http
// #formatter:off
.authorizeExchange()
.pathMatchers("${adminServerProperties.contextPath}/assets/**").permitAll()
.pathMatchers("${adminServerProperties.contextPath}/login").permitAll()
.anyExchange().authenticated().and()
.formLogin().loginPage("${adminServerProperties.contextPath}/login").and()
.logout().logoutUrl("${adminServerProperties.contextPath}/logout").and()
.httpBasic().and()
// #formatter:on
.csrf().disable()
.build()
#Bean
fun notifyLogger(instanceRepository: InstanceRepository) = LoggingNotifier(instanceRepository)
}
I found a pull request to update the documentation: https://github.com/spring-projects/spring-boot/issues/14069
For Reactive WebSockets,
{spring-reference}web-reactive.html#webflux-websocket[Spring WebFlux] offers rich support,
which is accessible through the spring-boot-starter-webflux module.
See the spring-boot-sample-websocket-reactive sample project to see how WebSockets may
be implemented using Spring WebFlux.
it turns out that using webflux and websocket leads to conflicts.
also in this pull request was denied in the resolution of the conflict
https://github.com/spring-projects/spring-boot/issues/14810
for reactive websocket see this sample https://www.baeldung.com/spring-5-reactive-websockets
I had the same issue and was able solve it by adding
spring.main.allow-bean-definition-overriding=true
to my application.properties.
Sounds like a workaround and it was also only necessary if I deployed it as WAR -- as a standalone application the exception never occured.
I also faced this error, after Reimport All Mavne Projects(Intellij IDE) it works fine for me. Here my detailed input on this issue here

'Address already in use' when running tests using Spring LDAP embedded server

I am trying to use Spring LDAP in one of my Spring Boot projects but I am getting an 'Address already in use' error when running multiple tests.
I have cloned locally the sample project here:
https://spring.io/guides/gs/authenticating-ldap/
...and just added the boilerplate test normally created by Spring Boot to verify that the Application Context loads correctly:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyApplicationTests {
#Test
public void contextLoads() {
}
}
If run alone, this test passes. As soon as LdapAuthenticationTests and MyApplicationTests are run together, I get the error above for the latter.
After debugging a bit, I've found out that this happens because the system tries to spawn a second instance of the embedded server.
I am sure I am missing something very stupid in the configuration.
How can I fix this problem?
I had a similar problem, and it looks like you had a static port configured (as was in my case).
According to this article:
Spring Boot starts an embedded LDAP server for each application
context. Logically, that means, it starts an embedded LDAP server for
each test class. Practically, this is not always true since Spring
Boot caches and reuses application contexts. However, you should
always expect that there is more than one LDAP server running while
executing your tests. For this reason, you may not declare a port for
your LDAP server. In this way, it will automatically uses a free port.
Otherwise, your tests will fail with “Address already in use”
Thus it might be a better idea not to define spring.ldap.embedded.port at all.
I addressed the same issue. I solved it with an additional TestExecutionListener since you can get the InMemoryDirectoryServer bean.
/**
* #author slemoine
*/
public class LdapExecutionListener implements TestExecutionListener {
#Override
public void afterTestClass(TestContext testContext) {
InMemoryDirectoryServer ldapServer = testContext.getApplicationContext().getBean(InMemoryDirectoryServer.class);
ldapServer.shutDown(true);
}
}
And on each SpringBootTest (or only once in an abstract super class)
#RunWith(SpringRunner.class)
#SpringBootTest
#TestExecutionListeners(listeners = LdapExecutionListener.class,
mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
public class MyTestClass {
...
}
also do not forget
mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS
to avoid disabling the whole #SpringBootTest auto configuration.
Okay, I think I found a solution by adding a #DirtiesContext annotation to my test classes:
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
If you are using spring embedded ldap, try to comment or remove port value from config file as below :
spring :
ldap:
embedded:
base-dn: dc=example,dc=org
credential:
username: cn=admin,dc=example,dc=org
password: admin
ldif: classpath:test-schema.ldif
# port: 12345
validation:
enabled: false
Try specifying the web environment type and the base configuration class (the one with !SpringBootApplication on it).
#RunWith(SpringRunner.class)
#SpringBootTest(
classes = MyApplication.class,
webEnvironment = RANDOM_PORT
)
public class MyApplicationTests {
#Test
public void contextLoads() {
}
}
Do this for all your test classes.
I solved this problem by adding #DirtiesContext over each test class that requires embedded ldap server. In my case (and as I feel in many others), embedded ldap server was starting up at every #SpringBootTest, since I added all spring.ldap.embedded.* properties to general application-test.properties. Therefore, when I run a bunch of tests, the problem of 'Address already in use' broke all test passing.
Steps I followed:
create an additional test profile (with corresponding named application properties file, e.g. 'application-ldaptest.properties')
move to that file all spring.ldap.embedded.* properties (with fixed port value)
over all #SpringBootTest-s that do require embedded server running up, add #ActiveProfiles("testladp") and #DirtiesContext annotations.
Hope, that helps.

Configuring Ninject for a console application and leveraging the existing repository for my MVC application

I have a MVC 3 solution configured with Ninject using a repository pattern. Some of my bindings include:
kernel.Bind<IDatabaseFactory>().To<DatabaseFactory>().InRequestScope();
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
kernel.Bind<IMyRepository>().To<MyRepository>().InRequestScope();
kernel.Bind<IMyService>().To<MyService>().InRequestScope();
kernel.Bind<ILogging>().To<Logging>().InSingletonScope();
I also added a console application to my solution and I want to leverage the same repository and services. My Ninject configuration for the console application looks like:
kernel.Bind<IDatabaseFactory>().To<DatabaseFactory>().InSingletonScope();
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InSingletonScope();
kernel.Bind<IMyRepository>().To<MyRepository>().InSingletonScope();
kernel.Bind<IMyService>().To<MyService>().InSingletonScope();
kernel.Bind<ILogging>().To<Logging>().InSingletonScope();
My console code looks like:
static void Main(string[] args)
{
IKernel kernel = new StandardKernel(new IoCMapper());
var service = kernel.Get<IMyService>();
var logger = kernel.Get<ILogging>();
... do some processing here
}
This works just fine but I want t be sure that I am configuring Ninject correctly for a console application. Is it correct to use InSingletonScope() for all my bindings in my console application? Should I be configuring it differently?
Do you want one and only one instance of each of your repository services for the whole application? If so, then use InSingletonScope.
Is your console application multithreaded? If this is the case and you want a new instance of your services for each thread then you will use InThreadScope.
If you want a new instance of the service(s) each time they are called for, set it to InTransientScope.
You also have the option of defining your own scope using InScope. Bob Cravens gives a good overview of each of these here http://blog.bobcravens.com/2010/03/ninject-life-cycle-management-or-scoping/