반응형

ET Core 간단하게 알아보기

데이터 모델링은 3가지 방법으로 가능하다.

-Convention(관례)

-Data Annotation(데이터 주석)

-Fluent API(직접 정의)

1. 관례의 경우에는 Id나 [클래스]Id가 붙을 경우 자동적으로 priamry key로 인식이 된다.

2. Attribute 즉, 속성을 활용하여 가능하다.

ex)

public int Id{ {get; set;}

[MaxLength(32)]

public string UserName {get; set;}

3. API를 사용하여 직접정의한다.

protected overrid void OnModelCreationg(ModelBuilder builder)

{

builder.Entity<GameResult>().Properity(x => x.UserName).ISUnicode(false);

}

지금까지는 1번 즉 관례의 방식으로 데이터 모델링을 사용했으나

2번과 3번도 유용하게 사용되는 경우가 있기에 모든 경우에 대하여 간단하게 알아보고자 한다.

[1]Entity Class에 대한 관례

정의) EF가 참조하는 클래스를 Entity Class라고 한다.

관례)

-public

-static이면 안됨

-생성자가 없거나, 인자 없는 생성자가 있어야 함

-id혹은 [클래스]id 형태의 property를 테이블의 Primary Key로 간주한다.

Column에 대한 관례

관례)

-property의 이름이 테이블 컴럼 이릅으로 사용

-propery의 타입을 해당하는 SQL타입으로 변환

-C# 형식의 기본 nullable이 테이블 컬럼 nullable에 영향

[2]Data Annotation

Blazor의 Form 검증 때 사용한 모든 Annotation이 모두 해당한다.

ex) Required, MaxLength...

[3] FLUENT API

Convention이나 Data Annotation으로 할 수 없는 모든 경우

Fluent Api로 처리가 가능하다.

ex)

protected override void OnModelCreating(ModelBuilder builder)

{

builder.Entity<GameResult>().Property(x => x.UserName).IsUnicode(false);

//UserId를 Index로 추가하는 코드 Has라 헷갈릴수 있지만 추가하는 코드이다.

builder.Entity<GameResult>().HasIndex(x => x.UserId);

//Score가 1000을 넘는 경우에만 필터링을 처리해서 추출하는 코드

builder.Entity<GameResult>().HasQueryFilter(p => p.Score > 1000);

}

이번엔 DB에서 특정 부분만을 제거해도록 하겠다.

Data Annotation에서는 [NotMapped] 속성을 사용하면 된다.

이를 만약에 class에다가 사용할 경우 DB생성에서 제외가 된다.

그리고 Fluent Api에서 사용할 경우

builder.Entity<GameResult>().Ignore(g => g.Excluded);

를 사용하면 된다.

다음으로 DB의 칼럼에서 TYPE과 SIZE 그리고 NULL여부를 제어하고 싶은 경우를 알아보겠다.

[Data Annotation의 경우]

Not null => [Required]

Set String Size => [MaxLength(123)]

Set String VarChar => X

[Fluent Api의 경우]

Not null => .IsRequired();

Set String Size => .HasMaxLength(123)

Set String Varchar => .IsUnicode(false)

테이블의 Primary Key를 설정하는 방식을 알아보겠다.

이 경우에는 위의 3가지 방식이 모두 가능하다.

[관례의 경우]

property의 이름에 Id가 들어가 있으면 기본 키로 인식해준다.

[속성의 경우]

[Key] 속성 값을 프로퍼티위에 붙여준다.

만약 [Key]의 값이 2개 이상의 프로퍼티에 지정되어 있는 복합 키라면

복합 키로 사용되므로 [Column(Order =0)]의 속성을 붙여서

순서를 지정해준다.

[Api의 경우]

.HasKey()로 지정해준다.

다음으로 테이블의 Index를 설정하는 경우는

Fluent Api만 가능하다.

builder.Entity<GameResult>().HasIndex(x => x.UserId);

그리고 테이블의 이름을 설정하는 것은 관례,속성,Api 모든 경우가 가능하다.

[관례]

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

[속성]

[Table("GameResultTable")] 을 class위에 붙여준다.

[Api]

builder.Entity<GameResult>().ToTable("GameResultTable");

마지막으로 결론을 정리해보자면

1)기본 상태로 충분하다면 관례방식으로

2)속성으로 가능하다면, 속성방식을 무조건 사용하자!

