Consider the scenario of making a cake:

  • Add cake ingredients
  • Add milk
  • Add the eggs
  • Mix the cake ingredients
  • Prepare the oven
  • cooking cake
  • The cake is ready

In this article, we want to implement a scenario similar to baking a cake in the form of short polling. The usual method is the Long polling method, in which the user sends a request to the site and waits for all the steps to be completed and then receives the cake. During this request, the user is not aware of the status of the cake and can only wait for the cake to be ready. But in the Short polling method, the user sends a request to the server every few seconds to find out what the condition of the cake is. The structure of the Short polling method is that the user first sends a request to the site to create a cake and immediately receives a token, then the next time the user must send the token to the site to check the status of the cake.

Advantages:

  1. The user is aware of the status of their request.

Disadvantages:

  1. The number of requests sent to the server increases.
  2. The complexity of this method is a bit more

Create an Asp.Net Core 5 project to implement this scenario. Then create a class called CakeState for cake status as follows:

public class CakeState
{
    public string Token { get; set; }
    public string State { get; set; }
    public bool IsFinish { get; set; }
}

Then a class for storing data (for testing a static class):

public static class CakeStateDate
{
    private static readonly List<CakeState> CakeStates = new List<CakeState>();
    public static List<CakeState> GetCakeStatus(string token)
    {
        return CakeStates.Where(a => a.Token == token).ToList();
    }
    public static void AddCakeState(CakeState cakeState)
    {
        Console.WriteLine(cakeState.State);
        CakeStates.Add(cakeState);
    }
}

Cake making service:

public interface IMakeCakeService
{
    Task<CakeState> MakeCakeLongPolling();
    Task MakeCakeShortPolling(string token);
    List<CakeState> GetCakeState(string token);
}
public class MakeCakeService : IMakeCakeService
{
    public List<CakeState> GetCakeState(string token)
    {
        return CakeStateDate.GetCakeStatus(token);
    }

    public async Task<CakeState> MakeCakeLongPolling()
    {
        await Task.Delay(2000);
        Console.WriteLine("Added cake mix");
        await Task.Delay(3000);
        Console.WriteLine("Added milk");
        await Task.Delay(1000);
        Console.WriteLine("Added eggs");
        await Task.Delay(1000);
        Console.WriteLine("Cake ingredients mixed");
        await Task.Delay(6000);
        Console.WriteLine("Oven is ready");
        Console.WriteLine("Baking cake...");
        await Task.Delay(2000);
        Console.WriteLine("Cake is ready");
        return new CakeState { IsFinish = true, State = "Cake is ready" };
    }
    public async Task MakeCakeShortPolling(string token)
    {
        await Task.Delay(2000);
        CakeStateDate.AddCakeState(new CakeState
        {
            IsFinish = false,
            State = "Added cake mix",
            Token = token
        });
        await Task.Delay(3000);
        CakeStateDate.AddCakeState(new CakeState
        {
            IsFinish = false,
            State = "Added milk",
            Token = token
        });
        await Task.Delay(3000);
        CakeStateDate.AddCakeState(new CakeState
        {
            IsFinish = false,
            State = "Added eggs",
            Token = token
        });
        await Task.Delay(1000);
        CakeStateDate.AddCakeState(new CakeState
        {
            IsFinish = false,
            State = "Cake ingredients mixed",
            Token = token
        });
        await Task.Delay(1000);
        CakeStateDate.AddCakeState(new CakeState
        {
            IsFinish = false,
            State = "Oven is ready",
            Token = token
        });
        CakeStateDate.AddCakeState(new CakeState
        {
            IsFinish = false,
            State = "Baking cake...",
            Token = token
        });
        await Task.Delay(6000);
        CakeStateDate.AddCakeState(new CakeState
        {
            IsFinish = true,
            State = "Cake is ready",
            Token = token
        });
    }
}

And finally the cake controller:

[Route("api/[controller]")]
[ApiController]
public class CakeController : ControllerBase
{
    private readonly IMakeCakeService _makeCakeService;

