Async load data in BaseController, how to make controller constructor async - c#

I run .net core 6 version, and I have no idea how to make constructor of my controller (BaseController) async, because i am calling async service to load items in Menu. Menu is on every page, so any other of my controllers (AccountController, OrderController) inherits from my BaseController.
It must be loaded in constructor, just on creating the controller, I cant hang on it on another action. Right now, when i just make it sync, on first page call there are no preloaded data, because it's not waiting on completition.
public class BaseController : Controller
{
private readonly ICategoryService _categoryService;
protected readonly IMapper _mapper;
protected LayoutViewModel _layoutViewModel = new LayoutViewModel();
public BaseController(ICategoryService categoryService, IMapper mapper)
{
_categoryService = categoryService;
_mapper = mapper;
LoadRankedCategories();
}
public void LoadCategories()
{
var categories = _categoryService.GetCategoriesAsync();
_layoutViewModel.Categories = _mapper.Map<IEnumerable<MenuCategoryViewModel>>(categories);
}
}

Ok, on controller construction start the asynchronous task and save said task in a field. Then make sure you await said task whenever you need the results.
public class BaseController : Controller
{
private Task _categoriesTask;
public BaseController()
{
_categoriesTask = LoadCategoriesAsync();
}
private async Task LoadCategoriesAsync()
{
var rawCategories = await _categoryService.GetCategoriesAsync();
return _mapper.Map<IEnumerable<MenuCategoryViewModel>>(rawCategories);
}
[HttpGet]
public async Task<IActionResult> SomeGet()
{
// Await the class-level task.
var categories = await _categoriesTask;
...
}
}
NOTE: As I see the sample here, however, you should do this in the static constructor or use some caching mechanism like memcached (if microservices or distributed) or Redis, assuming the categories never change. So take the solution as a purely academic response on how to overcome the problem. In reality, this doesn't feel right for the stated reason.
Finally, I wouldn't recommend this at all unless there is absolutely no other way: You can spawn a new thread and block it with Result. This might carry undesired issues, so use it at your own risk.
public BaseController()
{
// This will get you the actual categories.
_categories = Task.Run(() => LoadCategoriesAsync()).Result();
}

Related

Using UnitOfWork design at different endpoints. Can't tracking entity updates when I try to apply savechanges. #.net (UnitOfWork design)

If I use the two service's method at same action like below It is working.
[HttpPost, Route("save_changes")]
public async Task<IActionResult> SaveChangesAsync()
{
_logService.CreateLog(logCreateModel);
await _unitOfWork.SaveChangesAsync();
return Ok();
}
But I need to call the entity updating and SaveChanges methods at different action Because I want to send request from different API.Like this;
[HttpPost, Route("create")]
public async Task CreateLog(LogCreateModel logCreateModel)
{
await _logService.CreateLog(logCreateModel);
}
[HttpPost, Route("save_changes")]
public async Task<IActionResult> SaveChangesAsync()
{
unitOfWork.SaveChangesAsync();
return Ok();
}
and this my UnitOfWork class;
public class UnitOfWork : IUnitOfWork
{
private readonly PrimeLogDatabaseContext _context;
private IRepository<Log> _log;
private IRepository<LifeCycle> _lifeCycle;
public UnitOfWork(PrimeLogDatabaseContext context)
{
_context = context;
}
public IRepository<Log> log => _log ?? new LogRepository(_context);
public IRepository<LifeCycle> lifeCycle => _lifeCycle ?? new LifeCycleRepository(_context);
public async Task SaveChangesAsync()
{
await _context.SaveChangesAsync();
}
public void SaveChanges()
{
_context.SaveChanges();
}
}
How I can?
P.s:
I want to create more than one log and at the end of the request I want commit all logs to database.
I used the UnitOfWork design because I have to seperate SaveChanges method from creation process.
At Another API I have action which including CreateLog request services. And I send request to SaveChanges with using the attribute that trigger when action result.
After months ; I solved this problem with another design pattern. I used to UnitOfWork structure at one Api(working as middleware one) and used chain of responsibility pattern.

Fire and Forget database-related method in Hosted Service