속성은 낮우에 Blazor Form Validation등 다른 곳에서도 재사용이 가능하기 때문이다.

3)위의 2가지 경우가 안되는 정말 절망적인 상황이라면 Fluent Api를 사용하자!

ex) Index관련...

 

반응형
반응형

REST ( Representational State Trasnfer) 공식표준 스펙은 아닌데

원래 있던 HTTP 통신에서  재사용 하여 데이터 송수신 규칙을 만든것 => CRUD

 

 

CRUD 를 Blazor API 서버를 활용하여 작업 한것 

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SharedData.Models;
using System.Collections.Generic;
using System.Linq;
using WebAPI.Data;

namespace WebAPI.Controllers
{
    //www.google.com 이건 전체 주소
    //www.google.com/api/ranking   이건 세부 라우팅 주소로 전체 주소에서 구분지어 들어갈 때 사용됨
    //그 다음 어떤걸 하는지 지정 할 수 있다 GET, POST, PUT...)
    //Create : POST 방식 api/ranking => body 에 실제 정보를 담는다 , 아이템 생성 요청

    //Read 시에는 보통 Get 을 사용한다
    //GET : api/ranking => 전체 아이템을 가져온다 , api/ranking/1 id가 1인 아이템을 가져온다
    //get /api/ranking  모든 id를 주세요
    //get /api/ranking/1   id 중에서 1전을 주세요 라는 요청 
    //get 은 body 에 정보를 넣지 않고 이 요청 하나로 처리 된다

    //update : put 으로 사용됨
    //PUT : api/ranking  (put은 보안 무제로 일반적인 웹에서 사용되지 않는다)
    //put 은 body 에 정보를 넣어서 요청을 보낸다

    //delete /api/ranking/1   이렇게 삭제 할 수 있는데 보안 문제로 웹에서 쓰지 않는다
    //id=1 번인 아이템 삭제 요청

    //api controller 는 c# 객체를 반환하는 것이 가능하다
    //null 반환하면 클라에서 204 Response (No Content) 를 받는다
    //String을 반호나하면 => text/plain 타입으로 반환한다
    //나머지는 json 형태로 반환한다

    //localhost:portnumber/api/ranking 이 요청이라는 얘기인데 RankingController 이것이 [controller] 부분에서 ranking 으로 변환됨
    [Route("api/[controller]")]
    [ApiController]
    public class RankingController : ControllerBase
    {
        ApplicationDbContext _context;
        public RankingController(ApplicationDbContext context)
        {
                _context = context;

        }

        //create, body 에 보낼때는 [frombody 를 넣어주면 된다]
        [HttpPost]
        public GameResult AddGameResult([FromBody] GameResult gameResult)
        {
            _context.GameResultList.Add(gameResult);
            _context.SaveChanges();
            return gameResult;
        }


        //update
        [HttpPut]
        public bool UpdateGameResult([FromBody] GameResult gameResult)
        {
            var findResult = _context.GameResultList.Where(item => item.Id == gameResult.Id).FirstOrDefault();
            if(findResult == null)
            {
                return false;
            }

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

            return true;
        }

        //read
        //ranking
        [HttpGet]
        public List<GameResult> GetGameResults()
        {
            List<GameResult> results = _context.GameResultList.OrderByDescending(item=> item.Score).ToList();
            return results;
        }

        //ranking/1
        [HttpGet("{id}")]
        public GameResult GetGameResult(int id)
        {
            GameResult result = _context.GameResultList.Where(item => item.Id == id).FirstOrDefault();
            return result;
        }


        //delete
        [HttpDelete("{id}")]
        public bool DeleteGameResult(int id)
        {
            var findResult = _context.GameResultList.Where(item => item.Id == id).FirstOrDefault();
            if (findResult == null)
            {
                return false;
            }
            _context.GameResultList.Remove(findResult);
            _context.SaveChanges();

            return true;
        }
    }
}

 

아래 예시는 모든 데이터를 json 으로 갖고 오는 예시이다

반응형
반응형
Console.WriteLine(default(int));  // output: 0
Console.WriteLine(default(object) is null);  // output: True

void DisplayDefaultOf<T>()
{
    var val = default(T);
    Console.WriteLine($"Default value of {typeof(T)} is {(val == null ? "null" : val.ToString())}.");
}

