Hangfire recurring job at a specific time ASP MVC - asp.net

Ive been recently using hangfire to process lengthy jobs enabling me to return API calls more efficiently within an ASP MVC Core application.
i have implimented this by adding the following to startup.cs
public class Startup
{
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddCors(options => options.AddPolicy("AllowAll", p => p.AllowAnyOrigin()));
services.AddHangfire(configuration => configuration.UseSqlServerStorage(Configuration.GetConnectionString("DefaultConnection")));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseCors("AllowAll");
app.UseMvc();
app.UseHangfireDashboard();
app.UseHangfireServer();
}
and then calling this within an action on a controller
using (new BackgroundJobServer())
{
formData.DateCreated = DateTime.Now;
formData.Source = "Web";
formData.StatusItem = _context.Status.FirstOrDefault(x => x.Default == true);
_context.Lead.Add(formData);
_context.SaveChanges();
}
Now i have a need to send a an email at 1am every day with the status of records from the database.
Im slightly confused on the implementation from the following perspective:
-Where do i implement the background job?
-Do i put the call in a method, if so how is this called/
-I cannot find BackgroundJob.AddOrUpdate, which i understand is used for the recurring tasks
-The schedule method takes a timespan object, but all examples are using CRON.
I have all the code to create the email, i just need help scheduling it
Thanks for your help in advance.

Related

No service for type 'CommanderContext' has been registered

I am using .NET 5.0 and HotChoclate framework to create a GraphQL API.
Below is my ConfigureServices Method
public void ConfigureServices(IServiceCollection services)
{
services.AddPooledDbContextFactory<CommanderContext>(options =>
{
options.UseSqlServer(_configuration.GetConnectionString("default"));
});
services.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddFiltering()
.AddSorting()
.AddProjections();
}
To resolve the concurrency issues. I am using AddPooledDbContextFactory() method.
https://chillicream.com/docs/hotchocolate/integrations/entity-framework.
Below is my Configure Method
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
InitializeDatabase(app);
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGraphQL();
});
}
private void InitializeDatabase(IApplicationBuilder app)
{
using var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope();
scope.ServiceProvider.GetRequiredService<CommanderContext>().Database.Migrate(); //Here I get the exception.
}
Since the application is in development, I want the migrations to be automatically applied to the database.
But when I try and get instance of my context class in InitializeDatabase() method, I get the above exception.
Now I have a little understanding that instead of getting a single instance my context class I am getting back a pool of context class.
My Question is: How to I automatically apply the migrations to my database.
Any help is appreciated.
Thanks
AddPooledDbContextFactory method registers IDbContextFactory<TContext> instead the DbContext itself, that's why you can't use GetRequiredService<TContext>. Instead, you should retrieve the factory, use it to obtain TContext instance, do your work and then Dispose it (which in fact will return it to the pool). You can't rely on DI to do that for you because it is not resolved (thus not maintained) by the DI system, but from another abstraction (called factory pattern).
Something like this:
using var context = scope.ServiceProvider
.GetRequiredService<IDbContextFactory<CommanderContext>>()
.CreateDbContext();
context.Database.Migrate();

Configuring Rebus in a .net core Worker Service (or a Console App)

I have seen that Adding rebus in the ASP.NET Core execution pipeline is very neat using Startup.cs.
I wonder if there is a same neat way to do the same for Worker service or generally a console app.
Most .net core console apps I have seen are very simple demo applications.
Kindly if there is any concrete sample configuration using .net core console application.
Regards
Amour Rashid
One way would be to add Microsoft's Microsoft.Extensions.Hosting package and build your background service as a BackgroundService:
public class MyBackgroundService : BackgroundService
{
readonly IServiceCollection _services = new ServiceCollection();
public BackgroundService()
{
// configure the bus
services.AddResbus(
configure => configure
.Transport(t => t.Use(...))
);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using var provider = _services.BuildServiceProvider();
// start the bus
provider.UseRebus();
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken);
}
}
}
which you then add by going
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<MyBackgroundService>();
});
in your startup.
Thanks Mogens,
Another way is to
var host =CreateHostBuilder(args).Build();
host.UseRebus();
host.Run();
Or in ConfigureServices method
....
var provider=services.CreateServiceProvider();
provider.UseRebus();
It helped me I could create Worker Services using rebus.

System.InvalidOperationException: Scheme already exists: Identity.Application