I'm having issues finding a solution to my problem and mostly because i don't understand them completely so, here I am asking your support.
I need to fire and forget a method that selects and updates records from database, the problem is I have a 15seconds range between record creation and its appearance in my database (synchronization issue not fixable by me, so i have to accept it) without freezing user's interface and meanwhile letting it create other records.
I tried to simply Task.Run(method) it but every time it's fired the dbContext it's refreshed so nothing is done.
Googling around I found a lot of IHostedService and BackgroundService solutions but i really can't get to the point in them: if I understand what i'm trying to do, I need to call an Hosted Service and passing them a scoped version of my dbContext so every fired method will have it's own dbContext and they could work simultaneously. But can't really get HOW TO DO that.
I managed my code in various layers and repositories, so I'll try to be as clear as possible.
Controller:
public class RMAController : BaseController
{
private readonly ApplicationServiceRecords applicationServiceRecords;
public RMAController(ApplicationServiceRecords
applicationServiceRecords,
IConfiguration configuration,
AuthenticationService authenticationService,
AuthorizationService authorizationService)
: base(
configuration,
authenticationService,
authorizationService)
{
this.applicationService = applicationService;
this.applicationServiceRecords= applicationServiceRecords;
}
[HttpPost]
[Authorize]
public async Task<IActionResult>CreateRecord(
ResponseCreateRecord viewModel)
{
if (!ModelState.IsValid)
return Content("Error X");
//this is the method i want to fire and forget
await ApplicationServiceRecords.CreateRecordAsync(viewModel);
return RedirectToAction("TestPage");
}
}
Inside "CreateRecordAsync" i call other method's from Domain Layer that create, waits and update the record (again, can't get rid of waits nor create it without the need to update it immediately)
I tried using BackgroundService, this way:
public class BackgroundWorkerQueue
{
private ConcurrentQueue <Func< CancellationToken,Task >> _workItems = new ConcurrentQueue < Func < CancellationToken,Task >> ();
private SemaphoreSlim _signal = new SemaphoreSlim(0);
public async Task < Func < CancellationToken,
Task >> DequeueAsync(CancellationToken cancellationToken) {
await _signal.WaitAsync(cancellationToken);
_workItems.TryDequeue(out
var workItem);
return workItem;
}
public void QueueBackgroundWorkItem(Func < CancellationToken, Task > workItem) {
if (workItem == null) {
throw new ArgumentNullException(nameof(workItem));
}
_workItems.Enqueue(workItem);
_signal.Release();
}
}
public class LongRunningService: BackgroundService {
private readonly BackgroundWorkerQueue queue;
private readonly ILogger < LongRunningService > _logger;
private readonly MyContext _dbcontext;
public LongRunningService(BackgroundWorkerQueue queue, ILogger < LongRunningService > logger, IServiceProvider serviceProvider) {
_logger = logger;
_dbcontext = serviceProvider.CreateScope().ServiceProvider.GetRequiredService <MyContext > ();//thought thiw could be the solution, yet nope (probably can't get how to use it)
this.queue = queue;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var workItem = await queue.DequeueAsync(stoppingToken);
await workItem(stoppingToken);
}
}
}
Added them in startup:
services.AddHostedService<LongRunningService>();
services.AddSingleton<BackgroundWorkerQueue>();
And Fired the method using(?) this from controller :
_backgroundWorkerQueue.QueueBackgroundWorkItem(async token =>{
ApplicationServiceRecords.CreateRecordAsync(viewModel); });
But I got "Invalid attempt to call ReadAsync when reader is closed." on first attemp using DB in a simple select client = await repository.GetClienteByIdAsync(client.Id);
And that's all.
I'm sorry for bad english/ bad programming/bad explanation, and thank you in advance to everyone'll help.

Use NETCore ScopedService outside Controller

First, let me explain breafly what I have, what I want to achieve, how I did it so far, and why I'd like to improve my current implementation.
WHAT I HAVE
Basically, I have a .NET Core project that runs an API Service with some APIs.
Also, I have a class called MyFundamentalClass, which is used throughout the whole application: in fact, MyFundamentalClass implements the Singleton Pattern, having something like this:
public class MyFundamentalClass {
private static _myFundamentalClass = null;
public static MyFundamentalClass GetInstance() {
if (_myFundamentalClass == null)
_myFundamentalClass = new MyFundametalClass();
return _myFundamentalClass;
}
}
Reason why I want this Singleton Pattern is that this class is used in many occasion in the whole project; the alternative would be to instantiate the class in the ControllerAction, and then pass it basically EVERYWHERE: it's not sustainable.
THE PROBLEM
As you can imagine, here was the first problem: each request MUST HAVE its own instance of MyFundamentalClass. As you can imagine, static keyword does not work very well with that.
Why I'm telling this: if I want an instance of MyFundamentalClass in each ControllerAction, I should write something like this:
public async Task<ActionResult> GetUserData() {
MyFundamentalClass = new MyFundamentalClass();
return await MyFundametalClass.GetUserData();
}
So far so good, but as I said, I need the Singleton Pattern, so I should change the code into:
public async Task<ActionResult> GetUserData() {
MyFundamentalClass = MyFundamentalClass.GetInstance();
return await MyFundametalClass.GetUserData();
}
What's the problem? Two different API calls will overwrite the private field MyFundamentalClass._myFundamentalClass, mixing the context of the two API. HUGE PROBLEM!
MY CURRENT SOLUTION
What I found , the only way, was the use of AsyncLocal<MyFundamentalClass>. I've something like this:
public class RequestContext {
publicv static AsyncLocal<MyFundamentalClass> Instance = new AsyncLocal<MyFundamentalClass>(null);
}
// Then, in each ControllerAction
public async Task<ActionResult> GetUserData() {
var method = async () => {
RequestContext.Instance.Value = new MyFundamentalClass();
// Whatever I need to do
}
}
// Then, in the MyFundamentalClass
public MyFundamentalClass {
public MyFundamentalClass GetInstance() {
return RequestContext.Instance.Value;
}
}
With this solution, since the AsyncLocal context lives only thourghout the async context, it perfectly fits my need.
Though, why am I searching for something else? Because I feel like I am missusing the Dependency Injection and the whole ServiceProvider stuffs.
WHAT I AM LOOKING FOR
So.. I also come upon the services.AddScoped<MyFundamentalClass>() code (Link to Microsfot DOC).
By what it tells, it should perfectly fit my need: what is created by that AddScoped lives only for the API -> one API one instance.
But, problem is: how could I exploit the instance created by AddScoped with the Singleton Pattern?
I know that, with DependencyInject, in my Controller I can add the object in the constructor:
public class MyController : ControllerBase {
private MyFundamentalClass _myFundamentalClass;
public MyController(MyFundamentalClass myFundamentalClass) {
_myFundamentalClass = myFundamentalClass;
}
public async Task<ActionResult> GetUserData() {
return await _myFundamentalClass.GetUserData();
}
}
That feels much more correct, from a code point of view, but.. I don't have the SingletonPattern anymore, unless I still use the AsyncContext.
What I thought it was possible was to use:
public static IServiceProvider ServiceProvider;
public static WorkbenchViewModel GetInstance() {
ServiceProvider.GetService(typeof(WorkbenchViewModel));
}
But I have the same problem: each request has its own IServiceProvider, thus different API would override the value.