DisplayDefaultOf<int?>();
DisplayDefaultOf<System.Numerics.Complex>();
DisplayDefaultOf<System.Collections.Generic.List<int>>();
// Output:
// Default value of System.Nullable`1[System.Int32] is null.
// Default value of System.Numerics.Complex is (0, 0).
// Default value of System.Collections.Generic.List`1[System.Int32] is null.

 

 

ref : https://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/operators/default

반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

linq (3)  (0) 2023.04.10
linq (2)  (0) 2023.04.09
Linq (1)  (0) 2023.04.08
async/await & 커피와 베이컨  (0) 2023.04.07
C# - ArraySegment  (0) 2023.01.04
반응형

우선 테이블 명을 GameResultList 로 변경하였다

 

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 에서 GameResultList 내용을 DB에 저장할 수 있다. ORM
        //즉 서버를띄울때 자동으로 DB를 갱신하여 띄워주게 된다
        //PM> add-migration RankingService  명령어르 NeGet 콘솔에서 실행하여 DB를 갱신해준다
        //PM> update-database 명령어르 NeGet 콘솔에서 실행하여 DB를 갱신해준다
        //주의!! GameResultList 이 테이블 명과 동일해야 한다 dbo.GameResultList 에서 dbo 빼고   GameResultList 만
        public DbSet<GameResult> GameResultList { get; set; }

        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }
}

 

그런다음 패키지 관리자 콘솔에서 다음 처럼 입력한다음 다음 처럼 적용한다

PM> Add-Migration GameResultList

PM> Update-Database

이 처럼 마이그레이션이 변경된다

테이블명도 자동으로 바뀌게 된다

 

서비스 쪽에 갱신과 삭제에 대한 로직을 추가 하고 테이블에도 값이 삭제/갱신이 적용 될수 있게 처리 한다

       public Task<bool> UpdateGameResult(GameResult gameResult)
        {
            var result = _context.GameResultList.Where(x => x.Id == gameResult.Id).FirstOrDefault();
            if(result==null)
            {
                return Task.FromResult(false);
            }
            result.UserName = gameResult.UserName;
            result.Score = gameResult.Score;
            _context.SaveChanges();

            return  Task.FromResult(true);
        }

        public Task<bool> DeleteGameResult(GameResult gameResult)
        {
            var result = _context.GameResultList.Where(x => x.Id == gameResult.Id).FirstOrDefault();
            if (result == null)
            {
                return Task.FromResult(false);
            }
            _context.GameResultList.Remove(gameResult);
            _context.SaveChanges();
            return Task.FromResult(true);
        }

 

 

Ranking.razor 

갱신과 삭제 버튼이 보여 내용을 수정 할수 있도록 하고

추가팝업과 갱신 팝업은 동일한 형태로 사용한다

그리고 로그인이 되어 있지 않다면 유저 정보는 보이지 않도록 Autorized, NotAutorized 처리를 해 놓았다

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

@inject RankingService RankingService

<h3>Ranking</h3>

<AuthorizeView>
    <Authorized>
        
        @if (_gameResultList == null)
        {
            <p>Loading...</p>
        }else{
            <table class="table">
                <thead>
                    <tr>
                        <th>UserName</th>
                        <th>Score</th>
                        <th>Date</th>
                        <th></th>
                        <th></th>
                    </tr>
                </thead>
                <tbody>
                    @foreach (var gameResult in _gameResultList)
                    {
                        <tr>
                            <td>@gameResult.UserName</td>
                            <td>@gameResult.Score</td>
                            <td>@gameResult.Date</td>
                            <td>
                                <button class="btn btn-primary" @onclick="() => EditGameResult(gameResult)">
                                    Edit
                                </button>
                            </td>
                            <td>
                                <button class="btn btn-primary" @onclick="() => DeleteGameResult(gameResult)">
                                    Delete
                                </button>
                            </td>
                        </tr>
                    }
                </tbody>
            </table>
            <p>
                <!-- make button to add gameresult -->
                <button class="btn btn-primary" @onclick="AddGameResult">
                    Add
                    </button>
            </p>


            @if(_showPopup)
            {
                //dialog to show gameresult
                <div class="modal" style="display:block" role="dialog">
                    <div class="modal-dialog">
                        <div class="modal-content">
                            <div class="modal-header"></div>
                            <h3 class="modal-title">Add/Update Game Result</h3>

                            <button type="button" class="close" @onclick="ClosePopup">
                                <span aria-hidden="true">X</span>
                            </button>
                            <div class="modal-body">
                                <label for="UserName"> UserName</label>
                                <input class="form-control" type="text" placeholder="UserName" @bind-value="_gameResult.UserName" />
                                <label for="Score"> Score</label>
                                <input class="form-control" type="text" placeholder="Score" @bind-value="_gameResult.Score" />

                                <button class="btn-primary" @onclick="SaveGameResult">Save</button>
                            </div>
                        </div>
                    </div>
                </div>
            
            }
        }

    </Authorized>
    <NotAuthorized>
        인증 안됨(로그인 안됨)
    </NotAuthorized>

