C#에서 저장소(DB)에 있는 데이터를 읽어서 API화를 하는 간단한 프로젝트를 생성해보자

웹 API를 작성하는 방법은 여러가지가 있으나, 다음 보너스 기능을 고려하였습니다.

보너스기능

  • Docker지원 ( 리눅스 디버깅환경 )
  • ORM 지원 ( Mysql + 메모리DB 를 객체로만 접근하여 일괄적으로 사용)


또한 .net core api 의 스펙을 사용하여 사용자 계정을 컨트롤하는 공통 마이크로 서비스작성에 촛점을 두었습니다.

git : https://github.com/psmon/netcore-restweb-entity-akka


IDE환경

  • Visual Studio 2017 Community
  • MySQL WorkBench

프로젝트 생성

API : 마이크로 서비스 API의 구성요소를 위해 작고가벼운 API을 선택하였습니다.

ASP.net Core 2.1 : .net framework 4.? 은 주로 윈도우에 구동가능한 프레임웍입니다. 리눅스구동을위해 이것은 없다고 치고 ,리눅스 구동가능한 Core버젼으로 시작을 합니다.

Docker지원 : Docker/리눅스 명령을 알지못해도 리눅스 구동개발환경(HyperV for Docker)을 지원합니다. .net core가 리눅스를 지원함으로 가능해졌습니다.

Https 구성 : 마이크로서비스가 https 인증서를 모두가지고 있다고하면 관리가 어려워집니다. , https는 haproxy와 같이 별도의 L7 로드밸런스를 통해 셋팅될예정입니다.


프로젝트 구성

  • 1 : 디버깅시 실행환경을 선택할수가 있습니다.  IIS/Console/Docker등에서 구동가능합니다.
  • 2: RestAPI 샘플코드가 생성됩니다. Endpoint의 주소를 코드로 맵핑하는 패턴은 이제 프레임워크가 달라도 큰차이가 없습니다.
  • 3: Controllers 에서 추가적인 API들의 집합체를 추가할수가 있습니다.


저장소 제공자 추가하기

NuGet 패키지지원되는 데이터베이스 엔진유지 관리자/공급 업체참고/요구 사항유용한 링크
Microsoft.EntityFrameworkCore.SqlServerSQL Server 2008 이상EF Core 프로젝트(Microsoft)
docs
Microsoft.EntityFrameworkCore.SqliteSQLite 3.7 이상EF Core 프로젝트(Microsoft)
docs
Microsoft.EntityFrameworkCore.InMemoryEF Core 메모리 내 데이터베이스EF Core 프로젝트(Microsoft)테스트 전용docs
Npgsql.EntityFrameworkCore.PostgreSQLPostgreSQLNpgsql 개발 팀
docs
Pomelo.EntityFrameworkCore.MySqlMySQL, MariaDBPomelo Foundation 프로젝트
readme
Pomelo.EntityFrameworkCore.MyCatMyCAT ServerPomelo Foundation 프로젝트시험판, 최대 EF Core 1.1readme
EntityFrameworkCore.SqlServerCompact40SQL Server Compact 4.0Erik Ejlskov Jensen.NET Frameworkwiki
EntityFrameworkCore.SqlServerCompact35SQL Server Compact 3.5Erik Ejlskov Jensen.NET Frameworkwiki
MySql.Data.EntityFrameworkCoreMySQLMySQL 프로젝트(Oracle)시험판docs
FirebirdSql.EntityFrameworkCore.FirebirdFirebird 2.5 및 3.xJiří ČinčuraEF Core 2.0 이상docs
EntityFrameworkCore.FirebirdSqlFirebird 2.5 및 3.xRafael AlmeidaEF Core 2.0 이상wiki
IBM.EntityFrameworkCoreDb2, InformixIBMWindows 버전블로그
IBM.EntityFrameworkCore-lnxDb2, InformixIBMLinux 버전블로그
IBM.EntityFrameworkCore-osxDb2, InformixIBMmacOS 버전블로그
Devart.Data.Oracle.EFCoreOracle 9.2.0.4 이상DevArt지급docs
Devart.Data.PostgreSql.EFCorePostgreSQL 8.0 이상DevArt지급docs
Devart.Data.SQLite.EFCoreSQLite 3 이상DevArt지급docs
Devart.Data.MySql.EFCoreMySQL 5 이상DevArt지급docs
EntityFrameworkCore.JetMicrosoft Access 파일BubiEF Core 2.0, .NET Frameworkreadme

사용할 DB를 선택을 하였다고하면, 프로젝트→Nuget 패키지관리에서 설치를 합니다.

여기서는 .net core에서 ORM을 활용할수 있는 EF(Entity FrameWork) 2.1 와

EntityCore에서 Mysql을 지원하는 Pomelo.EntityFrameworkCore.Mysql 을 사용하였습니다. 


DB저장소를 이용하는 첫 Entity 만들기

Entity생성

