Create Project
- Blazor Server App
- [Auth]-[Individual User Account]-[Save in User Account 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]



- 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



- Click [Click here to comfirm your account], then your email will be comfirmed
- Login with this account



- You can see your log in data in topbar

Check User
- Check User in Database
- Open
dbo.AspNetUsers
Data - You can check your registed account
- Open



Migration
- Models
- Create
Data\Models
folder
- Create

- 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; }


- Input Data in Database directly



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
- Builds operations that will migrate the database
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 bothup
anddown
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 thedown
direction.
- Builds operations that will migrate the database
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>


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;
}

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; }
}
}

PostMan
Download
- Postman
- Download Postman
- deactivate [Settings]-[Enable SSL certificate verification]

WebApi
Add a new project
- WebApi
- ASP.NET Core Web Application
- template: API
- package: Microsoft.EntityFrameworkCore, Microsoft.EntityFrameworkCore.Design, Microsoft.EntityFrameworkCore.SqlServer

Reference
- Dependency
- references
SharedData
- references
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"
},
...
}

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





- 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;
}
...

- 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;
}
...


- 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;
}
...


Interlock RankingApp and WebApi
RankingApp
- Reference
- reference
SharedData
- reference
- Folder Cleanup
- delete
Data\Models
- delete

- 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
- use number in
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();
services.AddHttpClient<RankingService>(c =>
{
c.BaseAddress = new Uri("https://localhost:44313");
});
}

Build
Start several project at once
- Solution
- [Property] - [Several start project]

Result(CRUD)
CREATE
READ

UPDATE
DELETE
Interlock RankingApp, WebApi and Unity
WebTest
- WebManager.cs
- Put your WebApi Server Port number in
_baseUrl
- Put your WebApi Server Port number in
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

Result
