Async IStringLocalizer - GetAllStrings - c#

We have a custom implementation of IStringLocazlizer that Loads labels from our internal company CMS that exposes data via HTTP Rest interface.
We wanted to use NET Core built in locazlier but I do not like the GetAllStrings Sync method that will have to Block on Tasks to perfrom HTTP Call.
We have a cache obvioulsy but I do think that it does not seem right.
Any thoughts on that?
Example:
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
{
Task<CmsLabelsModel> task = pipeline.SendAsync(new GetLabelsModelRequest(ResourceName));
CmsLabelsModel result = task.GetAwaiter().GetResult(); //Yuk Yuk
return result.LabelModels.Select(pair => new LocalizedString(pair.Key, pair.Value.Value));
}

Related

What is the simplest way to run a single background task from a controller in .NET Core?

I have an ASP.NET Core web app, with WebAPI controllers. All I am trying to do is, in some of the controllers, be able to kick off a process that would run in the background, but the controller should go ahead and return before that process is done. I don't want the consumers of the service to have to wait for this job to finish.
I have seen all of the posts about IHostedService and BackgroundService, but none of them seem to be what I want. Also, all these examples show you how to set things up, but not how to actually call it, or I am not understanding some of it.
I tried these, but when you register an IHostedService in Startup, it runs immediately at that point in time. This is not what I want. I don't want to run the task at startup, I want to be able to call it from a controller when it needs to. Also, I may have several different ones, so just registering services.AddHostedService() won't work because I might have a MyServiceB and MyServiceC, so how do I get the right one from the controller (I can't just inject IHostedService)?
Ultimately, everything I have seen has been a huge, convoluted mess of code for something that seems like it should be such a simple thing to do. What am I missing?
You have the following options:
IHostedService classes can be long running methods that run in the background for the lifetime of your app. In order to make them to handle some sort of background task, you need to implement some sort of "global" queue system in your app for the controllers to store the data/events. This queue system can be as simple as a Singleton class with a ConcurrentQueue that you pass in to your controller, or something like an IDistributedCache or more complex external pub/sub systems. Then you can just poll the queue in your IHostedService and run certain operations based on it. Here is a microsoft example of IHostedService implementation for handling queues https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio#queued-background-tasks
Note that the Singleton class approach can cause issues in multi-server environments.
Example implementation of the Singleton approach can be like:
// Needs to be registered as a Singleton in your Startup.cs
public class BackgroundJobs {
public ConcurrentQueue<string> BackgroundTasks {get; set;} = new ConcurrentQueue<string>();
}
public class MyController : ControllerBase{
private readonly BackgroundJobs _backgroundJobs;
public MyController(BackgroundJobs backgroundJobs) {
_backgroundJobs = backgroundJobs;
}
public async Task<ActionResult> FireAndForgetEndPoint(){
_backgroundJobs.BackgroundTasks.Enqueue("SomeJobIdentifier");
}
}
public class MyBackgroundService : IHostedService {
private readonly BackgroundJobs _backgroundJobs;
public MyBackgroundService(BackgroundJobs backgroundJobs)
{
_backgroundJobs = backgroundJobs;
}
public void StartAsync(CancellationToken ct)
{
while(!ct.IsCancellationRequested)
{
if(_backgroundJobs.BackgroundTasks.TryDequeue(out var jobId))
{
// Code to do long running operation
}
Task.Delay(TimeSpan.FromSeconds(1)); // You really don't want an infinite loop here without having any sort of delays.
}
}
}
Create a method that returns a Task, pass in a IServiceProvider to that method and create a new Scope in there to make sure ASP.NET would not kill the task when the controller Action completes. Something like
IServiceProvider _serviceProvider;
public async Task<ActionResult> FireAndForgetEndPoint()
{
// Do stuff
_ = FireAndForgetOperation(_serviceProvider);
Return Ok();
}
public async Task FireAndForgetOperation(IServiceProvider serviceProvider)
{
using (var scope = _serviceProvider.CreateScope()){
await Task.Delay(1000);
//... Long running tasks
}
}
Update: Here is the Microsoft example of doing something similar: https://learn.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?view=aspnetcore-3.1#do-not-capture-services-injected-into-the-controllers-on-background-threads
As I understand from your question you want to create a fire and forget task like logging to database. In this scenario you don't have to wait for log to be inserted database. It also took much of my time to discover an easily implementable solution. Here is what I have found:
In your controller parameters, add IServiceScopeFactory. This will not effect the request body or header. After that create a scope and call your service over it.
[HttpPost]
public IActionResult MoveRecordingToStorage([FromBody] StreamingRequestModel req, [FromServices] IServiceScopeFactory serviceScopeFactory)
{
// Move record to Azure storage in the background
Task.Run(async () =>
{
try
{
using var scope = serviceScopeFactory.CreateScope();
var repository = scope.ServiceProvider.GetRequiredService<ICloudStorage>();
await repository.UploadFileToAzure(req.RecordedPath, key, req.Id, req.RecordCode);
}
catch(Exception e)
{
Console.WriteLine(e);
}
});
return Ok("In progress..");
}
After posting your request, you will immediately receive In Progress.. text but your task will run in the background.
One more thing, If you don't create your task in this way and try to call database operations you will receive an error like this which means your database object is already dead and you are trying to access it;
Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.\r\nObject name: 'DBContext'.
My code is based on Repository pattern. You should not forget to inject service class in your Startup.cs
services.AddScoped<ICloudStorage, AzureCloudStorage>();
Find the detailed documentation here.
What is the simplest way to run a single background task from a controller in .NET Core?
I don't want the consumers of the service to have to wait for this job to finish.
Ultimately, everything I have seen has been a huge, convoluted mess of code for something that seems like it should be such a simple thing to do. What am I missing?
The problem is that ASP.NET is a framework for writing web services, which are applications that respond to requests. But as soon as your code says "I don't want the consumers of the service to have to wait", then you're talking about running code outside of a request (i.e., request-extrinsic code). This is why all solutions are complex: your code has to bypass/extend the framework itself in an attempt to force it to do something it wasn't designed to do.
The only proper solution for request-extrinsic code is to have a durable queue with a separate background process. Anything in-process (e.g., ConcurrentQueue with an IHostedService) will have reliability problems; in particular, those solutions will occasionally lose work.

Show progress on long-running process

I have a few server-side processes that can take very long times to run (30-60m). These are a result of calling a web API from my webserver, and throttling the requests to match the maximum request rate of the service.
I want to be able to display the progress of this on the client, how can I go about doing this? Where do I start?
Previous answers have hinted at SignalR, but that does not seem to be a thing with Asp.Net Core.
Edit: It looks like SignalR is a thing with Asp.Net core. As this is the case, I have a followup question:
I'm using AngularJS and managing the client-side state via AJAX requests to API endpoints. Are there any potential coding or performance "gotchas" when trying to also utilize SignalR in conjunction?
Additionally, are there alternatives that don't add SignalR as a dependency? It looks like it requires a beta-version of .Net Core, I'd rather stay in stable-release-land if at all possible.
You can use SignalR and the IProgress interface to handle this. The basic implementation is something like this:
Server-side
[HubName("messageHub")]
public class MessageHub : Hub // This is your server-side SignalR
{
public async Task Countdown(IProgress<int> progress) // This is your IProgress
{
// 60 second countdown for an example
// NOT TESTED
for (int i = 1; i <= 61; i += 1)
{
await Task.Delay(1000);
progress.Report(i);
}
}
}
Client-side
// Hub connection
//
var messageConnection = $.hubConnection(url);
var messageHub = messageConnection.createHubProxy('MessageHub');
$.connection.hub.logging = true

Proper use of async/await in Web API

I have an existing Business Library, which I want to expose using Web API. My existing business classes looks like
public class Business
{
public bool DoSomeBusiness()
{
//Performing long running DB operations
return true;
}
//Other methods
}
I am writing a Web API method like following code and using asyn/await for better scalability.
public class SampleController : ApiController
{
Business _business;
public ValuesController(Business business)
{
_business = business;
}
public async Task<HttpResponseMessage> Get()
{
var result= await Task.Run(() => _business.DoSomeBusiness());
return Request.CreateResponse(HttpStatusCode.OK, result);
}
}
Is this approach correct? Will I get the real benefit of the asynchronous behaviour? I don’t want to change my existing business layer implementation to make them task based.
This accomplishes nothing. If it did, ASP.NET could just run your action in a Task.Run call automatically and achieve better scalability.
You can't cheat this. You need to use async all the way down to the IOs you're making. This infects everything which is why this should be done when needed and not by default.
I'll link you two basic posts about making this choice because right now you do not understand the topic enough to make the choice:
https://stackoverflow.com/a/25087273/122718 Why does the EF 6 tutorial use asynchronous calls?
https://stackoverflow.com/a/12796711/122718 Should we switch to using async I/O by default?

Can I run WCF on top of WebAPI?

For a work project, I'm building an application consisting of a frontend SPA (Aurelia), a WebAPI backend for that SPA, and a multitude of web service WebAPI projects contained in existing applications. (This application will perform data aggregation - it's a dashboard for our clients to show relevant information from many sources.)
Immediately, I was faced with some challenges. Using WebAPI, we wanted to expose the web services as REST endpoints. This works well for client applications and is very open. However, making server-to-server calls in .NET, I wanted to abstract away the REST calls and simply provide a method-based interface (so I could call, say, new MyWebServiceClient().getOrders() or something like that); I also did not want to have to duplicate data model classes across solutions, or worry about deserializing one JSON model type to another type. (Blegh.)
To achieve this goal, I've created an internal nuget package that a) provides access to the data model classes used in the service via the assembly, and b) provides an interface for HTTP calls, abstracting away the JSON serialization and deserialization, like so:
public async Task<T> Get<T>(string endpoint, IEnumerable<KeyValuePair<string, string>> parameters = null, CancellationToken cancellationToken = default(CancellationToken))
{
var builder = new UriBuilder(Properties.Settings.Default.MyEndpointHost + endpoint);
builder.Query = buildQueryStringFromParameters(parameters);
_httpClient.DefaultRequestHeaders.Accept.Clear();
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
try
{
// After this, we really shouldn't continue.
var request = await _httpClient.GetAsync(builder.Uri, cancellationToken);
if (!request.IsSuccessStatusCode)
{
if (request.StatusCode >= HttpStatusCode.BadRequest && request.StatusCode < HttpStatusCode.InternalServerError)
{
throw new EndpointClientException("Service responded with an error message.", request.StatusCode, request.ReasonPhrase);
}
if (request.StatusCode >= HttpStatusCode.InternalServerError && (int)request.StatusCode < 600)
{
throw new EndpointServerException("An error occurred in the Service endpoint.", request.StatusCode, request.ReasonPhrase);
}
}
var json = await request.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(json);
}
catch (Exception ex)
{
throw;
}
}
The public methods are simply convenience methods, calling that function with the requisite arguments, like this:
public async Task<IEnumerable<MyModel>> SearchMyModelsByFooName(string fooName, CancellationToken cancellationToken)
{
var parameters = new List<KeyValuePair<string, string>>();
parameters.Add(new KeyValuePair<string, string>("searchText", fooName));
return await this.Get<List<MyModel>>("myModel", parameters, cancellationToken);
}
I've had good results with this, although I have to maintain it manually and update dependencies.
However, upon talking to my colleagues, I was introduced to WCF, and it looks as though it solves a lot of the issues I'm trying to solve manually. Looking into this, though, reveals that the setup can be tricky, and we're not sure if it's worth the trouble. (Additionally, we'd have to maintain two APIs.)
Although it's fun, I don't want to reinvent the wheel. Is there a way to bolt WCF on top of WebAPI for server-to-server calls only, or have WCF generate data for WebAPI controllers?
If you don't need to use REST, and personally I don't see any reason to do this in a .NET server to server scenario, you could just create a WCF service to expose your data. Then generate proxy classes in your WebAPI that calls the WCF service.
Using svutil to generate the proxy classes make it easy to adapt to changes in WCF "API". And you will have, as you call it, a method based interface to interact with the WCF service/s.
Reference for using svutil:
https://msdn.microsoft.com/en-us/library/ms733133(v=vs.110).aspx

