2020. 2. 6. 15:30

DB를 먼저 작성하거나 다른 프로젝트에 의해 생성된 DB를 사용하는 경우 DB 퍼스트(DB First) 방식을 사용합니다.

 

연관글 영역

 

 

프로젝트는 'ASP.NET Core 2'로 구성되어 있습니다.

이 프로젝트는 sqlite를 기준으로 작성되었습니다.

 

 

1. 왜 DB 퍼스트를 사용하는가?

전통적으로 프로젝트가 진행되면 DB를 먼저 만들고 시작했습니다.

예정에는 여기에 직접 쿼리를 날리거나 DBA가 만들어놓은 프로시저를 호출해서 사용했죠.

 

EF(Entity Framework)와 같은 ORM(Object-Relational Mapping) 프레임웍들이 생기면서 모델을 만들어 접근하는 방식을 사용하기 시작합니다.

 

그렇다면!

기존 방식을 사용하는 프로젝트는 EF를 어떻게 연결하느냐?

이미 만들어져있는 DB의 정보를 불러와 컨텍스트(Context)와 모델(Model)을 자동으로 생성해주는 툴을 이용하여 사용하면 됩니다.

 

 

2. 프로젝트 준비

프로젝트 생성 옵션은

닷넷 코어 2.2

 

웹 응용프로그램

 

WebAPI

입니다.

 

 

누겟에서 

 

Microsoft.EntityFrameworkCore.Tools

 

를 찾아 설치합니다.

 

 

DB엔진에 따라 추가로 설치해야 할 프로바인더(provider) 다릅니다.

사용하려는 프로바인더를 찾아 설치합니다.

 

MSSQL : Microsoft.EntityFrameworkCore.SqlServer

MySQL : MySql.Data.EntityFrameworkCore

Oracle : Oracle.EntityFrameworkCore

SQLite : Microsoft.EntityFrameworkCore.Sqlite

 

 

DB를 미리 생성해두어야 하는데......

코드 퍼스트로 생성한 sqlite DB 파일을 복사해서 사용하겠습니다.

참고 : [.NET Core 2] EF(Entity Framework) 코어(Core) 코드 퍼스트(Code First)

 

 

3. DB 커낵션 스트링(Connection String) 만들기

사용하려는 DB에 맞는 커낵션 스트링을 만들어야 합니다.

 

'appsettings.json'파일을 열어 다음과 같이 커낵션 스트링을 추가해줍니다.

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
 
  //"CoreCodeFirst": {
  //  "DBType": "mssql",
  //  "ConnectionString": "Server=[주소];DataBase=[데이터 베이스];UId=[아이디];pwd=[비밀번호]"
  //},
 
  "CoreCodeFirst": {
    "DBType": "sqlite",
    "ConnectionString": "Data Source=DBFile\\CcfTest.sqlite"
  },
 
  "AllowedHosts": "*"
}

 

DB 종류를 구분하기 위해 'DBType'를 추가해 줍니다.

 

 

4. 모델(Model)과 컨텍스트 생성하기

컨텍스트와 모델을 생성하려면 패키지 관리자 콘솔을 열고 아래와 같이 명령어를 날려줍니다.

scaffold-dbcontext "[커낵션 스트링]" [연결된 프로바인더] -OutputDir [출력 폴더]
 
--- sqlite 예제 ---
cd "F:\work\Project_VS\vs2019\EntityFrameworkSample\DBFirst"
scaffold-dbcontext "Data Source=DBFile\\CcfTest.sqlite" Microsoft.EntityFrameworkCore.Sqlite -OutputDir ModelDB

 

cd 명령을 통해 프로젝트 폴더로 이동하지 않는 경우 에러가 나는 경우가 있어서 넣었습니다.

에러가 나지 않는다면 안 하셔도 됩니다.

 

정상적으로 컨텍스트가 생성되었다면 아래와 같이 생성됩니다.

using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
 
