Tech for good

[Blazor, C#] Blazor WebAssembly Blog Series 4 본문

IT/Computer Science

[Blazor, C#] Blazor WebAssembly Blog Series 4

Diana Kang 2022. 2. 23. 19:02

 

* 해당 영상을 공부하며 정리한 자료입니다.

https://www.youtube.com/playlist?list=PLF1jhYUTnHo5XFX9lgS0YsNSDJHpYnRxK 

 

Blazor WebAssembly Blog Series

 

www.youtube.com

 

✔ 기본세팅

: Blazor WebAssembly, .Net6

 

 

✔ 학습목표
: Microsoft의 EntityFrameworkCore를 활용하여 데이터베이스 작업을 수행한다.

 


4. Entity Framework & SQLite in a Blazor WebAssembly Application

4.1. Add conditions (Depending on result.StatusCode)

// BlazorBlog.Client/Pages/Services/BlogService.cs

namespace BlazorBlog.Client.Services
{
    public class BlogService : IBlogService
    {
        private readonly HttpClient _http;

        public BlogService(HttpClient http)
        {
            _http = http;
        }

        public async Task<BlazorBlog.Shared.BlogPost> GetBlogPostByUrl(string url)
        {
            //var post = await _http.GetFromJsonAsync<BlazorBlog.Shared.BlogPost>($"api/Blog/{url}");
            //return post;

            var result = await _http.GetAsync($"api/Blog/{url}");
            if (result.StatusCode != System.Net.HttpStatusCode.OK)
            {
                var message = await result.Content.ReadAsStringAsync();
                Console.WriteLine(message);
                return new BlogPost { Title = message };
            }
            else
            {
                return await result.Content.ReadFromJsonAsync<BlogPost>();
            }
        }

        public async Task<List<BlazorBlog.Shared.BlogPost>> GetBlogPosts()
        {
            return await _http.GetFromJsonAsync<List<BlazorBlog.Shared.BlogPost>>("api/Blog");
        }

    }
}

 

위 코드를 실행하면 Read more... 클릭 시 라우팅이 정상적으로 되고있음을 확인할 수 있다.

 

// BlazorBlog.Server/Controllers/BlogController.cs

namespace BlazorBlog.Server.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BlogController : ControllerBase
    {
        public List<BlazorBlog.Shared.BlogPost> Posts { get; set; } = new List<BlazorBlog.Shared.BlogPost>
        {
        new BlazorBlog.Shared.BlogPost { Url = "new-tutorial", Title = "A New Tutorial about Blazor with Web API", Description="This is a new tutorial, showing you how to build a blog with Blazor", Content = "I'm going to introduce what Blazor is from now on. Enjoy!"},
        new BlazorBlog.Shared.BlogPost { Url = "first-post", Title = "My First Blog Post with Web API", Description = "Hi! This is my new shiny blog.", Content = "This is my 'Tech for good Blog'. I'm going to write it down more often in 2022 :)"}
        };

        [HttpGet]
        public ActionResult<List<BlazorBlog.Shared.BlogPost>> GiveMeAllTheBlogPosts()
        {
            return Ok(Posts);
        }

        [HttpGet("{url}")]
        public ActionResult<BlazorBlog.Shared.BlogPost> GiveMeThatSingleBlogPost(string url)
        {
            var post = Posts.FirstOrDefault(p => p.Url.ToLower().Equals(url.ToLower()));
            if (post is null)
            {
                return NotFound("This post does not exist.");
            }

            return Ok(post);
        }
    }
}

 

URL 주소를 위와 같이 엉뚱한 걸로 변경(localhost:7029/posts/new-tutorial2)하면 Post를 찾을 수 없으며, "This post does not exist"라는 문자열이 return 되고 있다.

 

4.2. Install some packages and the entity tool for instance

Package Manager Console 열기 및 실행

1. Visual Studio에서 도구 > NuGet 패키지 관리자 > 패키지 관리자 콘솔 명령을 사용하여 콘솔을 연다.

2. 패키지 관리자 콘솔 창에서 dotnet tool install --global dotnet-ef를 입력한다.

3. 위의 명령을 실행하여 dotnet tool을 설치한 후에, dotnet ef를 입력한다.

 

 

Server > NuGet 패키지 관리
  • Microsoft.EntityFrameworkCore 설치

1. 솔루션 탐색기 > BlazorBlog.Server에서 우클릭 > NuGet 패키지 관리를 클릭한다.

2. 찾아보기 > 검색 창에 Microsoft.EntityFrameworkCore 입력 > 최신 버전 설치

 

 

  • Microsoft.EntityFrameworkCore.Design 설치

 

같은 방식으로 Microsoft.EntityFrameworkCore.Design 또한 최신 버전(6.0.1)을 설치한다.

 

 

  • Microsoft.EntityFrameworkCore.Sqlite 설치

 

같은 방식으로 Microsoft.EntityFrameworkCore.Sqlite 또한 최신 버전(6.0.1)을 설치한다.

 

4.3. Inherit a DbContext using Microsoft.EntityFrameworkCore

  • Server > Data 폴더 생성 및 DataContext.cs 클래스 추가

1. BlazorBlog.Server에 Data 폴더를 새로 만든다.

2. 새로 만들어진 Data 폴더 안에 Datacontext.cs라는 클래스를 생성한다.

 

 

3. 아래와 같이 코드를 입력하여 DbContext를 상속한다.

// BlazorBlog.Server/Data/DataContext.cs

using Microsoft.EntityFrameworkCore;

namespace BlazorBlog.Server.Data
{
		// Inherit a DbContext using Microsoft.EntityFrameworkCore
    public class DataContext : DbContext
    {
        // Add a constructor with an argument & base constructor with an argument
        public DataContext(DbContextOptions<DataContext> options) : base(options)
        {
				
        }

				// Add a DbSet as property
				// to map our relational objects which are BlogPosts to a database
        public DbSet<BlazorBlog.Shared.BlogPost> BlogPosts { get; set; }
    }
}

 

4.4. Create a connection string in the appsettings.json

연결문자열(Connection String)은 데이터베이스 연결을 위해 사용된다.
// BlazorBlog.Server/appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=blazorblog.db"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

 

4.5. Register a DbContext in the configure services method of the Program.cs

주의!! .NET6는 Startup.cs가 없다. 따라서 Program.cs의 코드를 수정해야 한다!
// BlazorBlog.Server/Program.cs

using BlazorBlog.Server.Data;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.Services.AddDbContext<DataContext>(x => x.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")));

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseWebAssemblyDebugging();
}
else
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseBlazorFrameworkFiles();
app.UseStaticFiles();

app.UseRouting();


app.MapRazorPages();
app.MapControllers();
app.MapFallbackToFile("index.html");

app.Run();
※ 여기서 꿀팁! (빨간 줄 오류 뜰 때 해결방법)
: ctrl + . or Alt + Enter
-> 해당 단축키를 누르면 using을 자동으로 가져와준다!

 

4.6. Create an initial migration

1) Visual Studio에서 '도구 > NuGet 패키지 관리자 > 패키지 관리자 콘솔' 명령을 사용하여 콘솔을 연다.

 

