.Net Core BackgroundService for RabbitMQ.Client.Core.DependencyInjection - not possible to use other services or MediatR command - .net-core

I'm creating an application using RabbitMQ.Client.Core.DependencyInjection as a consumer of messages in the BackgroundService. Receiving and deserializing the message works correctly, but when I'm sending command to the MediatR I got an error:
---> System.InvalidOperationException: Error while validating the service descriptor 'ServiceType: RabbitMQ.Client.Core.DependencyInjection.Services.IMessageHandlerContainerBuilder Lifetime: Singleton ImplementationType: RabbitMQ.Client.Core.DependencyInjection.Services.MessageHandlerContainerBuilder': Cannot consume scoped service 'Application.Common.Interfaces.IDbContext' from singleton 'RabbitMQ.Client.Core.DependencyInjection.Services.IMessageHandlerContainerBuilder'.
---> System.InvalidOperationException: Cannot consume scoped service 'Application.Common.Interfaces.IDbContext' from singleton 'RabbitMQ.Client.Core.DependencyInjection.Services.IMessageHandlerContainerBuilder'.
My Command's Handler's constructor is creating dbContext in the constructor, and outside the BackgroundService it works correctly.
I understand that for BackgroundService one needs to create scope and get service from the ServiceProvider:
using var scope = _sp.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<IDbContext>();
But how can I use the rest of the application, where I'm using injected services as singletons?
I believe the same issue would happen if I would call a Service class that constructs injected dbContext in the same way, it does not seem to be a problem with a MediatR.

ok, found a problem with RabbitMQ.Client.Core.DependencyInjection NuGet, instead used RabbitMQ.Client and all works as expected

Related

Grails 3.3.10: running integration tests Error creating bean with name 'com.cabolabs.security.UserController'

I'm trying to create an integration test for a service and keep receiving a controller, that is totally disconnected from the service test, can't be created. Spending a day or so on everything that might cause the issue, and failing miserably, decided to create a project from scratch and start adding line by line.
So I did:
grails create-app test
grails create-domain-class com.cabolabs.security.User
added username and password String fields
grails generate-app com.cabolabs.security.User
grails create-service com.cabolabs.cloud.BalanceUpdate
grails create-integration-test com.cabolabs.cloud.BalanceUpdate
grails test-app com.cabolabs.cloud.BalanceUpdate -integration
That runs OK, the test fails because of the default code, that is not important.
Then I started to add references to services in the UserController, and the BalanceUpdateService, like mailService from the mail plugin.
The test worked as before.
Then I added this line, which I have extensively used in many controllers of my original project:
def config = grailsApplication.config
With that line, the whole thing felt apart and got the error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.cabolabs.security.UserController': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.cabolabs.security.UserController]: Constructor threw exception; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1160)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1104)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:511)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1080)
at org.spockframework.spring.SpringMockTestExecutionListener.beforeTestMethod(SpringMockTestExecutionListener.java:54)
at org.spockframework.spring.AbstractSpringTestExecutionListener.beforeTestMethod(AbstractSpringTestExecutionListener.java:23)
at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:269)
at org.spockframework.spring.SpringTestContextManager.beforeTestMethod(SpringTestContextManager.java:54)
at org.spockframework.spring.SpringInterceptor.interceptSetupMethod(SpringInterceptor.java:45)
at org.spockframework.runtime.extension.AbstractMethodInterceptor.intercept(AbstractMethodInterceptor.java:28)
at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:87)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:114)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:57)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:66)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:109)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:147)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:129)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:46)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.cabolabs.security.UserController]: Constructor threw exception; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:154)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:89)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1152)
... 34 more
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at grails.web.api.WebAttributes$Trait$Helper.currentRequestAttributes(WebAttributes.groovy:45)
at grails.web.api.WebAttributes$Trait$Helper.getGrailsAttributes(WebAttributes.groovy:54)
at grails.web.api.WebAttributes$Trait$Helper.getGrailsApplication(WebAttributes.groovy:134)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:142)
... 36 more
My question is, how can I have the config injected to my controllers and make the integration tests work? Thanks.
I just created a project from scratch and added line by line, it seems adding this to the controller, makes the test for the service fail (which are not connected in any way): def config = grailsApplication.config
Since I used that in many controllers, I tested changing it to Holders.config and that actually worked. I'll report this as a bug to Grails Core.

