Merge asyc method into one task - c#

Not sure if this is possible or if it already does something simpler with chained tasks internally, could not work it out from the info I read.
public async Task<ClaimsIdentity> CreateIdentity(string userid )
{
Guid gid = Guid.Parse(userid);
Avatar avatar = await _dbContext.Users.Where(d => d.Id == gid).FirstOrDefaultAsync();
return await CreateIdentity(avatar);
}
public async Task<ClaimsIdentity> CreateIdentity(Avatar avatar)
{
var identity = new ClaimsIdentity(await GetClaims(avatar));
return identity;
}
public async Task<List<Claim>> GetClaims(Avatar avatar)
{
var claims = new List<Claim>();
claims = async //(database call to get claims)
return claims;
}
With above code or any other similar async code, what I'm wondering is there any way to say, continue into, or continue with. So, instead of ending up with three tasks, they could be combined so there is a single state machine either two of methods or even all three?
I'm not sure if it matters that much, just looking for correct way of do it.
eg. (not valid code i know)
public async Task<ClaimsIdentity> CreateIdentity(string userid )
{
Guid gid = Guid.Parse(userid);
Avatar avatar = await _dbContext.Users.Where(d => d.Id == gid).FirstOrDefaultAsync();
return contiuneinto CreateIdentity(avatar);
}
^ say something like that. so the next method would be in same task. seems such waste to create another task for something so little.

Each async method gets its own state-machine and task, you can't automagically combine them.
You can remove both the async and await and use Task.ContinueWith which removes the state-machine, but it does still create a new task:
public Task<ClaimsIdentity> CreateIdentity(string userid )
{
Guid gid = Guid.Parse(userid);
return _dbContext.Users.Where(d => d.Id == gid).FirstOrDefaultAsync().
ContinueWith(avatarTask => CreateIdentity(avatarTask.GetAwaiter().GetResult()));
}
The simplest way to cut back on these tasks and state-machines is to simply join these async methods together, for example:
public async Task<ClaimsIdentity> CreateIdentity(string userid )
{
Guid gid = Guid.Parse(userid);
Avatar avatar = await _dbContext.Users.Where(d => d.Id == gid).FirstOrDefaultAsync();
var claims = new List<Claim>();
claims = async //(database call to get claims)
return new ClaimsIdentity(claims);
}
In any case, you don't need to worry about it. The cost of that task creation is probably negligble comapred to your actual asynchronous operations.

Related