2) 패키지 관리자 콘솔 창에서 dotnet ef migrations add Initial 명령을 입력한다.

 

  • 단, 여기서 주의할 점은 BlazorBlog.Server에 마이그레이션을 시켜야 한다는 것이다.
  • 따라서 cd 명령어로 디렉토리를 변경 후, 위의 명령어를 입력한다!

 

 

3) 그럼 아래와 같이 Initial이라고 불리는 마이그레이션 클래스 파일이 생성된다.

BlazorBlog/Migrations/20220203100144_Initial.cs

 

dotnet ef migrations add Initial 명령을 통해 마이그레이션을 생성하는 코드를 생성하였으며, 해당 코드는 Migrations\<timestamp>_Initial.cs 파일에 있다.

 

  •  Up, Down 메서드
    • Initial 클래스의 Up 메서드는 데이터 모델 엔터티 집합에 해당하는 데이터베이스 테이블을 만들며, Down 메서드는 테이블을 삭제한다.
    • 엔티티 프레임워크를 활용하면 field ID에 맞는 PrimaryKey 또한 생성할 수 있다.

https://docs.microsoft.com/ko-kr/aspnet/core/data/ef-rp/migrations?view=aspnetcore-6.0&tabs=visual-studio 

 

4부. ASP.NET Core에서 EF Core를 사용한 Razor Pages - 마이그레이션

Razor Pages 및 Entity Framework 자습서 시리즈의 4부입니다.

docs.microsoft.com

 

 

4.7. Create a database (by applying migrations in production)

1) 패키지 관리자 콘솔 창에서 dotnet ef database update 명령을 입력한다.

 

2) 그럼 아래와 같이 blazorblog.db가 생성된다.

 

하지만 위 파일을 통해서는 db의 내용을 파악할 수 없기 때문에 아래의 작업을 추가적으로 진행해준다.

 

 

4.8. Use db browser for SQLite