    public CakeController(IMakeCakeService makeCakeService)
    {
        _makeCakeService = makeCakeService;
    }
    [HttpPost("MakeCake")]
    public IActionResult MakeCake()
    {
        var token = TokenGenerator.GenerateString(50);
        BackgroundJob.Enqueue(() => _makeCakeService.MakeCakeShortPolling(token).GetAwaiter().GetResult());
        return Ok(token);
    }
    [HttpGet("CheckState/{token}")]
    public IActionResult CakeState(string token)
    {
        return Ok(_makeCakeService.GetCakeState(token));
    }
    [HttpPost("MakeCakeLongPolling")]
    public async Task<IActionResult> MakeCakeLongPolling()
    {
        return Ok(await _makeCakeService.MakeCakeLongPolling());
    }
}

To create tokens, we have created a class called TokenGenerator:

public class TokenGenerator
{
    private const string AllowableCharacters = "abcdefghijklmnopqrstuvwxyz0123456789";
    public static string GenerateString(int length)
    {
        var bytes = new byte[length];

        using (var random = RandomNumberGenerator.Create())
        {
            random.GetBytes(bytes);
        }
        return new string(bytes.Select(x => AllowableCharacters[x % AllowableCharacters.Length]).ToArray());
    }
}

When the user submits a request for a short polling cake, the MakeCake method is called in the controller and a token is created for the ricochet, and then through Hangfire the cake is made in the background using BackgroundJob.Enqueue and immediately the token is sent to The user is sent. The MakeCakeShortPolling method then starts making the cake in the background and saves a sample of the CakeState class in the CakeStateData class each time it completes a task. receives. The user then has to call the CakeState API every few seconds to get the latest cake status and repeat until the IsFinish field value is not true.
Sample of making a cake cake:

POST
/api/Cake/MakeCake

Response:

zlgmghaikpuq230zogmkh6ipz21c1j1c24hdvd0dfk067ltzwb

Sample of the first request to receive cake status:

GET
/api/Cake/CheckState/zlgmghaikpuq230zogmkh6ipz21c1j1c24hdvd0dfk067ltzwb

Sample response to the first request:

[
  {
    "token": "zlgmghaikpuq230zogmkh6ipz21c1j1c24hdvd0dfk067ltzwb",
    "state": "Added cake mix",
    "isFinish": false
  },
  {
    "token": "zlgmghaikpuq230zogmkh6ipz21c1j1c24hdvd0dfk067ltzwb",
    "state": "Added milk",
    "isFinish": false
  }
]

Sample response to the second request:

[
  {
    "token": "zlgmghaikpuq230zogmkh6ipz21c1j1c24hdvd0dfk067ltzwb",
    "state": "Added cake mix",
    "isFinish": false
  },
  {
    "token": "zlgmghaikpuq230zogmkh6ipz21c1j1c24hdvd0dfk067ltzwb",
    "state": "Added milk",
    "isFinish": false
  },
  {
    "token": "zlgmghaikpuq230zogmkh6ipz21c1j1c24hdvd0dfk067ltzwb",
    "state": "Added eggs",
    "isFinish": false
  },
  {
    "token": "zlgmghaikpuq230zogmkh6ipz21c1j1c24hdvd0dfk067ltzwb",
    "state": "Cake ingredients mixed",
    "isFinish": false
  }
]

Last Response:

[
  {
    "token": "zlgmghaikpuq230zogmkh6ipz21c1j1c24hdvd0dfk067ltzwb",
    "state": "Added cake mix",
    "isFinish": false
  },
  {
    "token": "zlgmghaikpuq230zogmkh6ipz21c1j1c24hdvd0dfk067ltzwb",
    "state": "Added milk",
    "isFinish": false
  },
  {
    "token": "zlgmghaikpuq230zogmkh6ipz21c1j1c24hdvd0dfk067ltzwb",
    "state": "Added eggs",
    "isFinish": false
  },
  {
    "token": "zlgmghaikpuq230zogmkh6ipz21c1j1c24hdvd0dfk067ltzwb",
    "state": "Cake ingredients mixed",
    "isFinish": false
  },
  {
    "token": "zlgmghaikpuq230zogmkh6ipz21c1j1c24hdvd0dfk067ltzwb",
    "state": "Oven is ready",
    "isFinish": false
  },
  {
    "token": "zlgmghaikpuq230zogmkh6ipz21c1j1c24hdvd0dfk067ltzwb",
    "state": "Baking cake...",
    "isFinish": false
  },
  {
    "token": "zlgmghaikpuq230zogmkh6ipz21c1j1c24hdvd0dfk067ltzwb",
    "state": "Cake is ready",
    "isFinish": true
  }
]

You can download the codes of this article from GitHub ;)

Powered by Froala Editor