how can i configure hangfire dashboard in console application? - asp.net-core

i am using hangfire nuget package to schedule the jobs in asp.net core console application
i tried all the ways to configure the dashboard to the console application
how can i host the webpage from console application???
i have created startup.cs class for dashboard configuration
using Hangfire;
using Microsoft.AspNetCore.Builder;
namespace PulsarHangFire
{
public class Startup
{
public void Configuration(IApplicationBuilder app)
{
app.UseHangfireDashboard("/hangfire");
app.UseHangfireServer();
}
}
}
can anyone tell me how can i move forward

Create a Startup.cs file (or get one from the .NET Core Web App template) and configure the following:
public void ConfigureServices(IServiceCollection services)
{
// ... other required services ...
services.AddHangfire(configuration =>
{
// Do pretty much the same as you'd do with
// GlobalConfiguration.Configuration in classic .NET
// NOTE: logger and activator would be configured automatically,
// and in most cases you don't need to configure those.
configuration.UseSqlServerStorage(...);
// ... maybe something else, e.g. configuration.UseConsole()
});
}
Finally add the Hangfire dashboard:
public void Configure(IApplicationBuilder app, IRecurringJobManager recurringJobManager)
{
// ... previous pipeline stages, e.g. app.UseAuthentication()
app.UseHangfireDashboard(...);
// ... next pipeline stages, e.g. app.UseMvc()
// then you may configure your recurring jobs here:
recurringJobManager.AddOrUpdate(...);
}
Source

Related

Quartz.aspnetcore and hosting in ASP.NET Core

When we use Quartz.AspNetCore with ASP.NET Core 6, there are two ways to host it in the server:
builder.Services.AddQuartz(q =>
{
});
builder.Services.AddQuartzHostedService(options =>
{
// when shutting down we want jobs to complete gracefully
options.WaitForJobsToComplete = true;
});
But we can do it as well with
builder.Services.AddQuartz(q =>
{
});
builder.Services.AddQuartzServer(options =>
{
// when shutting down we want jobs to complete gracefully
options.WaitForJobsToComplete = true;
});
what difference between AddQuartzServer and AddQuartzHostedService?
According to this article, you could find the AddQuartz is used to add base Quartz scheduler, job and trigger configuration(configure and register the IScheduler instance and its related services with the ASP.NET Core dependency injection container).
The AddQuartzServer is used to configure how it works with asp.net core process , it configure and register a hosted service that starts and stops the IScheduler instance automatically when the ASP.NET Core application starts and stops.
It registers QuartzHostedService as a hosted service with the ASP.NET Core dependency injection container.
The AddQuartzServer call the AddQuartzHostedService, the AddQuartzServer will call the HealthChecks to check the scheduler health.
Source code like this:
using System;
using Microsoft.Extensions.DependencyInjection;
#if SUPPORTS_HEALTH_CHECKS
using Quartz.AspNetCore.HealthChecks;
#endif
namespace Quartz
{
public static class QuartzServiceCollectionExtensions
{
public static IServiceCollection AddQuartzServer(
this IServiceCollection services,
Action<QuartzHostedServiceOptions>? configure = null)
{
#if SUPPORTS_HEALTH_CHECKS
services
.AddHealthChecks()
.AddTypeActivatedCheck<QuartzHealthCheck>("quartz-scheduler");
#endif
return services.AddQuartzHostedService(configure);
}
}
}

Asp.Net Core 3.1 Web Application, Api page not found issue

