반응형

Blazor Server 생성 단계에서 인증 유형을 개별 계정으로 선택한다

이러면 가입, 로그인 등을 할 수 있게 된다

 

RankingDB 이름으로 DB를 하나 추가 한다

그리고 appsettings.json 부분에서 다음 처럼 변경한다

{
  "ConnectionStrings": {
    //"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-RankingApp-b997cf13-bc70-49dd-9313-05e01b61b6ab;Trusted_Connection=True;MultipleActiveResultSets=true"
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=RankingDB;Trusted_Connection=True;MultipleActiveResultSets=true"

  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

DefaultConnectino 부분에서 Database=RankingDB 로 변경해준다

PM> add-migration anyName 으로 DB를 마이그레이션 할때 DefaultConnection 에 잇는 Database=RankingDB 

RankingDB 이 이름으로 DB 가 생성되게 된다

 

 

 

먼저 최종 작업한 화면은 다음과 같다

여기서 Entity = EntityFramework 는 ORM 같은 것이다

 

기본 Blazor Server 에서 Ranking 부분을 추가 한것이도 Fetch data 와는 다르게 데이터는 db 에서 읽어온다 그래서 미리 DB 에 데이터를 넣어 놓어 놓는 것으로 시작한다

 

오른쪽 상단 Register 를 눌러 가입을 하나 하면 dbo.AspNetUsers 에 추저가 하나 추가 된다

그런 다음 EmailConfirmed 를 수동으로 true 로 변경한다 (이 부분은 이메일 확인 부분이다)

유저는 기본으로 제공된느 db 를 사용한다

 

 

GameResult.cs 를 Modes 폴더를 하나 만들어추가한다(Services 로 마찬가지로 추가한다)

using System;

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

 

 

원래는 GameResult 테이블을 하나 추가해 다음 처럼 데이터를 추가해야 하지만 

이렇게 하지 않고 Entity 를 활용해 자동으로 테이블을 등록하게한다 => db에 대한 버전 관리도 되고 .cs 와 코드적으로 자동 연동 됨으로 이것이 편하다

 

ApplicationDbContext.cs 가 db 와 .cs 같에 연결해주는 핵심적인 부분이다

 

먼저 IdentityDbContext 를 상속받는다 

DbSet<GameResult> 이 부분이 DB와 코드의 연결 부분이다

IdentityDbContext : Base class for the Entity Framework database context used for identity.

using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations;
using RankingApp.Data.Models;
using System;
using System.Collections.Generic;
using System.Text;

namespace RankingApp.Data
{
    public class ApplicationDbContext : IdentityDbContext
    {
        //DbSet 으로 추가해줘야지 GameReesult.cs 에서 GameResult 내용을 DB에 저장할 수 있다. ORM
        //즉 서버를띄울때 자동으로 DB를 갱신하여 띄워주게 된다
        //PM> add-migration RankingService  명령어르 NeGet 콘솔에서 실행하여 DB를 갱신해준다
        //PM> update-database 명령어르 NeGet 콘솔에서 실행하여 DB를 갱신해준다
        public DbSet<GameResult> GameResult { 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")));

이 부분을 통해 DB 로 연결하게 된다 (Identity 를 사용하기 때문에 이 부분이 자동으로 등록 되어 있긴 하지만 안되어 있으면 추가해준다)

 

 

 

Nuget 패키지 관리자 > 패키지 관리자 콘솔 에서 다음 명령어들을 실행한다

 

PM> add-migration RankingService  명령어로 NeGet 콘솔에서 실행하여 DB를 마이그레이션 해준다
PM> update-database 명령어로 NeGet 콘솔에서 실행하여 DB를 업데이트해준다

 

이렇게 까지 하면 GameResult DB 가 자동으로 만들어진다

그럼 다음과 같이 Migratoinos 폴더에 코드로 옮겨진 것을 볼 수 있다

20230408233459_RankingService.cs (자동 생성된 파일)

using System;
using Microsoft.EntityFrameworkCore.Migrations;

namespace RankingApp.Data.Migrations
{
    public partial class RankingService : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "GameResult",
                columns: table => new
                {
                    Id = table.Column<int>(nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    UserID = table.Column<int>(nullable: false),
                    UserName = table.Column<string>(nullable: true),
                    Score = table.Column<int>(nullable: false),
                    Date = table.Column<DateTime>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_GameResult", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "GameResult");
        }
    }
}

즉 GameResult 를 알아서 해석하여 테이블을 만드는 코드가 된다

Up 일때는 들어가고 Down 일때는 Drop 하게된다 (버전 관리 개념)

 

이렇게 하지 않으면 DB 에서 Field 를 변경하거나 코드에서 Field 명등 수정 사항이 안맞는 상황들이 발생 할수 있기 때문에 번거로운 작업이 계속 될 수 있는데 이것을 코드수준에서 DB로 맞춰만들어주기 때문에 작업의 효율을 높여준다

이에 더해 버전 관리 또한 가능해지게 된다

 

 

 

데이터는 우선 수동으로 다음처럼 추가한다

 

데이터를 읽어오는 코드를 먼저 작업하자

 

그러기 위해서  ApplicationDbContext.cs 에서 다음 처럼 

DbSet 을 통해서 DB 와 연결된 부분을 읽어와야 하는데 이미 위에서 다음과 같은 코드를 통해 연결을 해놨으니

public DbSet<GameResult> GameResult { get; set; }

 

RankingService.cs 서비스를 만들어 데이터를 가져올 수 있는 코드를 만들어 준면 된다

using RankingApp.Data.Models;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace RankingApp.Data.Services
{
    public class RankingService
    {
        ApplicationDbContext _context;

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

        public Task<List<GameResult>> GetGameResultAsync()
        {
            //DB 에서 GameResult 테이블의 모든 데이터를 가져온다
            List<GameResult> results = _context.GameResult.ToList();
            return Task.FromResult(results);
        }

    }
}

이 코드를 통해 db 에 있는 GameResult 테이블 내용을 읽어올 수 있게 된다

 

그리고 RankingService 를 사용하기 위해서 Startup.cs 에 AddScoped 로 RankigService 를 추가해준다

  public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));
            services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores<ApplicationDbContext>();
            services.AddRazorPages();
            services.AddServerSideBlazor();
            services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();
            services.AddSingleton<WeatherForecastService>();
            services.AddScoped<RankingService>();
        }

 

AddScoped() 이 내용은 (https://3dmpengines.tistory.com/search/AddScoped) 이 글을 참고

 

 

 

이제 이 내용을 Blazor 를 통해서 보여주면 된다

 

Ranking.razor

@page "/ranking"
@using RankingApp.Data.Models;
@using RankingApp.Data.Services;

@inject RankingService RankingService

<h3>Ranking</h3>

@if (_gameResults == null)
{
    <p>Loading...</p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>UserName</th>
                <th>Score</th>
                <th>Date</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var gameResult in _gameResults)
            {
                <tr>
                    <td>@gameResult.UserName</td>
                    <td>@gameResult.Score</td>
                    <td>@gameResult.Date</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    List<GameResult> _gameResults;

    protected override async Task OnInitializedAsync()
    {
        //db 에서 데이터 긁어와서 _gameResults 에 넣어줌
        _gameResults = await RankingService.GetGameResultAsync();
    }

}

이 페이지를 보여주기 위해 탭을 하나 추가한다

 

이때 상단에

@inject RankingService RankingService

이 코드를 통해 RankingService 를 사용 할수 있도록 Dependency injection 해준다

 

 

NavMenu.razor

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">RankingApp</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="counter">
                <span class="oi oi-plus" aria-hidden="true"></span> Counter
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="fetchdata">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="ranking">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Ranking data
            </NavLink>
        </li>
    </ul>
</div>

@code {
    private bool collapseNavMenu = true;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}

 

그럼 다음과 같은 결과를 볼 수 있다

 

반응형

+ Recent posts