1) DB Browser for SQLite를 다운로드 한 후, 실행한다.

 

https://sqlitebrowser.org/dl/

 

Downloads - DB Browser for SQLite

(Please consider sponsoring us on Patreon 😄) Windows Our latest release (3.12.2) for Windows: Windows PortableApp Note - If for any reason the standard Windows release does not work (e.g. gives an error), try a nightly build (below). Nightly builds ofte

sqlitebrowser.org

 

2) Visual Code 솔루션 탐색기 > BlazorBlog.Server/blazorblog.db에서 우클릭 후, 전체 경로 복사를 누른다.

 

3) DB Browser for SQLite를 실행시킨 후, 상단의 Open Database를 누른다.

 

4) 2번 단계에서 복사했던 db 파일의 경로를 아래와 같이 붙여넣기 한다.

 

5) 그러면 아래와 같이 연결된 테이블을 확인할 수 있다. 여기서 Browse Data 탭 바를 클릭한다.

 

 

 

6) 아래와 같이 BlogPosts 테이블을 작성한 후, Write Changes 버튼을 누른다.

 

7) BlogController.cs 파일로 돌아가 아래와 같이 코드를 수정한다.

// 이전 BlazorBlog.Server/Controllers/BlogController.cs

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace BlazorBlog.Server.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BlogController : ControllerBase
    {
        public List<BlazorBlog.Shared.BlogPost> Posts { get; set; } = new List<BlazorBlog.Shared.BlogPost>
        {
        new BlazorBlog.Shared.BlogPost { Url = "new-tutorial", Title = "A New Tutorial about Blazor with Web API", Description="This is a new tutorial, showing you how to build a blog with Blazor", Content = "I'm going to introduce what Blazor is from now on. Enjoy!"},
        new BlazorBlog.Shared.BlogPost { Url = "first-post", Title = "My First Blog Post with Web API", Description = "Hi! This is my new shiny blog.", Content = "This is my 'Tech for good Blog'. I'm going to write it down more often in 2022 :)"}
        };

        [HttpGet]
        public ActionResult<List<BlazorBlog.Shared.BlogPost>> GiveMeAllTheBlogPosts()
        {
            return Ok(Posts);
        }

        [HttpGet("{url}")]
        public ActionResult<BlazorBlog.Shared.BlogPost> GiveMeThatSingleBlogPost(string url)
        {
            var post = Posts.FirstOrDefault(p => p.Url.ToLower().Equals(url.ToLower()));
            if (post is null)
            {
                return NotFound("This post does not exist.");
            }

            return Ok(post);
        }
    }
}
// 이후 BlazorBlog.Server/Controllers/BlogController.cs

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace BlazorBlog.Server.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BlogController : ControllerBase
    {
        private readonly BlazorBlog.Server.Data.DataContext _context;

        // Inject DataContext
				// ASP.NET Core 종속성 주입은 DataContext의 인스턴스를 컨트롤러에 전달하는 것을 담당한다.
        public BlogController(BlazorBlog.Server.Data.DataContext context)
        {
            _context = context;
        }

        [HttpGet]
        public ActionResult<List<BlazorBlog.Shared.BlogPost>> GiveMeAllTheBlogPosts()
        {
            return Ok(_context.BlogPosts);
        }

        [HttpGet("{url}")]
        public ActionResult<BlazorBlog.Shared.BlogPost> GiveMeThatSingleBlogPost(string url)
        {
            var post = _context.BlogPosts.FirstOrDefault(p => p.Url.ToLower().Equals(url.ToLower()));
            if (post is null)
            {
                return NotFound("This post does not exist.");
            }

            return Ok(post);
        }
    }
}

 

+ 참고자료

 

https://docs.microsoft.com/ko-kr/ef/core/dbcontext-configuration/

 

DbContext 수명, 구성 및 초기화 - EF Core

종속성 주입 사용 여부에 따라 DbContext 인스턴스를 만들고 관리하는 패턴

docs.microsoft.com

https://docs.microsoft.com/ko-kr/aspnet/core/data/ef-mvc/intro?view=aspnetcore-6.0

 

자습서: ASP.NET MVC 웹앱에서 EF Core 시작

이 페이지는 Contoso University 샘플 EF/MVC 앱을 빌드하는 방법을 설명하는 자습서 시리즈 중 첫 번째입니다.

docs.microsoft.com

 

8) 패키지 관리자 콘솔 창에서 dotnet watch run 명령을 입력한다.

 

9) 크롬 창이 열리며 아래와 같은 화면을 볼 수 있다.