namespace DBFirst.ModelDB
{
    public partial class CcfTestContext : DbContext
    {
        public CcfTestContext()
        {
        }
 
        public CcfTestContext(DbContextOptions<CcfTestContext> options)
            : base(options)
        {
        }
 
        public virtual DbSet<TestUser> TestUser { get; set; }
        public virtual DbSet<TestUserInfo> TestUserInfo { get; set; }
 
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings.
                optionsBuilder.UseSqlite("Data Source=DBFile\\\\\\\\CcfTest.sqlite");
            }
        }
 
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.HasAnnotation("ProductVersion", "2.2.6-servicing-10079");
 
            modelBuilder.Entity<TestUser>(entity =>
            {
                entity.HasKey(e => e.IdTestUser);
 
                entity.Property(e => e.IdTestUser)
                    .HasColumnName("idTestUser")
                    .ValueGeneratedNever();
 
                entity.Property(e => e.JoinDate).IsRequired();
            });
 
            modelBuilder.Entity<TestUserInfo>(entity =>
            {
                entity.HasKey(e => e.IdTestUserInfo);
 
                entity.HasIndex(e => e.IdTestUserForeignKey);
 
                entity.Property(e => e.IdTestUserInfo)
                    .HasColumnName("idTestUserInfo")
                    .ValueGeneratedNever();
 
                entity.Property(e => e.IdTestUserForeignKey).HasColumnName("idTestUserForeignKey");
 
                entity.HasOne(d => d.IdTestUserForeignKeyNavigation)
                    .WithMany(p => p.TestUserInfo)
                    .HasForeignKey(d => d.IdTestUserForeignKey);
            });
        }
    }
}

 

'OnConfiguring'함수를 지워줍니다.

 

'ModelDB_partial'폴더를 만들고 'CcfTestContext'클래스를 생성해 줍니다.

그리고 아래와 같이 코드를 넣습니다.

namespace DBFirst.ModelDB
{
    public partial class CcfTestContext : DbContext
    {
        /// <summary>
        /// DB 컨택스트 생성
        /// </summary>
        /// <param name="options"></param>
        protected override void OnConfiguring(DbContextOptionsBuilder options)
        {
            switch (GlobalStatic.DBType)
            {
                case "sqlite":
                    options.UseSqlite(GlobalStatic.DBString);
                    break;
                case "mysql":
                    //options.UseSqlite(GlobalStatic.DBString);
                    break;
 
                case "mssql":
                default:
                    options.UseSqlServer(GlobalStatic.DBString);
                    break;
            }
        }
    }
}

 

모델을 생성할 때 지정한 폴더가 초기화되기 때문에 이렇게 다른 폴더를 만들어 사용합니다,

파샬(partial)을 이용하여 컨택스의 'OnConfiguring'함수를 정의해줍니다.

 

 

5. 테스트

이제 그냥 사용하면 됩니다.

 

'ValuesController.cs'를 열어 'Get()'함수를 아래와 같이 작성합니다.

/// <summary>
/// 모든 리스트를 반환 한다.
/// GET api/values
/// </summary>
/// <returns></returns>
[HttpGet]
public JsonResult Get()
{
    List<TestUser> tuList = new List<TestUser>();
 
    using (CcfTestContext db1 = new CcfTestContext())
    {
        //유저 검색
        tuList
            = db1.TestUser
                .ToList();
    }
 
    return new JsonResult(tuList);
}

 

실행해보면 잘됩니다.

 

 

마무리

완성된 샘플 : Github dang-gun - entityframeworksample/DBFirst/

 

DB퍼스트는 모델 생성하면 할 일의 반은 끝난 겁니다 ㅎㅎㅎ

만약 한 프로젝트에서 코드 퍼스트로 생성된 DB에 연결할 때는 따로 생성을 하지 말고 컨택스트와 모델을 복사하여 사용하면 됩니다.

결국 EF는 컨택스트와 모델만 맞으면 에러 없이 동작합니다 ㅎㅎ