OData from Silverlight without Repository Pattern

I am trying to create a sample app using OData and Silverlight, using (what else?) the Netflix service. I've already succeeded in creating the app using WPF, but am struggling to port my service class to an async model.
My existing service class (simplified) looks like this:
public IEnumerable<Title> BlockingSearch(TitleSearchParam param)
{
var catalog = new NetflixCatalog(new Uri("http://odata.netflix.com/Catalog/"));
return catalog.Titles.Where(t =>
t.Instant.AvailableFrom > param.InstantStartDate && t.Instant.AvailableFrom < param.InstantEndDate &&
(string.IsNullOrEmpty(param.TitleName) || t.Name.Contains(param.TitleName))).ToList();
}
All of the examples of consuming OData asynchronously employ some kind of Respository Pattern and/or require an instantiated collection to be passed in. I would like to model the method signature for the Silverlight/Async call to look something like this (with the service class itself being stateless):
public void AsyncSearch(TitleSearchParam param, Action<IEnumerable<Title>> completedCallback, Action<MyErrorClass> errorCallback, object callBackOwner)
{
}
I think I could so something along the lines of what MS outlines in Calling Synchronous Methods Asynchronously, but I was hoping there was a more elegant solution that I was missing.
As you already know, you can always run the call on a different thread than the UI thread, so there will be no blocking per se. That seems like a decent enough solution. Then you can (in fact, must) use dispatchers to handle results coming from callbacks if you intend to use them with UI.

Categories

Resources