</AuthorizeView>

@code {
    List<GameResult> _gameResultList;

    GameResult _gameResult;

    bool _showPopup = false;

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

    public async Task<List<GameResult>> readGameResultList()
    {
        //db 에서 데이터 긁어와서 _gameResultList 에 넣어줌
        _gameResultList = await RankingService.GetGameResultAsync();
        return _gameResultList;
    }


    public void AddGameResult()
    {
        _showPopup = true;
        //Add new gameresult to
        _gameResult = new GameResult()
        {
            Id = 0
        };
    }

    //ClosePopup
    void ClosePopup()
    {
        _showPopup = false;
    }


    void EditGameResult(GameResult gameResult)
    {
        _showPopup = true;
        _gameResult = gameResult;
    }

    async Task DeleteGameResult(GameResult gameResult)
    {
        var result = RankingService.DeleteGameResult(gameResult);
        await readGameResultList();
    }


    async Task SaveGameResult()
    {
        //새로 만드는 상태
        if(_gameResult.Id==0)
        {
            //save to db
            _gameResult.Date = DateTime.Now;
            var result = RankingService.AddGameResult(_gameResult);
            //close popup
            ClosePopup();
        }
        else
        {
            //수정하고 있는 상태
            var result = RankingService.UpdateGameResult(_gameResult);
        }

        await readGameResultList();
        _showPopup = false;
    }


}

 

결과는 다음과 같고

 

Edit 버튼을 눌러 값 또한 수정 반영 할 수 있다

 

 

로그인 안했을대 로그인 안된다는 문자표시

반응형
반응형

3DMP

 

 

 [ 강연 분야 ]  

  • IT 개발 분야 전망, 투자,  컨설팅, 대학, 회사 강연 특강
  •  프로그램, 코딩, 취직, 동기부여, IT 산업, 실무 이야기, 면접, 면접과 면접자의 관점
  • 과외문의는 받지 않습니다

 

이력 -1 ]

  • 크래프톤, 넥슨, 넷마블, NHN Games  및 다수 동종/타종 근무경험 및 다년간 현업 개발자
  • 3D게임 엔진/클라이언트 프로그래머/언리얼, 유니티 엔진/게임 메인 게임 개발 /모바일,PC/네트워크 프로그램 개발
  • 고차원 수학/ 물리 들의 알고리즘과 관련된 특수 프로그램 제작, 미들웨어(엔진)/ 일반 프로그램 프로젝트 제작
  • 게임업계 대기업 넥슨, NC, 펄어비스, 중견 B사, 등등의 기업에 합격시킨 노하우
  • 다수, 다양한 분들에 대한 다수의 과외 경험으로 축적된 노하우 보유
  • 기타 일반 프로젝트 경험
  • 컨설팅


[ 이력-2 : 이직/취직/ 과외를 수강하신 분들의 회사
]

  • 과외 분야 : https://3dmpengines.tistory.com/2331
  • 넥슨 코리아, NC, 넷마블, 넷게임즈, EA 코리아의, 마이크로소프트 - 프로그래머 다수/TA/기획자
  • 크래프톤, 펍지, 펄어비스, 블루홀의 - 프로그래머/TA/기획자
  • 스마일게이트, NHN, 기타 등등 의 - 프로그래머/TA/기획자/UI
  • 게임/비게임 임직원, CEO포함 수강
  • 대학생(취준생), SKY, 포함 상위 10% 이상 대학생, 그 외 글로벌 학교 및 유학생
  • 전문 프로그래머로 진로를 희망하시는 분들들을 위한 강의 및 컨설팅
  • 전공자/컴퓨터 관련 전공자가 아니지만 게임이나 프로그램쪽으로 진로를 희망하시는 분들
    (비전공자 일지라도 많은 상당수의 분들이 원하는 성과를 이루고 가셨습니다)
  • 프로그램을 혼자 하기엔 막연하여 그룹(단체) 수강
  • 초심자로 프로그램 배워보려 했지만 진입 장벽때문에 포기하신 분들 또한 수강

 