My Environment Windows 10. Visual Studio 2019 Professional, Asp.Net Core 3.1
I am following a Pluralsight course to teach myself Asp.Net Core 3.1. Following the instructor, I have created the web application. Everything goes well until the instructor adds an api controller to the application. It works for him but not for me.
Here's my api controller
namespace OdeToFood.Api
{
[Route("api/[controller]")]
[ApiController]
public class RestaurantsController : ControllerBase
{
private readonly OdeToFoodDbContext _context;
public RestaurantsController(OdeToFoodDbContext context)
{
_context = context;
}
// GET: api/Restaurants
[HttpGet]
public async Task<ActionResult<IEnumerable<Restaurant>>> GetRestaurants()
{
return await _context.Restaurants.ToListAsync();
}
// GET: api/Restaurants/5
[HttpGet("{id}")]
public async Task<ActionResult<Restaurant>> GetRestaurant(int id)
{
var restaurant = await _context.Restaurants.FindAsync(id);
if (restaurant == null)
{
return NotFound();
}
return restaurant;
}
. . . . .
Here's my project structure and hierarchy.
When I rebuild my project, and call the app from local IIS Express, i.e. https://localhost:44351/ It loads fine. I can interact fully, browse and CRUD entities. However when I point to any api url, e.g. https://localhost:44351/api/Restaurants or https://localhost:44351/api/Restaurants/2 I get "This localhost page can’t be found". The api simply does not load or respond in any way.
I am familiar with MVC5 where, when creating a new project, in the create project wizard scaffolding, you could check a box to add api functionality. I am not seeing this in VS2019 for Asp.Net Core 3.1 We Apps.
I promise you have have done my homework before asking this question here. I have googled to death. I have seen MS articles on core 3.1 breaking changes. I have looked at online project templates. I have searched stackoverflow. Maybe my search terms are flawed and I'm simply missing something simple.
Questions:
Why is the api shown above not loading?
Is there a way to add api functionality to an existing Asp.Net Core 3.1 Web Application or do I need to create a separate api project?
Is there a way to create a new Asp.Net Core 3.1 Web Application with api functionality included?
My thanks in advance
Kieran
If you'd like to add web APIs feature into an existing Razor Pages project, you need to do additional configuration, like below.
public void ConfigureServices(IServiceCollection services)
{
//add services for controllers
services.AddControllers();
services.AddRazorPages();
//...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//...
app.UseRouting();
//...
app.UseEndpoints(endpoints =>
{
//add endpoints for controller actions
endpoints.MapControllers();
endpoints.MapRazorPages();
});
}
Testing code of controller and action(s)
[Route("api/[controller]")]
[ApiController]
public class RestaurantsController : ControllerBase
{
public IActionResult GetRestaurants()
{
return Ok("Restaurants List Here");
}
}
Test Result

How to add global metadata to ASP.NET Core logging?

I'd like to add my app's build number to all logs in an ASP.NET Core 3.1 app that is using Application Insights for log storage. Is this possible without having to use BeginScope and EndScope everywhere? I assumed it would be part of the ConfigureLogging startup hook, but didn't see anything. I've done this in the past with Serilog's enrichers, but am not using that library currently.
You can achieve that with TelemetryInitializer. (https://learn.microsoft.com/en-us/azure/azure-monitor/app/api-filtering-sampling#addmodify-properties-itelemetryinitializer)
public class BuildNumberTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
(telemetry as ISupportProperties).Properties.Add("BuildNumber", "ValueForBuildNumber");
}
You need to add this initializer to the config, which is done like below if you are on Asp.Net Core applications.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ITelemetryInitializer, BuildNumberTelemetryInitializer >();
}

ASP.Net Core SignalR simple host - 404 error

I am wanting to have 1 simple console app host that is solely for self-hosting the SignalR component.
I have created an "Empty Web Application" using the template. I have created a very simple StartUp file that does not contain anything like MVC etc as it is not needed. However I am getting a 404 not found error from the browser when attempting to negotiate.
The Startup file is as follows:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddSignalR();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors(builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials());
app.UseSignalR(routes =>
{
routes.MapHub<Masterhub>("masterhub");
});
}
}
As you can see, it is very basic, but as I don't want any MVC/Web API functionality, I didn't include all of that setup. There is setup for CORS and SignalR, that is all.
Is what I am attempting to do possible?
It turns out that the JavaScript file I was using from the web client to connect to the self-hosted SignalR Console Application, was the old full .Net version, and not the new version you can get from NPM.

How to configure hangfire with unity?

