Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 26 days ago.
Improve this question
Which is the best practise for running a method in project start up?
In meantime i have create a class and a static method in it.
public class SqlServices
{
public static void Init()
{
//Run SqlServices
}
}
And then i just run my static method before application start inside Program.cs.
using WebApplication3;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
SqlServices.Init();
app.Run();
Is this a good practise or there is a better way? Thank you.
public class DbInitializerHostedService : IHostedService
{
public async Task StartAsync(CancellationToken stoppingToken)
{
// The code in here will run when the application starts, and block the startup process until finished
}
public Task StopAsync(CancellationToken stoppingToken)
{
// The code in here will run when the application stops
// In your case, nothing to do
return Task.CompletedTask;
}
}
Then in your Program.cs:
// ...
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHostedService<DbInitializerHostedService>();
// ...
You can use the Dependency Injection container to inject services to this class and further isolate parts of your logic and consume them (for example sharing the connection string or SQL connection between this service and the app itself). Check the documentation I linked above in the comments for examples using an ILogger for the hosted service.
Related
I'm developing an ASP.NET Core MVC web application where I have these two tasks that should be running as background services:
Set the user status as "Expired" if EndOfSubscription date is == DateTime.Now
Before 1 month of EndOfSubscription date send a reminder e-mail to this user
After searching, I found that I can use service worker to implement this. But I'm totally confused how to use this service worker in existing ASP.NET Core MVC web application where I need to access my models and database.
Should I isolate these tasks in a separate service worker project? But in this case should I share the same database for both projects?
Can someone guide me with main steps in this kind of situations?
Thank you in advance.
Service worker or Worker service?
A Service Worker is a way to run background tasks in a browser and definitely unsuitable if you want to execute something on the server.
A Worker service is essentially a template with the (few) calls needed to run a BackgroundService/IHostedService in a console application and (optionally, through extensions) as a Linux daemon or Windows service. You don't need that template to create and run a BackgroundService.
The tutorial Background tasks with hosted services in ASP.NET Core shows how to create and use a BackgroundService but is a bit ... overengineered. The article tries to show too many things at the same time and ends up missing some essential things.
A better introduction is Steve Gordon's What are Worker Services?.
The background service
All that's needed to create a background service, is a class that implements the IHostedService interface. Instead of implementing all the interface methods, it's easier to inherit from the BackgroundService base class and override just the ExecuteAsync method.
The article's example shows this method doesn't need to be anything fancy:
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
}
}
That's just a loop with a delay. This will run until the web app terminates and signals the stoppingToken. This service will be created by the DI container, so it can have service dependencies like ILogger or any other singleton service.
Registering the service
The background service needs to be registered as a service in ConfigureServices, the same way any other service is registered. If you have a console application, you configure it in the host's ConfigureServices call. If you have a web application, you need to register it in Startup.ConfigureServices:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<OrdersContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
...
//Register the service
services.AddHostedService<Worker>();
services.AddRazorPages();
}
This registers Worker as a service that can be constructed by the DI container and adds it to the list of hosted services that will start once .Run() is called in the web app's Main :
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
Using DbContext and other scoped services
Adding a DbContext as a dependency is trickier, since DbContext is a scoped service. We can't just inject a DbContext instance and store it in a field - a DbContext is meant to be used as a Unit-of-Work, something that collects all changes made for a single scenario and either commit all of them to the database or discard them. It's meant to be used inside a using block. If we dispose the single DbContext instance we injected though, where do we get a new one?
To solve this, we have to inject the DI service, IServiceProvider, create a scope explicitly and get our DbContext from this scope:
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly IServiceProvider _services;
//Inject IServiceProvider
public Worker(IServiceProvider services, ILogger<Worker> logger)
{
_logger = logger;
_services=services;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
//Create the scope
using (var scope = _services.CreateScope())
{
//Create OrdersContext in the scope
var ctx = scope.ServiceProvider.GetRequiredService<OrdersContext>();
var latestOrders = await ctx.Orders
.Where(o=>o.Created>=DateTime.Today)
.ToListAsync();
//Make some changes
if (allOK)
{
await ctx.SaveChangesAsync();
}
}
//OrdersContext will be disposed when exiting the scope
...
}
}
}
The OrdersContext will be disposed when the scope exits and any unsaved changes will be discarded.
Nothing says the entire code needs to be inside ExecuteAsync. Once the code starts getting too long, we can easily extract the important code into a separate method :
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
using (var scope = _services.CreateScope())
{
var ctx = scope.ServiceProvider.GetRequiredService<OrdersContext>();
await DoWorkAsync(ctx,stoppingToken);
}
await Task.Delay(1000, stoppingToken);
}
}
private async Task DoWorkAsync(OrdersContext ctx,CancellationToken stoppingToken)
{
var latestOrders = await ctx.Orders
.Where(o=>o.Created>=DateTime.Today)
.ToListAsync();
//Make some changes
if (allOK)
{
await ctx.SaveChangesAsync();
}
}
I have a problem: the service provider is disposed when arrives in the method.
Is this an issue or it is my fault?
My service
public class BomService
{
private readonly IServiceScopeFactory _scope;
public BomService(IServiceScopeFactory scope)
{
_scope = scope;
}
public void ImportAsync(ImportRequestDto importSettings)
{
Task.Run(async () => await ImportFile.ImportAsync<Bom, CatalogContext>(_scope));
}
}
Method
public static async Task ImportAsync<T, TContext>(IServiceScopeFactory parentScope) where T : class where TContext : DbContext
{
using var scope = parentScope.CreateScope();
var repo = scope.ServiceProvider.GetService<IGenericRepository<T, TContext>>();
}
The error:
Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed
Do not run long running tasks in an HTTP request, these should be done in a separate process while returning a response to the client immediately.
Refer to the answer of this question
As also suggested there you can use something like Hangfire to run background processes.
Update
It is not good practice to inject IServiceScopeFactory. Like that you are implementing the Service Locator anti pattern. Instead inject the repository directly and let the DI figure out the resolution and scope.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
I am new to .NET Core, as far as I learned from Microsoft docs, we register services in Startup.ConfigureServices and inject them into classes through the constructors.
Something like the code below
Startup
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<ICar, Car>();
services.AddScoped<IPeople, People>();
}
}
Controller
public class CarController : ControllerBase
{
private readonly ICar _car;
private readonly IPeople _people;
public CarController(ICar car, IPeople people)
{
_car = car;
_people = people;
}
[HttpGet]
public ActionResult Get()
{
var result = _people.Talk() + _car.Run();
return Ok(result);
}
}
But one of my colleagues uses another strategy to achieve the injection, according to what he said, we could write less lines and the injection only happens when we invoke the properties.
(Sorry, I can't tell what is the name of this strategy.)
Startup (same as above)
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<ICar, Car>();
services.AddScoped<IPeople, People>();
}
}
Controller
public class CarController : ControllerBase
{
private readonly IServiceProvider _serviceProvider;
private ICar Car => _serviceProvider.GetRequiredService<ICar>();
private IPeople People => _serviceProvider.GetRequiredService<IPeople>();
public CarController(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
[HttpGet]
public ActionResult Get()
{
var result = People.Talk() + Car.Run();
return Ok(result);
}
}
I am not sure is this correct or not.
Can someone point out the pros and cons between these two strategies?
Thank you.
This is way too close to a service locator , for which there are many (good) reasons not to use.
I can see some specific problems with the second solution. The first being that if the resource is registered as transient (services.AddTransient<ICar, Car>();), then each call of the property will return a new instance. This might be too surprising a behavior to expect from a property.
Next problem is a performance. Calling GetRequiredService is not free. And calling it each time a property is accessed might cause performance issues.
Both could be solved by getting the services in the constructor, like this :
private ICar Car;
private IPeople People;
public CarController(IServiceProvider serviceProvider)
{
Car = serviceProvider.GetRequiredService<ICar>();
People = serviceProvider.GetRequiredService<IPeople>();
}
But that still doesn't solve the problem of service locator, like it not being clear what dependencies a class is using and tying the class to the specific IoC provider. And really, you can save yourself the work and just declare the dependencies in the constructor at this point.
This question already has an answer here:
SignalR call client methods from outside the Hub class AspNetCore
(1 answer)
Closed 5 years ago.
In the old version we use GlobalHost like
var hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
hubContext.Clients.addNotification("Bla la la ");
But how to do it in the new alpha version ?
You need to inject IHubContext<THub> and then should be able to invoke methods.
class HubMethods
{
private IHubContext<THub> _hubContext;
public HubMethods(IHubContext<THub> hubContext)
{
_hubContext = hubContext;
}
public Task WriteMessageAsync(string method, param object[] args)
{
return _hubContext.Clients.All.InvokeAsync(method, args);
}
}
Previously (in asp.net 4.x) there was common practice to use WebActivator class for register bootstrap logic.
I understand that now we have Startup class where everything can be configured. But with WebActivator I had more options - it was possible to drop-in an assembly into an app (add a nuget) and the assembly registered everything it needs on its own. For this an assemble had assembly level attribute with a type which should be called:
[assembly: WebActivator.PreApplicationStartMethod(typeof (ModuleBootstrapper), "Start")]
What is recommended approach for such things ("lib initialization") in the new glory asp.net 5 now?
The functionality you can get with WebActivator is not possible under ASP.NET 5 and I strongly believe that it won't ever be because one of the great things about ASP.NET 5 pipeline is that you are responsible building up your request pipeline. So, the decision should be deliberately made. As an example:
I have a middleware:
public class MonitoringMiddlware
{
private RequestDelegate _next;
public MonitoringMiddlware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
// do some stuff on the way in
await _next(httpContext);
// do some stuff on the way out
}
}
I can package this up and publish to a NuGet feed. Consumer needs to pull this in and add this into the appropriate place inside the pipeline:
public class Startup
{
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// configure services here
}
public void Configure(IApplicationBuilder app)
{
app.UseStatusCodePages();
app.UseFileServer();
// I want middleware to sit here inside the pipeline.
app.UseMiddleware<MonitoringMiddlware>();
app.UseMvc(routes =>
{
routes.MapRoute("areaRoute", "{area:exists}/{controller}/{action}");
routes.MapRoute(
"controllerRoute",
"{controller}",
new { controller = "Home" });
});
}
}
So, whenever I come into this code, I can see how the pipeline is being built without any magic. In the WebActivator case, you would need to look into a few other places to figure out your pipeline and most of all, you wouldn't be making the decision where it sits.
So, it was not a bad thing to get rid of it.