[문의]

강연 문의 : 3dmpengines@gmail.com

https://pf.kakao.com/_uxabzK

 

상담 전용 카톡 아이디 : 게임프로그래밍 과외

 

 

반응형

'광고' 카테고리의 다른 글

광고문의  (0) 2023.05.07
반응형

 

랭킹 데이터를 추가 하고 다시 가져오는 코드이다

 

Ranking.razor

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

@inject RankingService RankingService

<h3>Ranking</h3>

<AuthorizeView>
    <Authorized>
        
        @if (_gameResultLIst == 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 _gameResultLIst)
                    {
                        <tr>
                            <td>@gameResult.UserName</td>
                            <td>@gameResult.Score</td>
                            <td>@gameResult.Date</td>
                        </tr>
                    }
                </tbody>
            </table>
            <p>
                <!-- make button to add gameresult -->
                <button class="btn btn-primary" @onclick="AddGameResult">
                    Add
                    </button>
            </p>


            @if(_showPopup)
            {
                //dialog to show gameresult
                <div class="modal" style="display:block" role="dialog">
                    <div class="modal-dialog">
                        <div class="modal-content">
                            <div class="modal-header"></div>
                            <h3 class="modal-title">Add/Update Game Result</h3>

                            <button type="button" class="close" @onclick="ClosePopup">
                                <span aria-hidden="true">X</span>
                            </button>
                            <div class="modal-body">
                                <label for="UserName"> UserName</label>
                                <input class="form-control" type="text" placeholder="UserName" @bind-value="_gameResult.UserName" />
                                <label for="Score"> Score</label>
                                <input class="form-control" type="text" placeholder="Score" @bind-value="_gameResult.Score" />

                                <button class="btn-primary" @onclick="SaveGameResult">Save</button>
                            </div>
                        </div>
                    </div>
                </div>
            
            }
        }

    </Authorized>
    <NotAuthorized>
        인증 안됨(로그인 안됨)
    </NotAuthorized>

</AuthorizeView>

@code {
    List<GameResult> _gameResultLIst;

    GameResult _gameResult;

    bool _showPopup = false;

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

    public async Task<List<GameResult>> readGameResults()
    {
        //db 에서 데이터 긁어와서 _gameResults 에 넣어줌
        _gameResultLIst = await RankingService.GetGameResultAsync();
        return _gameResultLIst;
    }

    
    public void AddGameResult()
    {
        _showPopup = true;
        //Add new gameresult to
        _gameResult = new GameResult()
        {
            Id = 0
        };
    }

    //ClosePopup
    void ClosePopup()
    {
        _showPopup = false;
    }

    async Task SaveGameResult()
    {
        //새로 만드는 상태
        if(_gameResult.Id==0)
        {
            //save to db
            _gameResult.Date = DateTime.Now;
            var result = RankingService.AddGameResult(_gameResult);
            //close popup
            ClosePopup();
        }
        else
        {
            //수정하고 있는 상태
            
        }
        
        _gameResultLIst = await readGameResults();
    }
}

 

 

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

        public Task<GameResult> AddGameResult(GameResult gameResult)
        {
            _context.GameResult.Add(gameResult);
            _context.SaveChanges(); //db 에 실제 저장이 됨
            return Task.FromResult(gameResult);
        }

    }
}

AddGameResult() 함수가 추가 되었고  

_context.GameResult.Add(gameResult);
_context.SaveChanges(); //db 에 실제 저장이 됨

이 코드가 실제 DB 에 저장하는 부분이 된다

이후 결과는 Task 로 리턴해주고 결과를 담을때는 FromResult 로 결과를 담아줘서 리턴하면 await 으로 결과를 기다 릴 수 있다

    public async Task<List<GameResult>> readGameResults()
    {
        //db 에서 데이터 긁어와서 _gameResults 에 넣어줌
        _gameResultLIst = await RankingService.GetGameResultAsync();
        return _gameResultLIst;
    }

 

 

 

 

유저를 하나 추가하면 다음처럼 보이게 된다 (test1 유저 추가)

DB 데이터에 추가된 내용

반응형
반응형

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