How do I make a discord bot respond only to people with a certain role? [C#]

I want to make my discord bot respond exclusively to people with a #Member role, so when a person without that role writes a command (ex. >say Hello), the bot will not respond to it, but when a person with the #Member role writes that, it will.
Ideally, if this is being used for a command or rather multiple commands, a precondition should be used (example administration commands). This allows you to not have to duplicate the checks within each command.
Ah example of such a precondition can be found here.
You can read more on preconditions here
You will need to add role validation to each of your commands.
The quick and easy way would be to do the following:
[Command("test")]
public async Task TestCommand()
{
var user as Context.User as IGuildUser;
var roleToValidate = Context.Guild.Roles.First(r => r.Name == "SomeRoleName");
if (!user.RoleIDs.Any(r => r == roleToValidate.Id))
return;
// the rest of the code
}
Another approach (which I would recommend) is using PreconditionAttribute
/// CheckRole.cs
using Discord;
using Discord.Commands;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Example.Attributes
{
public class CheckRole : PreconditionAttribute
{
private List<string> _roles;
public CheckRole(params string[] roles)
{
_roles = roles.ToList();
}
public async override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command)
{
var user = context.User as IGuildUser;
var discordRoles = context.Guild.Roles.Where(gr => _roles.Any(r => gr.Name == r));
foreach (var role in discordRoles)
{
var userInRole = user.RoleIds.Any(ri => ri == role.Id);
if (userInRole)
{
return await Task.FromResult(PreconditionResult.FromSuccess());
}
}
return await Task.FromResult(PreconditionResult.FromError("You do not have permission to use this role."));
}
}
}
/// WhateverClassThatYouWriteCommandsIn.cs
[Command("test")]
[CheckRole("AdminRoleName", "ModeratorRole", "NormalRole")]
public async Task TestCommandForMostRoles()
{
var user as Context.User as IGuildUser;
var roleToValidate = Context.Guild.Roles.First(r => r.Name == "Some Role Name");
if (!user.RoleIDs.Any(r => r == roleToValidate.Id))
return;
// the rest of the code
}
[Command("test")]
[CheckRole("AdminRoleName", "ModeratorRole")]
public async Task TestCommandForAdmins()
{
var user as Context.User as IGuildUser;
var roleToValidate = Context.Guild.Roles.First(r => r.Name == "Some Role Name");
if (!user.RoleIDs.Any(r => r == roleToValidate.Id))
return;
// the rest of the code
}
This literal code may not work as I haven't tested it, however it is based on my own implementation of role preconditions authorisation which works.
To break down the code:
I have a variable to store multiple role names and use params[] in the constructor to allow any amount of role names to be provided. They are stored in the variable.
private List<string> _roles;
public CheckRole(params string[] roles)
{
_roles = roles.ToList();
}
CheckPermissionsAsync is automatically called every time that particular command is called.
public async override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command)
Get the actual Role objects from the context from the names, loop through them and check the user to see if they have that permission. The first time a role is found on a user it will return a success and the command code in the original command function will be run. If FromError is returned then the command code is not run.
var user = context.User as IGuildUser;
var discordRoles = context.Guild.Roles.Where(gr => _roles.Any(r => gr.Name == r));
foreach (var role in discordRoles)
{
var userInRole = user.RoleIds.Any(ri => ri == role.Id);
if (userInRole)
{
return await Task.FromResult(PreconditionResult.FromSuccess());
}
}
return await Task.FromResult(PreconditionResult.FromError("You do not have permission to use this role."));
This might seem like a lot, but you do not need to re-write role authorisation code again and you can simply add this attribute to whatever commands you want. You can also add this attribute to the class if you want every command in that class to be authorised by the role:
[CheckRoles("Moderator", "LowLevelModerator")]
public class ModeratorCommands : ModuleBase<SocketCommandContext>
{
[Command("CheckStats")]
public async Task ModeratorCommandForCheckStats()
{
// the code
}
[Command("BanUser")]
public async Task ModeratorCommandForBanUser()
{
// the code
}
[CheckRole("Admin")]
[Command("BanUser")]
public async Task ModeratorCommandOnlyForAdminsForBanModerator()
{
// the code
}
}

What happens when we don't await an async method