SignalR - ChatHub Dependency Management for Database Operation

Background:
I have a web application in which I have SignalR as well.
I'm using AutoFac as DI container where my database is registered as
builder.RegisterType<MyDbContext>().AsSelf().InstancePerLifetimeScope();
i.e. MyDbContext is registered as PerRequestDependency.
The ChatHub is also registered with same dependency level. i.e.
builder.Register<IHubContext>((c) =>
{
return GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
})
.InstancePerLifetimeScope();
Problem:
The problem I am facing is - The DbContext throws error saying there are multiple threads calling the DbContext.
Here is the exact error:
System.NotSupportedException: A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe. at System.Data.Entity.Internal.ThrowingMonitor.EnsureNotEntered()
Note: I have looked into entire code and I am 100% sure that I have awaited all async calls to the database.
Possible Solution:
If I change the AutoFac registration to per below then the error goes away but I feel, it will require more database connections.
builder.RegisterType<MyDbContext>().AsSelf();
i.e. remove InstancePerLifetimeScope
Expectation:
Better solution than increasing database connections.
Make sure you don't open the same entity twice.
Example:
var x = db.user.FirstOrDefault(a=>a.id == 1);
.
some code here
.
var y = db.user.FirstOrDefault(a=>a.id == 1);
y.userName = "";

How to configure Hangfire with Autofac in a dotnet core console app

I'm trying to port a working Hangfire setup embedded in a Kestrel webserver to a console app. I've modified the web app so it still provides the Hangfire dashboard but doesn't start its own Hangfire server.
The code I must port uses Autofac. I've added the Hangfire.Autofac package to the console app and have already performed all the steps detailed in the answer to Hangfire Autofac .net core 3.1
When I create a job (using the web app) the console app Hangfire server tries to execute the job but I get this failure message:
The requested service 'AED.ServicesLayer.JobProcessing.ProcessManager' has not been registered.
Investigating this we examine the setup of Autofac in the console app. This is how I set up my container.
IConfiguration config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.Build();
var containerBuilder = new Autofac.ContainerBuilder();
containerBuilder.RegisterInstance(Log.Logger).AsImplementedInterfaces();
containerBuilder.RegisterModule(new RepositoryModule(config));
containerBuilder.RegisterType<UserService>().As<IUserService>();
containerBuilder.RegisterInstance(config).As<IConfiguration>();
containerBuilder.RegisterModule(new JobProcessingModule(config));
var container = containerBuilder.Build();
When the app is executed, hitting a breakpoint in JobProcessingModule proves the following line of code is executed.
builder.RegisterType<ProcessManager>().As<IProcessManager>();
It is very curious that the containerBuilder instance passed to JobProcessingModule.Load(containerBuilder) is not the same containerBuilder object on which RegisterModule is invoked.
However, experiments with simplified injectables suggest that this is normal, and the injected items are nevertheless visible in the registrations for the container that is returned.
Re-examining the logged failure we note that the class is mentioned by class name and not by interface. Changing the registration by removing the interface registration, like so
builder.RegisterType<ProcessManager>();//.As<IProcessManager>();
caused the ProcessManager to be found in the Hangfire console host but caused run-time errors in the web application when creating the job.
Registering it both ways caused ProcessManager to be found by both, with a new problem surfacing: cannot resolve dependencies. This, however, is merely a new case of the same problem.
While this allows me to move forward with getting a console host working, I do not like code I do not understand. Why should the console host require registration by class name when the web app does not?
Whatever is causing this has also caused Hangfire.IBackgroundJobClient to fail to resolve to the background job client. This is a hangfire class so it really does seem like there is a fundamental problem.
A lengthy investigation eventually revealed, confirmed by experiments, that this code
_recurringJobManager.AddOrUpdate(
insertResult.ToString(),
pm => pm.RunScheduledJobs(insertResult), interval.CrontabExpression
);
is responsible for the behaviour described in the question. AddOrUpdate is a generic method. When it is not explicitly typed it acquires its type from the class of the object passed to it. When the method is explicitly typed as the interface, like so
_recurringJobManager.AddOrUpdate<IProcessManager>(
insertResult.ToString(),
pm => pm.RunScheduledJobs(insertResult), interval.CrontabExpression
);
it remains compatible with the object, but the type acquired by Hangfire is the interface, and the console application can resolve ProcessManager from its interface.
Why the problem was not manifest in the web hosted Hangfire server remains a puzzle, but at least now I'm puzzled by the absence of a problem in a situation I don't have.

