• Home
  • About
    • Hanna's Blog photo

      Hanna's Blog

      I wanna be a global developer.

    • Learn More
    • Email
    • LinkedIn
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

[Blazor] Ranking APP

04 Feb 2021

Reading time ~8 minutes

Reference by [C#과 유니티로 만드는 MMORPG 게임 개발 시리즈]Part6: 웹 서버

Create Project

  • Blazor Server App
    • [Auth]-[Individual User Account]-[Save in User Account App]
Blazor Ranking App

Active Database

  • Startup.cs
  public void ConfigureServices(IServiceCollection services)
  {
    services.AddDbContext<ApplicationDbContext>(options =>
      options.UseSqlServer(
        Configuration.GetConnectionString("DefaultConnection")));
      ...
  }

Create a new Database

  • RankingDB
    • Create in Local DB
    • Click [Property]
    • Copy context in [Link String]
Blazor Ranking App
  • Paste in appsetting.json
  {
    "ConnectionStrings": {
      "DefaultConnection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=RankingDB;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"
    },
  ...

Register User

  • Register User in Web
    • Execute the solution
    • Click [Register]
    • Fill forms and Click [Register] button
    • If you register first user, then click [Migrations Apply] button then the button will be change to [Migrations Applied]
    • Press [F5] on Keyborad or refresh button in web
Blazor Ranking App
  • Click [Click here to comfirm your account], then your email will be comfirmed
  • Login with this account
Blazor Ranking App
  • You can see your log in data in topbar
Blazor Ranking App

Check User

  • Check User in Database
    • Open dbo.AspNetUsers Data
    • You can check your registed account
Blazor Ranking App
Blazor Ranking App

Migration

  • Models
    • Create Data\Models folder
Blazor Ranking App
  • Data\Models\GameResult.cs
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Threading.Tasks;

  namespace RankingApp.Data.Models
  {
    public class GameResult
    {
      public int Id { get; set; }
      public int UserId { get; set; }
      public string UserName { get; set; }
      public int Score { get; set; }
      public DateTime Date { get; set; }
    }
  }
  • Data\ApplicationDbContext.cs
  public DbSet<GameResult> GameResults { get; set; }
Blazor Ranking App
  • Input Data in Database directly
Blazor Ranking App

Database Version Management

  • Up()
    • Builds operations that will migrate the database up
    • That is, builds the operations that will take the database from the state left in by the previous migration so that it is up-to-date with regard to migartion
    • This method must to be overridden in each class the inherits from Migration
  protected override void Up(MigrationBuilder migrationBuilder)
  {
    ...
  }
  • Down()
    • Builds operations that will migrate the database down
    • That is, builds the operations that will take the database from the state left in by the this migration so that it returns to the state that it was in before this migration was applied.
    • This method must to be overridden in each class the inherits from Migration if both up and down migrations are to be supported. If it is not overridden, then calling it will throw and it will not to be possible to migrate in the down direction.
  protected override void Down(MigrationBuilder migrationBuilder)
  {
    ...
  }

Authorization

  • AuthorizeView
    • Displays differing content depending on the user’s authorization status
  • Authorized
    • The content that will be displayed if the user is authorized.
  • NotAuthorized
    • The content that will be displayed if the user is not authorized.
  <AuthorizeView>
    <Authorized>
      ...
    </Authorized>
    <NotAuthorized>
      ...
    </NotAuthorized>
  </AuthorizeView>
Blazor Ranking App

CRUD

  • FormResult Method
    • Creates a System.Threading.Tasks.Task`1 that’s completed successfully with the specified result.

CREATE

  public Task<GameResult> AddGameResult(GameResult gameResult)
  {
    _context.GameResults.Add(gameResult);
    _context.SaveChanges();

    return Task.FromResult(gameResult);
  }
  • SaveChanges Method
    • Saves all changes made in this context to the database.
    • This method will automatically call Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker.DetectChanges to discover any changes to entity instances before saving to the underlying database.
    • This can be disabled via Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker.AutoDetectChangesEnabled.
  void AddGameResult()
  {
    _showPopup = true;
    _gameResult = new GameResult() { id = 0 };
  }

  async Task SaveGameResult()
  {
    if (_gameResult.id == 0)
    {
      _gameResult.Date = DateTime.Now;
      var result = RankingService.AddGameResult(_gameResult);
    }
    ...
    _gameResults = await RankingService.GetGameResultsAsync();
    _showPopup = false;
  }
Blazor Ranking App

READ

  public Task<List<GameResult>> GetGameResultsAsync()
  {
    List<GameResult> results = _context.GameResults
      .OrderByDescending(item => item.Score)
      .ToList();
    
    return Task.FromResult(results);
  }
  protected override async Task OnInitializedAsync()
  {
    _gameResults = await RankingService.GetGameResultsAsync();
  }

UPDATE

  public Task<bool> UpdateGameResult(GameResult gameResult)
  {
    var findResult = _context.GameResults
      .Where(x => x.Id == gameResult.Id)
      .FirstOrDefault();

    if (findResult == null)
      return Task.FromResult(false) ;

    findResult.UserName = gameResult.UserName;
    findResult.Score = gameResult.Score;
    _context.SaveChanges();

    return Task.FromResult(true);
  }
  void UpdateGameResult(GameResult gameResult)
  {
    _showPopup = true;
    _gameResult = gameResult;
  }

  async Task SaveGameResult()
  {
    if (_gameResult.Id == 0)
    ...
    else
    {
      var result = RankingService.UpdateGameResult(_gameResult);
    }

    _gameResults = await RankingService.GetGameResultsAsync();
    _showPopup = false;
  }

DELETE

  public Task<bool> DeleteGameResult(GameResult gameResult)
  {
    var findResult = _context.GameResults
      .Where(x => x.Id == gameResult.Id)
      .FirstOrDefault();

    if (findResult == null)
      return Task.FromResult(false);

    _context.GameResults.Remove(gameResult);
    _context.SaveChanges();

    return Task.FromResult(true);
  }
  async Task DeleteGameResult(GameResult gameResult)
  {
    var result = RankingService.DeleteGameResult(gameResult);
    _gameResults = await RankingService.GetGameResultsAsync();
  }

SharedData

Add a new project

  • SharedData
    • Class Library(.Net Core)
  • Data\GameResult.cs
  using System;
  using System.Collections.Generic;
  using System.Text;

  namespace SharedData.Models
  {
    public class GameResult
    {
      public int Id { get; set; }
      public int UserId { get; set; }
      public string UserName { get; set; }
      public int Score { get; set; }
      public DateTime Date { get; set; }
    }
  }
Blazor Ranking App

PostMan

Download

  • Postman
    • Download Postman
    • deactivate [Settings]-[Enable SSL certificate verification]
Blazor Ranking App

WebApi

Add a new project

  • WebApi
    • ASP.NET Core Web Application
    • template: API
    • package: Microsoft.EntityFrameworkCore, Microsoft.EntityFrameworkCore.Design, Microsoft.EntityFrameworkCore.SqlServer
Blazor Ranking App

Reference

  • Dependency
    • references SharedData

Database Connect

  • Data\ApplicationDbContext.cs
  using Microsoft.EntityFrameworkCore;
  using SharedData.Models;
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Threading.Tasks;

  namespace WebApi.Data
  {
    public class ApplicationDbContext: DbContext
    {
      public DbSet<GameResult>GameResults { get; set; }
      public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options){ }
    }
  }
  • Startup.cs
  public void ConfigureServices(IServiceCollection services)
  {
    ...
    services.AddDbContext<ApplicationDbContext>(options =>
      options.UseSqlServer(
        Configuration.GetConnectionString("DefaultConnection")));
  }
  • appsettings.json
{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=RankingApiDB;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"
  },
  ...
}
Blazor Ranking App

Controllers\RankingController.cs

  • REST (Representational State Transfer)
    • Reusing features from the original HTTP communication to create a data transmission rule
  • ApiController feature
    • can return C# object
    • if return null, then 204 Response(No Content) in cli
    • string → text/plain
    • rest(int, bool) → application/json
  • Read
    • ex: GET/ api/ ranking → get all items
    • ex: GET/ api/ ranking/1 → get item which is id=1
  using Microsoft.AspNetCore.Http;
  using Microsoft.AspNetCore.Mvc;
  using SharedData.Models;
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Threading.Tasks;
  using WebApi.Data;

  namespace WebApi.Controllers
  {
    
    [Route("api/[controller]")]
    [ApiController]
    public class RankingController : ControllerBase
    {
      ApplicationDbContext _context;

      public RankingController(ApplicationDbContext context)
      {
        _context = context;
      }

      // Read
      [HttpGet]
      public List<GameResult> GetGameResults()
      {
        List<GameResult> results = _context.GameResults
          .OrderByDescending(item => item.Score)
          .ToList();

        return results;
      }

      [HttpGet("{id}")]
      public GameResult GetGameResults(int id)
      {
        GameResult result = _context.GameResults
          .Where(item => item.Id == id)
          .FirstOrDefault();

        return result;
      }
    ...
  • Add data in dbo.GameResults
Blazor Ranking App
Blazor Ranking App
Blazor Ranking App
  • Create
    • ex: POST/ api/ ranking → Create item(real data in Body)
...
  [HttpPost]
  public GameResult AddGameResult([FromBody]GameResult gameResult)
  {
    _context.GameResults.Add(gameResult);
    _context.SaveChanges();

    return gameResult;
  }
...
Blazor Ranking App
  • Update
    • ex: PUT /api/ ranking(not used in web because of PUT authority problem) → request update item(real data in Body)
...
  [HttpPut]
  public bool UpdateGameResult([FromBody] GameResult gameResult)
  {
    var findResult = _context.GameResults
      .Where(x => x.Id == gameResult.Id)
      .FirstOrDefault();

    if (findResult == null)
      return false;

    findResult.UserName = gameResult.UserName;
    findResult.Score = gameResult.Score;
    _context.SaveChanges();

    return true;
  }
...
Blazor Ranking App
  • Delete
    • DELETE/ api/ ranking/ 1(not used in web because of DELETE authority problem) → delete item which is id=1
...
  [HttpDelete("{id}")]
  public bool DeleteGameResult(int id)
  {
    var findResult = _context.GameResults
      .Where(x => x.Id == id)
      .FirstOrDefault();

    if (findResult == null)
      return false;

    _context.GameResults.Remove(findResult);
    _context.SaveChanges();

    return true;
  }
...
Blazor Ranking App

Interlock RankingApp and WebApi

RankingApp

  • Reference
    • reference SharedData
  • Folder Cleanup
    • delete Data\Models
Blazor Ranking App
  • Data\ApplicationDbContext.cs
  using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
  using Microsoft.EntityFrameworkCore;
  using System;
  using System.Collections.Generic;
  using System.Text;

  namespace RankingApp.Data
  {
    public class ApplicationDbContext : IdentityDbContext
    {
      public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
      {  }
    }
  }
  • Data\Services\RankingService.cs
  using Newtonsoft.Json;
  using SharedData.Models;
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Net.Http;
  using System.Text;
  using System.Threading.Tasks;

  namespace RankingApp.Data.Services
  {
    public class RankingService
    {
      HttpClient _httpClient;

      public RankingService(HttpClient client)
      {
        _httpClient = client;
      }

      // Create
      public async Task<GameResult> AddGameResult(GameResult gameResult)
      {
        string jsonStr = JsonConvert.SerializeObject(gameResult);
        var content = new StringContent(jsonStr, Encoding.UTF8, "application/json");
        var result = await _httpClient.PostAsync("api/ranking", content);

        if (result.IsSuccessStatusCode == false)
          throw new Exception("AddGameResult Failed");

        var resultContent = await result.Content.ReadAsStringAsync();
        GameResult resGameResult = JsonConvert.DeserializeObject<GameResult>(resultContent);

        return resGameResult;
      }

      // Read
      public async Task<List<GameResult>> GetGameResultsAsync()
      {
        var result = await _httpClient.GetAsync("api/ranking");
        var resultContent = await result.Content.ReadAsStringAsync();
        List<GameResult> resGameResults = JsonConvert.DeserializeObject<List<GameResult>>(resultContent);
        return resGameResults;
      }

      // Update
      public async Task<bool> UpdateGameResult(GameResult gameResult)
      {
        string jsonStr = JsonConvert.SerializeObject(gameResult);
        var content = new StringContent(jsonStr, Encoding.UTF8, "application/json");
        var result = await _httpClient.PutAsync("api/ranking", content);

        if (result.IsSuccessStatusCode == false)
          throw new Exception("AddGameResult Failed");

        return true;
      }

      // Delete
      public async Task<bool> DeleteGameResult(GameResult gameResult)
      {
        var result = await _httpClient.DeleteAsync($"api/ranking/{gameResult.Id}");

        if (result.IsSuccessStatusCode == false)
          throw new Exception("DeleteGameResult Faild");

        return true;
      }
    }
  }
  • Razor.razor
...
  async Task SaveGameResult()
  {
    if (_gameResult.Id == 0)
    {
      _gameResult.Date = DateTime.Now;
      var result = await RankingService.AddGameResult(_gameResult);
    }
    else
    {
      var result = await RankingService.UpdateGameResult(_gameResult);
    }

    _gameResults = await RankingService.GetGameResultsAsync();
    _showPopup = false;
  }
  • Startup.cs
    • use number in Properties\launchSetting.json
  public void ConfigureServices(IServiceCollection services)
  {
    ...
    services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();

    services.AddHttpClient<RankingService>(c =>
    {
      c.BaseAddress = new Uri("https://localhost:44313");
    });
  }
Blazor Ranking App

Build

Start several project at once

  • Solution
    • [Property] - [Several start project]
Blazor Ranking App

Result(CRUD)

CREATE

READ

Blazor Ranking App

UPDATE

DELETE

Interlock RankingApp, WebApi and Unity

WebTest

  • WebManager.cs
    • Put your WebApi Server Port number in _baseUrl
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Text;
  using UnityEngine;
  using UnityEngine.Networking;

  public class GameResult
  {
    public string userName;
    public int score;
  }

  public class WebManager : MonoBehaviour
  {
    string _baseUrl = "https://localhost:44358/api";

    void Start()
    {
      GameResult res = new GameResult()
      {
        userName = "Hanna",
        score = 999
      };

      SendPostRequest("ranking", res, (uwr) =>
      {
        Debug.Log("Post Success!");
      });

      SendGetAllRequest("ranking", (uwr) =>
      {
        Debug.Log("Get All Success!");
      });
    }

    public void SendPostRequest(String url, object obj, Action<UnityWebRequest> callback)
    {
      StartCoroutine(CoSendWebRequest(url, "POST", obj, callback));
    }

    public void SendGetAllRequest(String url, Action<UnityWebRequest> callback)
    {
      StartCoroutine(CoSendWebRequest(url, "GET", null, callback));
    }

    IEnumerator CoSendWebRequest(string url, string method, object obj, Action<UnityWebRequest> callback)
    {
      yield return null;

      string sendUrl = $"{_baseUrl}/{url}/";
      byte[] jsonBytes = null;

      if(obj != null)
      {
        string jsonStr = JsonUtility.ToJson(obj);
        jsonBytes = Encoding.UTF8.GetBytes(jsonStr);
      }

      var uwr = new UnityWebRequest(sendUrl, method);
      uwr.uploadHandler = new UploadHandlerRaw(jsonBytes);
      uwr.downloadHandler = new DownloadHandlerBuffer();
      uwr.SetRequestHeader("Content-Type", "application/json");

      yield return uwr.SendWebRequest();

      if(uwr.isNetworkError || uwr.isHttpError)
      {
        Debug.Log(uwr.error);
      }
      else
      {
        Debug.Log("Recy " + uwr.downloadHandler.text);
        callback.Invoke(uwr);
      }
    }
  }
  • GameObject
Blazor Ranking App

Result

Blazor Ranking App

Download Download



C#DotNetASP.NetBlazorEntity Framework Share Tweet +1