I have a .NET CORE 2 backend. In one of my controller endpoints, I'm creating invitations to be sent out via email. This seems to be a huge bottleneck on the endpoint and after thinking about it, I don't really need to wait for these invitations. If the email fails to send out, I can't really do anything about it anyway.
If I don't do await sendFn() would it essentially be a fire and forget method? I was reading on another stackoverflow thread that I'd have to do sendFn().ContinueWith(t => throw(t)) to be able to catch the exception since it'll be in another thread.
I have similar mailing functions around the code base. They each do slightly different things, but is there a service fn I can do to wrap these to make them fire and forget? I think some places I can just not use await (if that works), but some things alter the database context so if I don't await them I can potentially run into a case where something is accessing the same db context.
[HttpPost]
public async Task<IActionResult> CreateEvent([FromBody] Event val)
{
_ctx.Event.Add(val);
await _ctx.SaveChangesAsync();
await SendInvitations(val); // fn in question
return Ok();
}
public async Task SendInvitation(Event event)
{
forEach (var person in event.people)
{
await _ctx.Invitation.Add(person); // This shouldn't happen while another iteration or some other async code elsewhere is using the db ctx.
_ctx.SaveChangesAsync();
await _mailService.SendMail(person.email,"you have been invited"); // don't really need to await this.
}
}
I'm posting to my server with data about an event. After I create and save the event to the database, I go and create invitations for each person. These invitations are also database items. I then send out an email. I'm mostly worried that if I drop the await, then when I'm creating invitations, it may conflict with db context elsewhere or the next iteration.
To get your code to compile and run I had to make these changes:
public async Task<IActionResult> CreateEvent(Event val)
{
_ctx.Event.Add(val);
await _ctx.SaveChangesAsync();
await SendInvitation(val);
return Ok();
}
public async Task SendInvitation(Event #event)
{
foreach (var person in #event.people)
{
await _ctx.Invitation.Add(person);
await _ctx.SaveChangesAsync();
await _mailService.SendMail(person.email, "you have been invited");
}
}
I also had to write this harness code:
public OK Ok() => new OK();
public class Event
{
public List<Person> people = new List<Person>();
}
public class Person
{
public string email;
}
public interface IActionResult { }
public class OK : IActionResult { }
public class Invitation
{
public Task Add(Person person) => Task.Run(() => { });
}
public static class _ctx
{
public static List<Event> Event = new List<Event>();
public static Invitation Invitation = new Invitation();
public static Task SaveChangesAsync() { return Task.Run(() => { }); }
}
public static class _mailService
{
public static Task SendMail(string email, string message) { return Task.Run(() => { }); }
}
Then I updated SendInvitation like this:
public async Task SendInvitation(Event #event)
{
Thread.Sleep(2000);
foreach (var person in #event.people)
{
await _ctx.Invitation.Add(person);
await _ctx.SaveChangesAsync();
await _mailService.SendMail(person.email, "you have been invited");
}
Console.WriteLine("Done `SendInvitation`.");
}
Now, I can run it all like so:
var e = new Event();
e.people.Add(new Person() { email = "foo#bar.com" });
CreateEvent(e).ContinueWith(t => Console.WriteLine("Done `CreateEvent`."));
Console.WriteLine("Done `Main`.");
That outputs:
Done `Main`.
Then 2 seconds later:
Done `SendInvitation`.
Done `CreateEvent`.
If I simply change CreateEvent to this:
public async Task<IActionResult> CreateEvent(Event val)
{
_ctx.Event.Add(val);
await _ctx.SaveChangesAsync();
Task.Run(() => SendInvitation(val));
return Ok();
}
Then I get this output:
Done `Main`.
Done `CreateEvent`.
Then 2 seconds later:
Done `SendInvitation`.
That seems to be what you want.
The short answer is that you have no guarantees that that the execution of that code will complete.
That's why ASP.NET Core has infrastructure for background work: Implementing background tasks in .NET Core 2.x webapps or microservices with IHostedService and the BackgroundService class

Asynchronous programming with custom DTO's and Entity Framework

Hopefully I can explain myself clearly, as I'm fairly new to async/await and Task.
Basically I'm going down the route of teaching myself asynchronous programming, whilst fundamentally, I fully understand the concept, and when it's best applied.
Although in my project, I may have gone a little overboard. So I've always enjoyed crafting my own DTO's, and I don't really enjoy using a third party tool (AutoMapper) to do so, But potentially with great regret because I'm not really sure how to return Task<DTO> from manually mapping.
If I may elaborate:
Client Repo
public sealed class ClientRepository : IClientRepository
{
private ClientConfigEntities _context;
public ClientRepository(ClientConfigEntities context)
{
_context = context;
}
public async Task<Client> AuthenticateAsync(string apiKey, string password)
{
//Throws error because it cannot return Task<Client> from Client
return await MapClient.ToModel(_context.tbl_Client
.FirstOrDefaultAsync(c => c.ApiKey == apiKey
&& c.Password == password));
}
}
Mapping
public static class MapClient
{
public static Client ToModel(tbl_Client source)
{
return (source != null) ? new Client
{
Id = source.Id,
ApiKey = source.ApiKey,
ApiURL = source.ApiURL,
ClientKey = source.ClientKey,
Password = source.Password,
RetainMeApiKey = source.RetainMeApiKey,
Secret = source.Secret
}
: null;
}
}
I also have another abstraction AuthenticationService which is then called in a Controller - allows me to hide complexity.
With the above I've tried return a Task<Client> on the static mapping method but I'm sure as your aware of, it throws type issues.
What's the best approach to return an asynchronous Task<Client>?
Really hope someone can give me insight into how and why?
Much appreciated,
Pay attention on what FirstOrDefaultAsync return: Task<Client>. Then you are trying to pass this Task to a method that accept a Client object and not a Task<Client>.
Change as follow:
return MapClient.ToModel(await _context.tbl_Client
.FirstOrDefaultAsync(c => c.ApiKey == apiKey
&& c.Password == password));
By doing this await _context.tbl_Client.FirstOrDefaultAsync will return a Client object once the Task is completed and after that you will pass the model into your mapping method (I also suggest you to use the extension method instead of a "normal" static method).
Adding to Tinwor's answer, there was some confusion in OP in where the await was to be used. Trying to do the desired code inline helped in causing that confusion.
Refactor the code to make it easier to read and identify where awaited code should be placed thus showing the intent of the code.
public async Task<Client> AuthenticateAsync(string apiKey, string password) {
var entity = await _context.tbl_Client.FirstOrDefaultAsync(
c => c.ApiKey == apiKey && c.Password == password
);
var model = MapClient.ToModel(entity);
return model;
}
Converting the ToModel to an extension method also allows the code to be cleaner as well
public static class MapClient {
public static Client ToModel(this tbl_Client source) {
return (source != null) ? new Client {
Id = source.Id,
ApiKey = source.ApiKey,
ApiURL = source.ApiURL,
ClientKey = source.ClientKey,
Password = source.Password,
RetainMeApiKey = source.RetainMeApiKey,
Secret = source.Secret
}
: null;
}
}
Which would result in the final code looking like
public async Task<Client> AuthenticateAsync(string apiKey, string password) {
var entity = await _context.tbl_Client.FirstOrDefaultAsync(
c => c.ApiKey == apiKey && c.Password == password
);
var model = entity.ToModel();// <-- extension method used here
return model;
}
First, MapClient.ToModel is not a async method. So you cannot call await on this method.
Second, you are only mapping the data within the ToModel method. So there is no need to make it async either.
The best way to handle this would be to change the return type of 'AuthenticateAsync' method to Client only(not compulsary though). The code inside the method can be changed to -
return MapClient.ToModel(await _context.tbl_Client.FirstOrDefaultAsync(
c => c.ApiKey == apiKey && c.Password == password
));

ASP.NET Core: Making service asynchronous

I have an ASP.NET core application where I use a service layer between my controllers and Entity Framework.
Currently my services are synchronous, but I would like to make them asynchronous.
This is an controller example:
public async Task<IActionResult> Search(BusinessesSearchModel model)
{
var resultModel = await _businessService.Search(model);
return Ok(resultModel);
}
And this is an example of my service:
public interface IBusinessesService
{
Task<BusinessResultListModel> Search(BusinessesSearchModel model);
}
public class BusinessesService : IBusinessesService
{
public async Task<BusinessResultListModel> Search(BusinessesSearchModel model)
{
var queryResult = (from b in MyDBContext.Businesses
where b.Name.Contains(model.Name)
&& b.Phone.Contains(model.Phone)
select new BusinessesListModel
{
ID = b.ID,
Name = b.Name,
Phone = b.Phone
}
).ToList();
var resultModel = new BusinessResultListModel();
resultModel.data = queryResult;
resultModel.othervalue = "other value";
return resultModel;
}
}
This does not work. Visual Studio tells me:
This async method lacks 'await' operators and will run synchronously
Where should I add await to make this async?
Your code isn't doing anyting asynchronously. To utilize the await/async API, you need to be invoking an async method inside your own method (unless you want to implement the async logic yourself).
In your case, if you're using Entity Framework, you can use it's async methods for querying:
public class BusinessesService : IBusinessesService
{
public async Task<BusinessResultListModel> Search(BusinessesSearchModel model)
{
var queryResult = await _context.YourEntity.Where(x => x.SomeProperty == model.Query).ToListAsync(); //<--- This is your async call, awaiting the ToListAsync() method
var resultModel = new BusinessResultListModel();
resultModel.data = queryResult;
resultModel.othervalue = "other value";
return resultModel;
}
}
Other async methods in EntityFramework are FirstOrDefaultAsync(), SingleOrDefaultAsync(), SaveChangesAsync() and so on
you are missing the async within linq.
for example:
public async Task<IEnumerable<SchedulerJob>> GetAllByIdsAsync(IEnumerable<int> ids, CancellationToken cancellationToken = default(CancellationToken))
{
return await Context.SchedulerJobs
.Include(s => s.Program)
.Where(job => ids.Contains(job.Id))
.ToListAsync(cancellationToken);
}
the ToListAsync(cancellationToken); in combination with await, and you are set to go!
i see you edited the page:
you need to do:
public async Task<BusinessResultListModel> Search(BusinessesSearchModel model)
{
//something like:
var queryResult = await (from b in MyDBContext.Businesses
where b.Name.Contains(model.Name)
&& b.Phone.Contains(model.Phone)
select new BusinessesListModel
{
ID = b.ID,
Name = b.Name,
Phone = b.Phone
}
).ToListAsync();
var resultModel = new BusinessResultListModel();
resultModel.data = queryResult;
resultModel.othervalue = "other value";
return resultModel;
}

Any reason to use async/await when the controller action is sync?

Lets say I have a controller action that cannot be made async (for various reasons), but I have a service that (through several methods) calls a rest service using HttpClient. Is there any thing to gain by using the async client and using .Wait or .Result? Or would it be less performant to use the synchronous method?
So either:
//MyController.cs
public ActionResult GetSomething(int id)
{
//lots of stuff here
var processedResponse = _myService.Get(id);
//lots more stuff here
return new ContentResult(result);
}
//MyService.cs
public ProcessedResponse Get(int id)
{
var client = new HttpClient();
var result = client.Get(_url+id);
return Process(result);
}
Or:
//MyController.cs
public ActionResult GetSomething(int id)
{
//lots of stuff here
var processedResponse = _myService.GetAsync(id).Result;
//or, .Wait, or Task.Run(...), or something similar
//lots more stuff here
return new ContentResult(result);
}
//MyService.cs
public async Task<ProcessedResponse> GetAsync(int id)
{
var client = new HttpClient();
var result = await client.GetAsync(_url+id);
return await Process(result);
}
Is there any thing to gain by using the async client and wrapping the
method in Task.Run(() => _myService.Get()).Result?
The only thing you'll most likely end up gaining is a deadlock. Think about it, you're queuing a naturally asynchronous method on a thread-pool thread, where the ASP.NET already gave you a thread to process you Action inside. There isn't much sense in that.
If you want to go async, and think you'll actually benefit from the scale provided by asynchrony, then you should re-factor your controllers to be async as well and return a Task<T>, where you can await those asynchronous methods.
So I'd either stay synchronous, or re-factor the code top to bottom to support async:
//MyController.cs
public async Task<ActionResult> GetSomethingAsync(int id)
{
//lots of stuff here
await GetAsync(id);
return new ContentResult(result);
}
//MyService.cs
public async Task<ProcessedResponse> GetAsync(int id)
{
var client = new HttpClient();
var result = await client.GetAsync(_url+id);
return await Process(result);
}
In your scenario, no there isn't a good reason, but lets add some functionality:
//MyController.cs
public ActionResult GetSomething(int id)
{
//lots of stuff here
var processedResponse = _myService.GetAsync(id).Result;
//or, .Wait, or Task.Run(...), or something similar
//lots more stuff here
return new ContentResult(result);
}
//MyService.cs
public async Task<ProcessedResponse> GetAsync(int id)
{
var client = new HttpClient();
var pendingResult1 = client.GetAsync(_url+id);
var pendingResult2 = someAsyncOperation();
var result3 = someSyncOperation();
var result1 = await pendingResult;
var result2 = await pendingResult2;
return await Process(result1, result2, result3);
}
Now, since your request takes a while to complete, someAsynchOperation starts executing immediately instead of waiting for GetAsync() to complete. someSyncOperation is also being executed in the meantime.
Without the async keyword, you would not be able to use await, so it's better to have it if you plan to have asynchronous execution inside your funciton.
It gets interesting when do can do it like this
//MyController.cs
public ActionResult GetSomething(int id)
{
var processedResponseTask = _myService.GetAsyn(id)
//lots of stuff here (1)
var processedResponseTask.Wait();
var processedResponse = processedResponseTask.Result;
//lots more stuff here (2)
return new ContentResult(result);
}
now the lots of stuff here(1) is done in parallel with your async task. (Or if you called your service twice for example). If you don't actually do a lot around lots of stuff here(1) then there isn't much of a point.

Categories

Resources