C#SQL-DDL
using System;
using System.ComponentModel.DataAnnotations.Schema;


namespace accountapi.Models
{
    [Table("student")]
    public class Student
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstName { get; set; }
        public DateTime RegDate { get; set; }

    }
}
CREATE TABLE `student` (
  `id` int(11) NOT NULL,
  `lastname` varchar(45) DEFAULT NULL,
  `firstname` varchar(45) DEFAULT NULL,
  `regdate` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

Entity(C#객체) 를 통해 자동으로 DDL코드가 생성되는 방법및 동기화하는 방법도 존재하지만 여기서

Table생성은 SQL문을 통해 진행합니다. 

Repository(DBContent)생성

C#DB-SCHMEMAS
using accountapi.Models;
using Microsoft.EntityFrameworkCore;

namespace accountapi.Repository
{
    public class AccountContent : DbContext
    {
        public DbSet<Student> Students { get; set; }

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


DataBase에서 하나의 스키마는 여러가지의 테이블을 가지고 있을수 있습니다.

유사한 개념으로 하나의 DBContext(Repository)는 하나의 스키마에 대응할수도 있으며 여러개의 Entity(Table)을 가질수가 있습니다.

DB에서의 스키마와 DBContext의 논리적 구분이 다를수 있음으로 꼭 1:1을 지킬필요는 없습니다.

하나의 스키마는 여러개의 논리적인 DBContext를 가진방식으로 설계할수도 있습니다.

Controller에 DBContent 연결

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using accountapi.Models;
using accountapi.Repository;
using Microsoft.AspNetCore.Mvc;

namespace accountapi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        private readonly AccountContent _context;

        public ValuesController( AccountContent context )
        {
            _context = context;

            if ( _context.Students.Count() == 0 )
            {
                // 테스트용
                // Create a new Student if collection is empty,
                // which means you can't delete all Student.
                _context.Students.Add(new Student { ID=0, FirstName = "ORM",LastName="Entity" });
                _context.SaveChanges();                
            }
        }

하나의 API Controller는 하나의 DBContext를 가질수 있습니다. 하나의 컨트롤러가 다중의 DBContext를 가질수도 있지만

이 규칙은 지켜주는것이 좋습니다. 

ORM을 사용하기위해  복잡한 과정을 셋팅한것같지만, SQL문을 직접 사용하는 방식에서 처음 셋팅은 여기까지는 동일합니다.

여기서 차이점은 이후에 발생하며 SQL문을 직접 사용할시 여기에서 SQL문을 Entity와 맵핑처리하는 공수가 더들어가게되며 다양한 조회문

( 3가지가 패턴이있다고 가정합시다.)  데이터삽입(2패턴) / 업데이트(2패턴) / 딜리트(2패턴) 등이 있다고 하면 

SQL문 작성을  패턴별로 9가지를 해야하며, 각각 의 결과문에 해당하는 맵핑처리를 하는 코드를 작성해야 합니다.

ORM은 오브젝트 접근을 통해서만 CRUD(Create Read Update Delete)를 지향하는 방식으로 반복 SQL문 작성을 줄일수가 있습니다.

다음 Document를 참고합니다.


실제 DB연결환경 설정

using accountapi.Repository;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace accountapi
{
    public class Startup
    {
        public Startup( IConfiguration configuration )
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices( IServiceCollection services )
        {
            services.AddDbContext<AccountContent>(opt =>
                opt.UseMySql("server=localhost;database=db_account;user=psmon;password=db1234"));            

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

특정 Object에 어떠한 의존성(mysql설정)을 설정하는것은 의존성 주입이라고도 불립니다.

DBContext(Repository) 는 특정한 DB를 지정할수가 있으며, 다중 DB설정도 DBContext별로 어렵지 않게 설정할수 있음을 의미합니다. 


DBContext로부터 Table자동생성

    public class Startup
    {
        ........................
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure( IApplicationBuilder app, IHostingEnvironment env )
        {
            if ( env.IsDevelopment() )
            {
                using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
                {
                    var context = serviceScope.ServiceProvider.GetRequiredService<AccountContent>();
                    context.Database.EnsureDeleted();
                    context.Database.EnsureCreated();
                }
                app.UseDeveloperExceptionPage();               
            }
            app.UseMvc();
        }
    }

DB의 구조가 잡혀있지않아, 초기개발단계에서 사용될수 있는 옵셥으로

DBContent에 포함된 Entity객체들을, 어플리케이션 구동시 리셋후 다시 맞춰줍니다.

DBTable을 SQL문을 통해 생성할 필요가 없기 때문에, 초기 프로토타잎시 활용될수 있습니다.


데이터베이스에 스키마 변경 내용을 증분 방식으로 적용하여 EF Core 모델과의 동기 상태를 유지하면서 데이터베이스에 기존 데이터를 보존하는 방법을 제공하니

아래 URL을 참고합니다.

https://docs.microsoft.com/ko-kr/ef/core/managing-schemas/migrations/



  • No labels