I have ASP.NET Web API application. The application is using Unity as IoC container. The application is also using Hangfire and I am trying to configure Hangfire to use Unity.
So based on documentation i am using Hangfire.Unity which registers the unity container as a current job activator in Hangfire.
I have a class which has dependency on IBackgroundJobClient
public class MyService
{
private MyDBContext _dbContext = null;
private IBackgroundJobClient _backgroundJobClient = null;
public MyService(MyDbContext dbContext, IBackgroundJobClient backgroundJobClient)
{
_dbContext = dbContext;
_backgroundJobClient = backgroundJobClient;
}
}
However even after configuring Hangfire.Unity it could not create & pass instance of BackgroundJobClient
So i had to register every dependency of BackgroundJobClient with unity container.
Unity Registration
public class UnityConfig
{
private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
public static IUnityContainer GetConfiguredContainer()
{
return container.Value;
}
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<MyDbContext>(new HierarchicalLifetimeManager(), new InjectionFactory(x => new MyDbContext()));
// register hangfire dependencies
container.RegisterType<IBackgroundJobClient, BackgroundJobClient>();
container.RegisterType<JobStorage, SqlServerStorage>(new InjectionConstructor("HangfireConnectionString"));
container.RegisterType<IJobFilterProvider, JobFilterAttributeFilterProvider>(new InjectionConstructor(true));
container.RegisterType<IBackgroundJobFactory, BackgroundJobFactory>();
container.RegisterType<IRecurringJobManager, RecurringJobManager>();
container.RegisterType<IBackgroundJobStateChanger, BackgroundJobStateChanger>();
}
}
OWIN Startup
public class Startup
{
public void Configuration(IAppBuilder app)
{
var container = UnityConfig.GetConfiguredContainer();
Hangfire.GlobalConfiguration.Configuration.UseSqlServerStorage("HangfireConnectionString");
Hangfire.GlobalConfiguration.Configuration.UseUnityActivator(container);
// if i dont call UseSqlServerStorage() above then UseHangfireDashboard() method fails with exception
//JobStorage.Current property value has not been initialized. You must set it before using Hangfire Client or Server API.
app.UseHangfireDashboard();
app.UseHangfireServer();
RecurringJob.AddOrUpdate<MyService>(x => x.Prepare(), Cron.MinuteInterval(10));
}
}
Code is working with such configuration. However i have questions:
Is this the correct way of configuring Unity with Hangfire?
Why do i need to invoke Hangfire.GlobalConfiguration.Configuration.UseSqlServerStorage("HangfireConnectionString") in OWIN startup even though SqlServerStorage is already registered with Unity container as JobStorage?
If i dont invoke UseSqlServerStorage() method in OWIN startup then i get exception on app.UseHangfireDashboard() method.
JobStorage.Current property value has not been initialized. You must
set it before using Hangfire Client or Server API.
I believe there is a problem where you want to kick off Hangfire outside of the Unity ecosystem, but also want Unity to understand how to instantiate the appropriate Hangfire interfaces with the associated implementations. Since Hangfire itself doesn't use Unity, you will need to start up Hangfire with the appropriate configuration, such as the SQL Server connection string, and then use that configuration to inform Unity how to instantiate the Hangfire interfaces. I was able to solve this problem by setting the global Hangfire configuration for SQL and then use that same Hangfire static instance to set up Unity.
Here's example code where first you will see how I start the hangfire dashboard and server with a connection string:
public void Configuration(IAppBuilder app)
{
var configuration = new Configuration(); // whatever this is for you
GlobalConfiguration.Configuration.UseSqlServerStorage(
configuration.GetConnectionString());
GlobalConfiguration.Configuration.UseActivator(
new HangfireContainerActivator(UnityConfig.GetConfiguredContainer()));
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] {new HangfireAuthorizationFilter()}
});
app.UseHangfireServer();
}
As the second example, here's the configuration of Unity for Hangfire; notice how this code is using the static JobStorage Hangfire object to instantiate any requests for JobStorage.
public static void RegisterHangfire(IUnityContainer container)
{
container.RegisterType<JobStorage>(new InjectionFactory(c => JobStorage.Current));
container.RegisterType<IJobFilterProvider, JobFilterAttributeFilterProvider>(new InjectionConstructor(true));
container.RegisterType<IBackgroundJobFactory, BackgroundJobFactory>();
container.RegisterType<IRecurringJobManager, RecurringJobManager>();
container.RegisterType<IBackgroundJobClient, BackgroundJobClient>();
container.RegisterType<IBackgroundJobStateChanger, BackgroundJobStateChanger>();
}
I believe this approach gives you the best of both worlds where you only set up your SQL Server connection once and you do it early to kick off Hangfire, but then you use that instance to tell Unity how to behave.