In the following example, you are going to add a new service for handling game invitations and updating the Tic-Tac-Toe application. This facilitates email communication, which is used for contacting other users to join a game, while using method injection:
- Add a new service called GameInvitationService in the Services folder for managing game invitations (adding, updating, removing, and more):
public class GameInvitationService { private static ConcurrentBag<GameInvitationModel>
_gameInvitations; public GameInvitationService(){ _gameInvitations = new ConcurrentBag<GameInvitationModel>();} public Task<GameInvitationModel> Add(GameInvitationModel
gameInvitationModel) { gameInvitationModel.Id = Guid.NewGuid(); _gameInvitations.Add(gameInvitationModel); return Task.FromResult(gameInvitationModel); }
public Task Update(GameInvitationModel gameInvitationModel) { _gameInvitations = new ConcurrentBag<GameInvitationModel>
(_gameInvitations.Where(x => x.Id != gameInvitationModel.Id)) { gameInvitationModel }; return Task.CompletedTask; } public Task<GameInvitationModel> Get(Guid id) { return Task.FromResult(_gameInvitations.FirstOrDefault(x =>
x.Id == id)); }
}
- Extract the IGameInvitationService interface:
- Add the new game invitation service to the ConfigureServices method of the Startup class (we want a single application instance, so add it as a singleton):
services.AddSingleton<IGameInvitationService,
GameInvitationService>();
- Update the Index method in GameInvitationController and inject an instance of the game invitation service via method injection using the RequestServices provider:
public IActionResult Index(GameInvitationModel gameInvitationModel, [FromServices]IEmailService emailService) { var gameInvitationService = Request.HttpContext.RequestServices.GetService <IGameInvitationService>(); if (ModelState.IsValid) { emailService.SendEmail(gameInvitationModel.EmailTo,
_stringLocalizer["Invitation for playing a Tic-Tac-Toe game"], _stringLocalizer[$"Hello, you have been invited to play the
Tic-Tac-Toe game by {0}. For joining the game, please
click here {1}", gameInvitationModel.InvitedBy,
Url.Action("GameInvitationConfirmation", GameInvitation",
new { gameInvitationModel.InvitedBy,
gameInvitationModel.EmailTo }, Request.Scheme,
Request.Host.ToString())]); var invitation = gameInvitationService.Add
(gameInvitationModel).Result; return RedirectToAction("GameInvitationConfirmation",
new { id = invitation.Id }); } return View(gameInvitationModel);
}
Don't forget to add the following using statement at the beginning of the class: using Microsoft.Extensions.DependencyInjection;, If you don't, the .GetService<IGameInvitationService>(); method can't be used and you will get build errors.
- Add a new method called GameInvitationConfirmation to GameInvitationController:
[HttpGet] public IActionResult GameInvitationConfirmation(Guid id,
[FromServices]IGameInvitationService gameInvitationService) { var gameInvitation = gameInvitationService.Get(id).Result; return View(gameInvitation); }
- Create a new view for the GameInvitationConfirmation method you added previously. This will display a waiting message to the user:
@model TicTacToe.Models.GameInvitationModel @{ ViewData["Title"] = "GameInvitationConfirmation"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h1>@Localizer["You have invited {0} to play
a Tic-Tac-Toe game
with you, please wait until the user is connected",
Model.EmailTo]</h1> @section Scripts{ <script> $(document).ready(function () { GameInvitationConfirmation('@Model.Id'); }); </script> }
- Add a new method called GameInvitationConfirmation to the scripts1.js file. You can use the same basic structure we used for the existing EmailConfirmation method:
function GameInvitationConfirmation(id) { if (window.WebSocket) { alert("Websockets are enabled"); openSocket(id, "GameInvitation"); } else { alert("Websockets are not enabled"); interval = setInterval(() => { CheckGameInvitationConfirmationStatus(id); }, 5000); } }
- Add a method called CheckGameInvitationConfirmationStatus to the scripts2.js file. You can use the same basic structure we used for the existing CheckEmailConfirmationStatus method:
function CheckGameInvitationConfirmationStatus(id) { $.get("/GameInvitationConfirmation?id=" + id,
function (data) { if (data.result === "OK") { if (interval !== null) clearInterval(interval); window.location.href = "/GameSession/Index/" + id; } }); }
- Update the openSocket method in the scripts2.js file and add the specific game invitation case:
... if (strAction == "Email") { wsUri = protocol + "//" + window.location.host + "/CheckEmailConfirmationStatus"; operation = "CheckEmailConfirmationStatus"; } else if (strAction == "GameInvitation") { wsUri = protocol + "//" + window.location.host + "/GameInvitationConfirmation"; operation = "CheckGameInvitationConfirmationStatus"; } var socket = new WebSocket(wsUri); socket.onmessage = function (response) { console.log(response); if (strAction == "Email" && response.data == "OK") { window.location.href = "/GameInvitation?email=" + parameter; }else if (strAction == "GameInvitation") { var data = $.parseJSON(response.data); if (data.Result == "OK") window.location.href = "/GameSession/Index/" + data.Id; } }; ...
- Add a new method called ProcessGameInvitationConfirmation in the communication middleware. This will process game invitation requests without using WebSockets for browsers that don't support this feature:
private async Task ProcessGameInvitationConfirmation(HttpContext context) { var id = context.Request.Query["id"]; if (string.IsNullOrEmpty(id))await context.
Response.WriteAsync("BadRequest:Id is required"); var gameInvitationService = context.RequestServices.GetService
<IGameInvitationService>(); var gameInvitationModel = await
gameInvitationService.Get(Guid.Parse(id)); if (gameInvitationModel.IsConfirmed) await
context.Response.WriteAsync(
JsonConvert.SerializeObject(new { Result = "OK", Email = gameInvitationModel.InvitedBy,
gameInvitationModel.EmailTo })); else { await context.Response.WriteAsync(
"WaitGameInvitationConfirmation"); } }
Don't forget to add the following using statement at the beginning of the class:
using Microsoft.Extensions.DependencyInjection;.
using Microsoft.Extensions.DependencyInjection;.
- Add a new method called ProcessGameInvitationConfirmation with additional parameters to the communication middleware. This will process game invitation requests while using WebSockets for the browsers that support this:
private async Task ProcessGameInvitationConfirmation(HttpContext context,
WebSocket webSocket, CancellationToken ct,
string parameters) { var gameInvitationService = context.RequestServices.GetService
<IGameInvitationService>(); var id = Guid.Parse(parameters); var gameInvitationModel = await gameInvitationService.Get(id); while (!ct.IsCancellationRequested && !webSocket.
CloseStatus.HasValue &&
gameInvitationModel?.IsConfirmed == false) {
await SendStringAsync(webSocket, JsonConvert.
SerializeObject(new { Result = "OK", Email = gameInvitationModel.InvitedBy,
gameInvitationModel.EmailTo, gameInvitationModel.Id }), ct); Task.Delay(500).Wait(); gameInvitationModel = await gameInvitationService.Get(id); } }
- Update the Invoke method in the communication middleware. This will work with email confirmations and game invitation confirmations from now on, with and without WebSockets:
public async Task Invoke(HttpContext context) { if (context.WebSockets.IsWebSocketRequest) { ... switch (command.Operation.ToString()) { ... case "CheckGameInvitationConfirmationStatus": { await
ProcessGameInvitationConfirmation(context,webSocket, ct,
command.Parameters.ToString()); break; } } } else if (context.Request.Path.Equals
("/CheckEmailConfirmationStatus")) { await ProcessEmailConfirmation(context); } else if (context.Request.Path.Equals
("/CheckGameInvitationConfirmationStatus")) { await ProcessGameInvitationConfirmation(context); } else { await _next?.Invoke(context); } }
In this section, you have learned how to use method injection in your ASP.NET Core 3 web applications. This is the preferred method for injecting your services and you should use it whenever applicable.
You have advanced well with the implementation of the Tic-Tac-Toe game. Mostly everything around user registration, email confirmation, game invitation, and game invitation confirmation has now been implemented.