The app is an .Net 6.0 API and it has a background service that has to send messages in bulk. It will be along running process. I have a flag in the service logic which when turned on can only send the messages. How can I invoke this background service using and API endpoint in my application? And then I call invoke this periodically using Hangfire.
I have similar use case, what I did is make that background service injectable, so I can then use it with my API call.
So in the API constructor, inject the background service and trigger the on write method to perform that specific operation. Example below -
Register the background service in your Program.cs
services.AddSingleton<SchedulerBackgroundService>();
services.AddSingleton<IHostedService, SchedulerBackgroundService>(serviceProvider => serviceProvider.GetService<SchedulerBackgroundService>());
Then in your API endpoint, inject in the constructor and trigger on each API call
private readonly SchedulerBackgroundService _schedulerBackgroundService;
public SchedulerController(SchedulerBackgroundService schedulerBackgroundService)
{
_schedulerBackgroundService = schedulerBackgroundService;
}
[HttpGet]
public async Task<ActionResult> SomeScheduleraction()
{
await _schedulerBackgroundService.WriteValueAsync(…);
return Ok();
}
You can inject this in Hangfire too
full bg service as below
public class SchedulerBackgroundService : BackgroundService
{
private Channel<SchedulerModel> _channel;
public SchedulerBackgroundService()
{
_channel = Channel.CreateUnbounded<SchedulerModel>(new UnboundedChannelOptions
{
SingleReader = true,
SingleWriter = true,
});
}
public async Task WriteValueAsync(SchedulerModel data)
{
await _channel.Writer.WriteAsync(data);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
do
{
while (_channel.Reader.TryRead(out var data))
{
try
{
switch (data.Type)
{
...
}
}
catch //(Exception ex)
{
}
}
} while (await _channel.Reader.WaitToReadAsync(stoppingToken));
}
}
Related
I have created an Identity Server using .NET Core and IdentityServer4, I have set of APIs and all calls to these APIs must be authenticated but these APIs might be used by third-party applications so clients can be dynamic
Till now example I am finding is set Clients on startup statically like
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients());
}
public class Config
{
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("resourceApi1", "API Application1")
new ApiResource("resourceApi2", "API Application2")
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "clientApp1",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = { "resourceApi1" }
}
};
}
}
Is there a way in IdentityServer implementation to register Client Apps and set dynamically
For Example, If I have APIs
1. resourceApi1
2. resourceApi2
Each third-party APIs should be able to register and we should be able to generate ClientID and Client secret for each with what resources they can access and Identity Server authenticates that ClientID and Client Secret?
There is an option to use a client store. By default identity server uses in memory store for finding clients:
services.AddIdentityServer()
.AddInMemoryClients(Clients)
You can change this and register your own client store
services.AddIdentityServer()
.AddClientStore<MyClientStore>()
and implement the service
class MyClientStore : IClientStore
{
public Task<Client> FindClientByIdAsync(string clientId)
{
}
}
This would solve dynamic lookup of clients. For registration of clients and their management, you would need to implement your own infrastructure.
First of all configure your IdentityServer using EntityFramework, Then you need to some Apis to add Client and ApiResources.
IdentityServer has its own implementation of Stores using EntityFramework.
You can add new Api for this purpose.
[Route("api/[controller]")]
[ApiController]
[AllowAnonymous]
public class DynamicController : ControllerBase
{
private readonly ConfigurationDbContext context;
public DynamicController(ConfigurationDbContext context)
{
this.context = context;
}
[HttpPost]
public async Task<ActionResult> AddApiResource(ApiResource apiResource)
{
context.ApiResources.Add(apiResource);
await context.SaveChangesAsync();
return Ok();
}
[HttpPost]
public async Task<ActionResult> AddClient(Client client)
{
context.Clients.Add(client);
await context.SaveChangesAsync();
return Ok();
}
}
Can I create an Asp.Net core application that can essentially mimic a windows service and just constantly run in the background? I suspect you can but I am unsure which application type to use (ie. console app, web app, etc.).
Scenario: This is a very niche situation as it will be created for a cloud based environment we are using, Siemens MindSphere. We have an application in the cloud already that can read from a PostgreSQL database, but we need a backend service app which every hour on the hour can call MindSphere Api's, receive data from it and populate a field in the above database with this data. Is this possible using .net core?
You can use Background tasks. Example of timed task:
internal class TimedHostedService : IHostedService, IDisposable
{
private readonly ILogger _logger;
private Timer _timer;
public TimedHostedService(ILogger<TimedHostedService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is starting.");
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
private void DoWork(object state)
{
_logger.LogInformation("Timed Background Service is working.");
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
Registration in Startup.cs in ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHostedService<TimedHostedService>();
...
}
I have a stateful service that stores a bunch of data about my users that is stored in a reliable dictionary and obviously also retrieves it from there too.
However, I also have a SQL database that used to store this info. On initialization of a new stateful service instance, I need to migrate that info from my SQL database into the new reliable storage mechanism. From that point on, the stateful service is the source of truth. Ideally, I'd like to delay availability of my stateful service until this initialization process is completed.
Are there any suggestions on an approach for how to do this?
Something like does will do the trick:
public interface IStateful1 : IService
{
Task MyMethod();
}
internal sealed class Stateful1 : StatefulService, IStateful1
{
private bool isReady = false;
public Stateful1(StatefulServiceContext context)
: base(context)
{ }
public Task MyMethod()
{
if(!isReady)
throw new NotImplementedException(); // Probably throw or return something more meaningful :-)
return Task.CompletedTask; // Do your thing here
}
protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
{
return new ServiceReplicaListener[0];
}
protected override async Task RunAsync(CancellationToken cancellationToken)
{
await Task.Run(() => {
// Simulation of some work
Thread.Sleep((int)TimeSpan.FromMinutes(5).TotalMilliseconds);
});
isReady = true;
}
}
In this setup the import from the DB into the reliable collection is done in the RunAsync method.
Unfortunately, AFAIK, there is not way to plug in the communication listeners at a later time. That would make things way easier.
If CreateServiceReplicaListeners would be an async operation we could await the initialization task here, but we can't right now. Using .Wait() is not going to work as it will report that the instance is taking to long to get running and will mark the instance as unhealthy.
A complete overview of the lifecycle of a service can be found in the docs
I am not sure if I got you right. But based on your comment I would suggest the following solution for returning the 'Not ready' response during the migration.
public interface IMigrationService
{
bool IsDone();
}
public class MigrationService : IMigrationService
{
private bool migrating = tu;
public bool BeginMigration()
{
this.migrating = true;
}
public bool EndMigration()
{
this.migrating = false;
}
public bool IsDone()
{
return this.migrating;
}
}
// WebHost startup class
public class Startup
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// Register a middle-ware that would short circuit responses
// while migration is in progress.
app.Use(
async (context, next) =>
{
var migrationService =
context.RequestServices.GetService<IMigrationService>();
if (!migrationService.IsDone())
{
/* short circuit the response with approriate error message */
}
await next();
});
app.UseMvc();
}
}
public class Stateful : StatefulService
{
private readonly IMigrationService migrationService;
public Stateful(StatefulServiceContext context)
: base(context)
{
this.migrationService = new MigrationService();
}
protected override IEnumerable<ServiceReplicaListener>
CreateServiceReplicaListeners()
{
/*
Create a listener here with WebHostBuilder
Use Startup class with the middle-ware defined and
add configure services -> .ConfigureServices()
with services.AddSingleton<IMigrationService>(this.migrationService)
*/
}
protected override async Task
RunAsync(CancellationToken cancellationToken)
{
this.migrationService.StartMigration();
/* Migration code */
this.migrationService.EndMigration();
}
}
This would allow you to roll-out a new version of the service that would short circuit all requests with appropriate error message while the migration is in progress.
Hope this helps.
I'm trying to do ASP.NET Core 2 api with windows authentication. I need some unusual authorization requirements so I decided to create my own requirement for a policy.
public void ConfigureServices(IServiceCollection services)
{
(...)
services.AddAuthorization(options =>
{
options.AddPolicy("MyPolicy", policy => policy.AddRequirements(new MyRequirement())
);
});
}
My requirement:
public class MyRequirement: IAuthorizationRequirement
{
(...)
}
Handler for it:
public class MyHandler: AuthorizationHandler<MyRequirement>
{
public IService userService;
public MyHandler(IService service)
{
this.service = service;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyRequirement requirement)
{
(...)
return Task.CompletedTask;
}
}
And obviously, Authorize attribute for my method:
[Authorize(Policy = "MyPolicy")]
public IEnumerable<string> GetAll()
{
(...)
}
But when I try to access such a method I get:
InvalidOperationException: No authenticationScheme was specified, and
there was no DefaultForbidScheme found.
I wasted a lot of time trying to fix it. Why is it happening and how can I get it working?
Everything happens locally, on IISExpress.
When start Asp.Net Core I have to call the database through EntityFrameworkCore, I have to run it only once during user "Session"
Any suggests ?
I usually use IHostedService. There is a great article on this by Andrew Lock.
In essence, what you want to do is implement the IHostedService interface and put your DB code in the StartAsync method.
public class MigratorHostedService: IHostedService
{
// We need to inject the IServiceProvider so we can create
// the scoped service, MyDbContext
private readonly IServiceProvider _serviceProvider;
public MigratorStartupFilter(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
// Create a new scope to retrieve scoped services
using(var scope = _seviceProvider.CreateScope())
{
// Get the DbContext instance
var myDbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();
//Do the migration asynchronously
await myDbContext.Database.MigrateAsync();
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
// noop
return Task.CompletedTask;
}
}
This code was taken directly from the article mentioned and is only placed here to answer the question asked. All credit must go to Andrew Lock