FromResult or IServiceScopeFactory in Blazor server-side

So I've been injecting IServiceScopeFactory to my pages to use it's scope when getting my data through EF Core.
But yesterday I stumbled upon somebody using Task.FromResult when calling the database.
Is one preferred over the other and if so why?
Thank you in advance!
Ex Task.FromResult
//In code behind
[Inject]
IMyService myService { get; set; }
protected void GetSomeData()
{
var someData = await myServie.GetSomeData();
}
//From serviceClass
public async Task<List<SomeData>> GetSomeData(int id)
{
return await Task.FromResult(db.SomeTable.Where(x => x.Id == qualifierVersionId).AsEnumerable());
}
Ex
//In code behind
[Inject]
IServiceScopeFactory ScopeFactory { get; set; }
protected void GetSomeData()
{
using (var serviceScope = ScopeFactory.CreateScope())
{
var myService = serviceScope.ServiceProvider.GetService<IMyService>();
var someData = await myServie.GetSomeData();
}
}
//From serviceClass
public async Task<List<SomeData>> GetSomeData(int id)
{
return await db.SomeTable.Where(x => x.Id == id).ToListAsync();
}
Edit (because the questions of why I want to know which way to go)
I need to use one or the other because when using Server side Blazor the lifespan makes a Scoop behave like a Singleton. So if I for instance call return await db.SomeTable.Where(x => x.Id == id).ToListAsync(); without the IServiceScopeFactory it will live until you close the website. So this creates an error: 'A second operation started on this context before a previous operation completed.'.
You can't compare the two as they don't have anything in common.
Task.FromResult just creates a task that's completed successfully with the specified result. I believe one of the main use cases for using Task.FromResult is when you're implementing an async interface but your implementation is synchronous, as in the example above. This is not really a good idea on DB calls as they could take a long time.
I'm not sure why you're using the IServiceScopeFactory above. If it's so that the service you're requesting is scoped to the lifetime of the component you're requesting it in then there is a better way.
public class MyComponent : OwningComponentBase<IMyService>
{
protected void GetSomeData(int id)
{
var someData = await Service.GetSomeData(id);
}
}
public class MyService : IMyService
{
public async Task<List<SomeData>> GetSomeData(int id)
{
return await db.SomeTable.Where(x => x.Id == id).ToListAsync();
}
}
By using OwningComponentBase a service scope is created for you under the hood and the service is exposed by the base component via the Service property. I've written a blog post on it if you want to know more.

ASP.NET Core - How to reuse methods created in one controller in another controller?

One way is to create use class inheritance, but is there any other way I could reuse methods that I created in one controller in another controller?
EDIT: should I use a custom basecontroller like this?
public class BaseController : Controller
{
private readonly ApplicationDbContext _context;
private readonly IIdentityService _identityService;
public BaseController(ApplicationDbContext context, IIdentityService identityService)
{
_context = context;
_identityService = identityService;
}
public BaseController()
{
}
//reusable methods
public async Task<Account> GetAccount()
{
//code to do something, i.e query database
}
}
public class MyController : BaseController
{
private readonly ApplicationDbContext _context;
private readonly IIdentityService _identityService;
public MyController(ApplicationDbContext context, IIdentityService identityService)
{
_context = context;
_identityService = identityService;
}
public async Task<IActionResult> DoSomething()
{
var account = await GetAccount();
//do something
Return Ok();
}
}
There are several aspects we want to touch:
if code you have is useful in all controllers, most of the time it is good practice to create BaseApiController that will inherit from ApiController and put things that are used across all controllers there. (You also inherit your controllers from that class of course)
if code is some kind of business logic and is not strictly speaking related to handling http request one way or another ( i.e. you have model Square and you want to calculate area and return it from controller method). Things like that you want to refactor to specific service which might or might not be model, dedicated service, static class or something completely different
Generally when you find something you want to use in two places it's a candidate to be spun into it's own class. Then both controllers can use the common class.

Categories