I'm trying to create a simple Hangfire test but it's not working. Here's all the important code, and how I've configured it with the Hangire.Autofac . Not sure what I'm missing here. The exception I'm getting in the /hangfire dashbaord is below also.
public class AmazonSqsService : IAmazonSqsService
{
private readonly IBackgroundJobClient _backgroundJobClient;
private readonly ILogService _logService;
public AmazonSqsService(IBackgroundJobClient backgroundJobClient, ILogService logService)
{
_backgroundJobClient. = backgroundJobClient;
_logService= logService;
}
public async Task<string> Test()
{
return _backgroundJobClient.Enqueue(() => Looper());
}
public void Looper() {
while (true) { _logService.Info("In Looper Loop"); Thread.Sleep(5000); }
}
}
public partial class Startup
{
public static IContainer ConfigureContainer()
{
var builder = new ContainerBuilder();
RegisterApplicationComponents(builder);
AppGlobal.Container = builder.Build();
}
public static void RegisterApplicationComponents(ContainerBuilder builder)
{
builder.RegisterType<LogService>().As<ILogService>().InstancePerLifetimeScope();
builder.RegisterType<AmazonSqsService>().As<IAmazonSqsService>().InstancePerLifetimeScope();
builder.RegisterType<BackgroundJobClient>().As<IBackgroundJobClient>().InstancePerLifetimeScope();
builder.Register(c => JobStorage.Current).As<JobStorage>().InstancePerLifetimeScope();
builder.Register(c => new StateMachineFactory(JobStorage.Current)).As<IStateMachineFactory>().InstancePerLifetimeScope();
}
public static void ConfigureHangfire(IAppBuilder app)
{
app.UseHangfire(config =>
{
config.UseAutofacActivator(AppGlobal.Container);
config.UseSqlServerStorage("DefaultDatabase");
config.UseServer();
});
}
}
However in the dashboard I keep getting this error for the task:
Failed An exception occurred during job activation.
Autofac.Core.Registration.ComponentNotRegisteredException
The requested service 'App.Services.AmazonSqsService' has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.
Figured this out eventually.
Correct Usage:
public class Service : IService {
public void MethodToQueue() { ... }
}
public class AnyOtherClass {
public void StartTasks() {
BackgroundJob.Enqueue<IService>(x => x.MethodToQueue()); //Good
}
}
Incorrect usage (what I was doing wrong)
public class Service : IService {
public void StartTasks() {
BackgroundJob.Enqueue(() => this.MethodToQueue()); //Bad
}
public void MethodToQueue() { ... }
}
public class AnyOtherClass {
public AnyOtherClass(IService service) {
service.StartTasks();
}
}
Related
I am building an ASP.NET Core WebAPI application, it is working perfectly fine with the below setup
public void ConfigureServices(IServiceCollection services)
{
var settings = Configuration.Get<Settings>();
CosmosClient client = new CosmosClient(settings.CosmosDB.EndpointUrl, settings.CosmosDB.PrimaryKey);
CosmosDbContainerFactory cosmosDbClientFactory = new CosmosDbContainerFactory(client, settings.CosmosDB.DatabaseName, settings.CosmosDB.Containers);
services.AddSingleton<ICosmosDbContainerFactory>(cosmosDbClientFactory);
services.AddTransient<IFamilyService, FamilyService>();
services.AddTransient<IFamilyRepository, FamilyRepository>();
services.AddControllers();
}
But while trying to replace the manual service registration with Scrutor Scan, like mentioned below
public void ConfigureServices(IServiceCollection services)
{
var settings = Configuration.Get<Settings>();
CosmosClient client = new CosmosClient(settings.CosmosDB.EndpointUrl, settings.CosmosDB.PrimaryKey);
CosmosDbContainerFactory cosmosDbClientFactory = new CosmosDbContainerFactory(client, settings.CosmosDB.DatabaseName, settings.CosmosDB.Containers);
services.AddSingleton<ICosmosDbContainerFactory>(cosmosDbClientFactory);
services.Scan(s => s
.FromAssembliesOf(typeof(IApiAssemblyMarker))
.AddClasses(false)
.UsingRegistrationStrategy(RegistrationStrategy.Append)
.AsImplementedInterfaces()
.WithTransientLifetime()
);
services.AddControllers();
}
I am getting the following error
Error while validating the service descriptor 'ServiceType: FBAuthDemoAPI.Services.Contract.IFamilyService Lifetime: Transient ImplementationType: FBAuthDemoAPI.Services.Implementation.FamilyService': Unable to resolve service for type 'Microsoft.Azure.Cosmos.CosmosClient' while attempting to activate 'FBAuthDemoAPI.CosmosDBFactory.CosmosDbContainerFactory'.
public interface IFamilyService
{
}
public class FamilyService : IFamilyService
{
private readonly IFamilyRepository _familyRepository;
public FamilyService(IFamilyRepository familyRepository)
{
this._familyRepository = familyRepository;
}
}
public interface IGenericRepository<T> where T : class
{
}
public abstract class GenericRepository<T> : IGenericRepository<T> where T : Entity
{
private readonly Container _container;
private readonly ICosmosDbContainerFactory _cosmosDbContainerFactory;
public abstract string DatabaseName { get; }
public abstract string ContainerName { get; }
protected GenericRepository(ICosmosDbContainerFactory cosmosDbContainerFactory)
{
this._cosmosDbContainerFactory = cosmosDbContainerFactory ?? throw new ArgumentNullException(nameof(ICosmosDbContainerFactory));
this._container = this._cosmosDbContainerFactory.GetContainer(ContainerName)._container;
}
}
public interface IFamilyRepository : IGenericRepository<Family>
{
}
public class FamilyRepository : GenericRepository<Family>, IFamilyRepository
{
public override string DatabaseName => "FamilyDB";
public override string ContainerName => "Family";
public FamilyRepository(ICosmosDbContainerFactory factory) :
base(factory)
{
}
}
What is the issue and How do I fix this?
Update:: Having registered the classes shown below, it started working, but I'm not understanding why it failed when registering with the Single instance like mentioned above, Is it due to the limitation of Scrutor library?
var cosmosDBSettings = new CosmosDBSettings();
configration.Bind(CosmosDBSettings.SectionName, cosmosDBSettings);
services.AddSingleton(Microsoft.Extensions.Options.Options.Create(cosmosDBSettings));
services.Scan(scan => scan
.FromAssembliesOf(typeof(IApiAssemblyMarker))
.AddClasses(classes => classes.AssignableTo<ICosmosDbContainerFactory>()) // Filter classes
.AsSelfWithInterfaces()
.WithSingletonLifetime()
.AddClasses(x => x.AssignableTo(typeof(IGenericRepository<>))) // Can close generic types
.AsMatchingInterface()
.WithScopedLifetime()
.AddClasses(x => x.AssignableTo(typeof(IFamilyService))) // Can close generic types
.AsMatchingInterface()
.WithScopedLifetime());
Ok, I am making a Api, trying to use DI.
My Controller:
[Route("api/[controller]")]
[ApiController]
public class TerminalsController : ControllerBase
{
private readonly IServiceWrapper _service;
public TerminalsController(IServiceWrapper service)
{
_service = service;
}
[HttpPost]
public async Task<ActionResult> Post([FromBody] Message object)
{
try
{
Result result = await _service.Terminal.UpsertInfo(ternminalObject);
if (result.shopId != -1 || result.deviceId != -1 || result.companyId != -1)
{
return Ok(result);
}
else
{
return BadRequest("Can not save info from session on database");
}
}
catch (Exception ex)
{
return StatusCode(500, "Internal server error");
}
}
}
And the code of my service:
public class TerminalService : ITerminalService
{
private readonly IRepositoryWrapper _repository;
public TerminalService(IRepositoryWrapper repository)
{
_repository = repository;
}
public async Task<Result> UpsertInfo(company company)
{
try
{
var company = await _repository.Company.GetById(int.Parse(company.Id))
return result;
}
catch (Exception ex)
{
throw ex;
}
}
}
When my code rise the line
var company = await _repository.Company.GetById(int.Parse(company.Id))
I get the error
System.NullReferenceException: 'Object reference not set to an instance of an object.'
Here there are my others class:
My factory:
public class DbClientFactory<T>
{
private static Lazy<T> _factoryLazy = new Lazy<T>(
() => (T)FormatterServices.GetUninitializedObject(typeof(T)),
LazyThreadSafetyMode.ExecutionAndPublication);
public static T Instance
{
get
{
return _factoryLazy.Value;
}
}
}
The factory instace the service and the repositories.
This is my StartUp.cs:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
SqlHelper.connectionString = Environment.GetEnvironmentVariable("CONNECTION_STRING");
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureCors();
services.AddMvc();
services.ConfigureServiceWrapper();
services.ConfigureRepositoryWrapper();
services.AddControllers().AddNewtonsoftJson();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCors("CorsPolicy");
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.All
});
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
The ConfigureRepositoryWrapper and the ConfigureServiceWrapper are in the ServiceExtensions.cs:
public static class ServiceExtensions
{
public static void ConfigureCors(this IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod());
});
}
public static void ConfigureRepositoryWrapper(this IServiceCollection services)
{
services.AddScoped<IRepositoryWrapper, RepositoryWrapper>();
}
public static void ConfigureServiceWrapper(this IServiceCollection services)
{
services.AddScoped<IServiceWrapper, ServiceWrapper>();
}
}
The implement of ServiceWrapper is:
public class ServiceWrapper : IServiceWrapper
{
private ITerminalService _terminal;
public ITerminalService Terminal {
get
{
if (_terminal == null)
{
_terminal = DbClientFactory<TerminalService>.Instance;
}
return _terminal;
}
}
}
And the implement of RepositoryWrapper is:
public class RepositoryWrapper : IRepositoryWrapper
{
private IDeviceRepository _device;
private IShopRepository _shop;
private ICompanyRepository _company;
public IDeviceRepository Device
{
get
{
if (_device == null)
{
_device = DbClientFactory<DeviceRepository>.Instance;
}
return _device;
}
}
public IShopRepository Shop
{
get
{
if (_shop == null)
{
_shop = DbClientFactory<ShopRepository>.Instance;
}
return _shop;
}
}
public ICompanyRepository Company {
get {
if (_company == null)
{
_company = DbClientFactory<CompanyRepository>.Instance;
}
return _company;
}
}
}
I really dont know what is wrong here...
Thank you!
You are getting this error because company.Id does not exist.
var company = await _repository.Company.GetById(int.Parse(company.Id));
You are creating/fetching an object named company and at the same already have an object passed in that is named company, you can not do have two variables or objects in the same scope with the same name. Rename one of the objects from company to something else.
Also you likely are not passing in an object in the parameter, you are probably passing in null which is the reason for company.Id not existing. Check the value you are passing in, and rename one of the items.
var newCompany = await _repository.Company.GetById(int.Parse(company.Id));
If the exception is happening on _repository, the reason is that you are trying to pass a dependency into a non service class. To fix it, pass the dependency into the controller, then when initializing TerminalService just pass in the _repository. Also make sure that _service.Terminal is instantiated.
public class TerminalsController : ControllerBase
{
private readonly IServiceWrapper _service;
private readonly IRepositoryWrapper _repository;
public TerminalsController(IServiceWrapper service, IRepositoryWrapper repository)
{
_service = service;
_repository = repository;
}
[HttpPost]
public async Task<ActionResult> Post([FromBody] Message object)
{
_service.Terminal = new TerminalService(_repository);
Result result = await _service.Terminal.UpsertInfo(ternminalObject);
//remaining code
}
}
I have service factory in .NET Core 3.1. Each service implements IService and each service has arguments in the constructor, (there is no parameterless ctors)
What is the best way to inject service factory?
--Update with current solution--
public interface IServiceFactory
{
IService Create(string serviceType);
}
public class ServiceFactory : IServiceFactory
{
public IService[] services;
public ServiceFactory(IService[] services)
{
this.services = services;
}
public IService Create(string serviceType)
{
return services.First(s => s.ServiceName == serviceType);
}
}
public interface IService
{
string ServiceName { get; }
bool Send();
}
public class ServiceA : IService
{
private ISrv1 srv1:
private ISrv2 srv2;
public ServiceA(ISrv1 srv1, ISrv2 srv2)
{
this.srv1 = srv1;
this.srv2 = srv2;
}
public ServiceName => return "serviceA";
public bool Send()
{
var data = srv1.GetData();
var msg = new MessageData
{
Id = data.Id,
Email = data.MailAddress
};
return srv2.Send(msg);
}
}
//not full class just the idea for sending message when getting message data from
//different services
public class ServiceB : IService
{
public ServiceB()
{
}
public ServiceName => return "serviceB";
public bool Send() => false;
}
How the factory should create the correct service with all dependencies?
And how to register correctly the services and the factory in configue services?
For now the solution is
services.AddTransient<IServiceFactory>(c =>
new ServiceFactory(new INotificationService[]
{
new ServiceA(c.GetService<ISrv1>(),c.GetService<ISrv2>()),
new ServiceB();
}));
As a solution I used IServiceProvider in the FactoryService for getting the correct service by the givven type in runtime.
I registered the services as following
services.AddScoped<IServiceFactory, ServiceFactory>();
services.AddScoped<ServiceA>()
.AddScoped<IService, ServiceA>(c => c.GetService<ServiceA>());
services.AddScoped<ServiceB>()
.AddScoped<IService, ServiceB>(c => c.GetService<ServiceB>());
I am using Readiness healthchecks for my project and want to add a warmup period to it.
Dependency Injection is being used to get the warmup task from the Kernel but I am not able to get it because the Readiness Healthcheck is being initialized before the IKernel it seems.
I am getting the follow error:
Unable to resolve service for type 'IKernel' while attempting to activate 'Project.Ranking.API.HealthCheck.RankingReadinessHealthCheck'.
How does one use a class to warm up the pod before it is being used.
I have not been able to find a working example where someone warms up before the endpoints are available.
UPDATE:
Core.Library Startup.CS
public void CoreConfigureServices(IServiceCollection services)
{
... other code
services.AddHealthChecks()
.AddIdentityServer("https://identity.example.com")
.AddCheck<IReadinessHealthCheck>("Readiness", failureStatus: null)
.AddCheck<ILivenessHealthCheck>("Liveness", failureStatus: null);
services.AddSingleton<ILivenessHealthCheck, LivenessHealthCheck>();
}
public void CoreConfigure(IApplicationBuilder app, IHostEnvironment env)
{
... other code
app.UseHealthChecks("/healthcheck/live", new HealthCheckOptions()
{
Predicate = check => check.Name == "Liveness"
});
app.UseHealthChecks("/healthcheck/ready", new HealthCheckOptions()
{
Predicate = check => check.Name == "Readiness",
});
}
API Startup.CS
public void ConfigureServices(IServiceCollection services)
{
CoreConfigureServices(services);
... other code
services.AddSingleton<Core.Library.IReadinessHealthCheck, ReadinessHealthCheck>();
}
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
CoreConfigure(app, env);
... other code
//Here used to be the warm up, but this is used in the liveness probe and i want to warm up in the readiness probe
//Kernel.Get<IWarmupTask>().Initialize();
Kernel.Bind<IReadinessHealthCheck>().To<ReadinessHealthCheck>();
}
Core.Library BaseReadinessHealthCheck.cs
public abstract class BaseReadinessHealthCheck : IReadinessHealthCheck
{
public BaseReadinessHealthCheck()
{
}
private bool StartupTaskCompleted { get; set; } = false;
public abstract void WarmUp();
public void CompleteTask()
{
StartupTaskCompleted = true;
}
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
//start tasks
if (!StartupTaskCompleted)
{
Task.Run(() => WarmUp());
}
if (StartupTaskCompleted)
{
return Task.FromResult(HealthCheckResult.Healthy("The startup task is finished."));
}
return Task.FromResult(HealthCheckResult.Unhealthy("The startup task is still running."));
}
}
API ReadinessHealthCheck.CS
public class ReadinessHealthCheck : ReadinessHealthCheck
{
public ReadinessHealthCheck(IKernel kernel) : base(kernel)
{
}
public override void WarmUp()
{
// I want to do a warmup here, where it calls IWarmupTask
CompleteTask();
}
}
Consider Apache Ignite.NET cluster that provides service grid.
There is a simple service, that will run on any node:
public class ClientConnectionService : IClientConnectionService, IService
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
[InstanceResource] private IIgnite Ignite { get; set; }
public void Listen(string hostname, int port, uint username, string password,
ClientConnectionListenerOptions options = ClientConnectionListenerOptions.All)
{
Logger.Debug("Listen");
}
public void Init(IServiceContext context)
{
Logger.Debug("Initialized");
}
public void Execute(IServiceContext context)
{
Logger.Debug("Executed");
}
public void Cancel(IServiceContext context)
{
Logger.Debug("Canceled");
}
}
The application is using Castle Windsor as inversion of control container.
I would like to inject custom dependencies, that won't be serialized and transferred over the wire.
Is there any way to achieve it?
N.B. In Java version, there is #SpringResourceannotation that will basically do what I want, but the question is about .NET, that provides just [InstanceResource] attribute.
This is what I have ended up with:
In shared project where all the interfaces and contracts are described I've introduced IContainer
public interface IContainer
{
T Resolve<T>();
}
In project that is responsible for Apache Ignite.NET integration I've implemented simple Apache Ignite.NET plugin
public class DependencyInjectionPlugin
{
public IContainer Container { get; set; }
public T Resolve<T>()
{
return Container.Resolve<T>();
}
}
[PluginProviderType(typeof(DependencyInjectionPluginProvider))]
public class DependencyInjectionPluginConfiguration : IPluginConfiguration
{
public void WriteBinary(IBinaryRawWriter writer)
{
// No-op
}
public int? PluginConfigurationClosureFactoryId { get; } = null; // No Java part
}
public class DependencyInjectionPluginProvider : IPluginProvider<DependencyInjectionPluginConfiguration>
{
public string Name { get; } = "DependencyInjection";
public string Copyright { get; } = "MIT";
protected DependencyInjectionPlugin DependencyInjectionPlugin { get; set; }
public T GetPlugin<T>() where T : class
{
return DependencyInjectionPlugin as T;
}
public void Start(IPluginContext<DependencyInjectionPluginConfiguration> context)
{
DependencyInjectionPlugin = new DependencyInjectionPlugin();
}
public void Stop(bool cancel)
{
}
public void OnIgniteStart()
{
}
public void OnIgniteStop(bool cancel)
{
}
}
In main project, that is responsible for wiring up all components, I've implemented IContainer, defined previously, and registered it in Castle Windsor:
public class DependencyInjectionContainer : IContainer
{
protected IKernel Kernel { get; set; }
public DependencyInjectionContainer(IKernel kernel)
{
Kernel = kernel;
}
public T Resolve<T>()
{
return Kernel.Resolve<T>();
}
}
public class DependencyInjectionInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component
.For<IContainer>()
.ImplementedBy<DependencyInjectionContainer>()
);
}
}
In the very same project I've registered Apache Ignite.NET
public class IgniteInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component
.For<IIgnite>()
.UsingFactoryMethod(() => Ignition.Start(new IgniteConfiguration
{
PluginConfigurations = new[] {new DependencyInjectionPluginConfiguration()}
}))
);
}
}
Finally, in application's main method:
// Build Windsor container
using (var container = new WindsorContainer())
{
// Install DI abstraction layer
container.Install(new DependencyInjectionInstaller());
// Install cluster abstraction layer
container.Install(new IgniteInstaller());
// Attach DI container to cluster plugin
container
.Resolve<IIgnite>()
.GetPlugin<DependencyInjectionPlugin>("DependencyInjection")
.Container = container.Resolve<IContainer>();
// Wait
Done.Wait();
}
That's it. From now on, I am able to access IContainer implementation in Apache Ignite.NET distributed service like this:
var plugin = Ignite.GetPlugin<DependencyInjectionPlugin>("DependencyInjection");
var whatever = plugin.Resolve<IWhatever>();