.NET Generic Host - Is it possible to stop and restart a host?

Consider this extremely simple .NET Core 3.1 (and .NET 5) application with no special config or hosted services:
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
internal class Program
{
public static async Task Main(string[] args)
{
var builder = Host.CreateDefaultBuilder(args);
builder.UseWindowsService();
var host = builder.Build();
var fireAndforget = Task.Run(async () => await host.RunAsync());
await Task.Delay(5000);
await host.StopAsync();
await Task.Delay(5000);
await host.RunAsync();
}
The first Run (sent as a background fire and forget task only for the purpose of this test) and Stop complete successfully. Upon calling Run a second time, I receive this exception:
System.AggregateException : 'Object name: 'EventLogInternal'.Cannot access a disposed object. Object name: 'EventLogInternal'.)'
If I do the same but using StartAsync instead of RunAsync (this time no need for a fireAndForget), I receive a System.OperationCanceledException upon called StartAsync the second time.
Am I right to deduce that .NET Generic Host aren't meant to be stopped and restarted?
Why do I need this?
My goal is to have a single application running as a Windows Service that would host two different .NET Generic Host. This is based on recommendation from here in order to have separate configuration and dependency injection rules and message queues.
One would stay active for all application lifetime (until the service is stopped in the Windows services) and would serve as a entry point to receive message events that would start/stop the other one which would be the main processing host with full services. This way the main services could be in "idle" state until they receive a message triggering their process, and another message could return them to idle state.
The host returned by CreateDefaultBuilder(...).Build() is meant to represent the whole application. From docs:
The main reason for including all of the app's interdependent resources in one object is lifetime management: control over app startup and graceful shutdown.
The default builder registers many services in singleton scope and when the host is stopped all of these services are disposed or switched to some "stopped" state. For example before calling StopAsync you can resolve IHostApplicationLifetime:
var appLifetime = host.Services.GetService<IHostApplicationLifetime>();
It has cancellation tokens representing application states. When you call StartAsync or RunAsync after stopping, all tokens still have IsCancellationRequested set to true. That's why the OperactionCancelledException is thrown in Host.StartAsync.
You can list other services during configuration:
For me it sounds like you just need some background jobs to process messages but I've never used NServiceBus so I don't know how it will work with something like Hangfire. You can also implement IHostedService and use it in the generic host builder.
I'm doing something like:
do
{
using IHost host = BuildHost();
await host.RunAsync();
} while (MainService.Restart);
with MainService constructor:
public MainService(IHostApplicationLifetime HostApplicationLifetime)
MainService.Restart is a static bool set by the MainService itself in response to some event which also calls HostApplicationLifetime.StopApplication().

EJB Timer IllegalArgumentException

I have an EJB3.0 timer which runs great.During application deployment i see this error in my WL logs,
An exception occurred while registering the MBean null.java.lang.IllegalArgumentException: Registered more than one instance with the same objectName : com.bea:ServerRuntime=admin,Name=weblogic.ejb.timer"
And during undeployment this
An unexpected error was encountered while attempting to remove any EJB Timers from the persistent store for the EJB 'TimerBean(Application: )
I don't use persistence store mechanism.I trigger the timer with servlet context.
We use WL 10.3.1,How can i overcome/catch this exception so,that it wouldn't be displayed during build process.
Thanks
The WLS ejb timers are persisted to a default store. The error messages seem to be related to it. Its likely that the ejb timer from a previous deployment is interfering. Does a server restart resolve this issue? You may want to try your app on WLS 10.3.4 to see if the issue has been resolved.

Resources