I wanted to add my own custom data for users so I was following the tutorial
Add, download, and delete custom user data to Identity in an ASP.NET Core project
I already had an existing application so I could not follow that tutorial line by line (my existing application has a database of users already). I didn't get very far in it when I hit the error: System.InvalidOperationException: Scheme already exists: Identity.Application
I used the scaffolder to try to add ... (? the code ?)
I've gone through the links below but to no avail
Scheme already exists: Identity.Application #8223 seems most relevant
InvalidOperationException: Scheme already exists ... #1412
AddIdentity() fails "InvalidOperationException: Scheme already exists: Identity.Application"
It seems like a lot of other people add problems with calling the identity twice but I'm only calling it once in my code. I've also tried commenting out the line entirely in the startup and then it says there is none defined and gets mad at me. I've also tried switch form the default as shown below.
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<IdentityUser, IdentityRole>()
// services.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<WebApp1.Models.WebApp1Context>()
.AddDefaultTokenProviders();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc();
}
I feel like I shouldn't be getting the exception thrown and yet.... any advice on a fix?
edit: relevant steps i took until i got this error.
Create project to use invidual user accounts in process creation
override with scaffolder,
and create a secondary user model that you can override.
migrate and update database run.
Try renaming your IdentityUser class to something unique from AspNetIdentity classes. Then make sure you are inheriting from IdentityUser
For example here is a class
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsDisabled { get; set; }
}
And this is the startup
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<IdentityContext>()
.AddDefaultTokenProviders();
The marked answer is not the correct answer. I had the same issue and the reason is that when adding ALL identity scaffolding (via dotnet aspnet-codegenerator as Visual Studio crashes ),it creates a IdentityHostingStartup class under areas/identity. This class duplicates the identity setup in startup.cs. So deleting this class fixed the problem.
I had the same error, but the problem was that I called it twice:
_ = services.AddDefaultIdentity<IdentityUser>(options =>
options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
This ASP Identity Core InvalidOperationException is thrown mostly, when a duplicate call to function exists in Startup.cs or any class in the project using ASP Identity Core, what is required for ASP Identity to work in Startup class:
public void ConfigureServices(IServiceCollection services)
{
_ = services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("AmpCoreDb")));
_ = services.AddDefaultIdentity<IdentityUser>(options =>
options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
_ = services.AddScoped<AuthenticationStateProvider,
RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();
_ = services.AddDatabaseDeveloperPageExceptionFilter();
}

Can I eliminate this linkage in UseExceptionHandler DI

I inject a custom error logging and reporting service into my services collection during Startup.ConfigureServices.
services.AddTransient<IErrorService, ErrorService>();
I then add a global error trap in Startup.Configure.
app.UseExceptionHandler("/Home/Error");
Then in Home.Error, I have my DI for ErrorService, and I pass it my context, and all works perfectly.
Is there some way to eliminate the linkage to Home.Error, and just invoke my ErrorService right in Configure? But I can't figure out how to get linkage to ErrorService at this point in Startup.
Note, two of the other services I inject are my EmailService and my ProgramSettings. I need both of these in ErrorService, so I do need ErrorService invoked in such a way that standard .NET Core DI is wired up.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseExceptionHandler(a => {
a.Run(ctx => {
var exceptionFeature = ctx.Features.Get<IExceptionHandlerPathFeature>();
... execute ErrorService here somehow????????
ctx.Response.StatusCode = StatusCodes.Status500InternalServerError;
return Task.CompletedTask;
});
});

Global exception handling in ASP.NET 5

How can I attach my own logging logic to an ASP.NET 5 application to handle each exception thrown in the business logic and lower layers?
I tried with own ILoggerProvider implementation and loggerfactory.AddProvider(new LoggerProvider(Configuration)) in Startup.cs. But it seems that it intercepts inner ASP.NET stuff, and not my thrown exceptions in lower layers.
Worked it out, by using two options:
1) ILoggerProvider
Implement your own ILoggerProvider and ILogger from the namespace Microsoft.Framework.Logging Then attach it to the MVC Framework in Startup.cs add following code:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory)
{
loggerfactory.AddProvider(new YourCustomProvider());
}
But this above option, seems to only call the Write function of the ILogger on MVC specific events, routing related and so on, it wasn't called when I threw exceptions on my lower layers, so the second option worked out:
2) Global Filter
Register your own ActionFilterAttribute implementation in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().Configure<MvcOptions>(options =>
{
options.Filters.Add(new YourCustomFilter());
});
}
It's important, that the custom filter class implements the IExceptionFilter interace:
public class YourCustomFilter : ActionFilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext context)
{
///logic...
}
}
(EDIT:)
And then in the Startup class we add the filter:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Filters.Add(new YourCustomFilter());
});
}
If you want a really global exception trap framework, not just at the controller level, take a look at one of my open source projects. I plan to make it into a Nuget Package soon after the holidays. I'm also planning to update my blog showing why I developed it.
The open source project is on github:
https://github.com/aspnet-plus/AspNet.Plus.Infrastructure
Take a look at the sample for usage.

Resources