이 연습에서는 Entity Framework의 Code First 마이그레이션에 대해 간략하게 설명합니다. 전체 연습을 완료하거나 관심 있는 항목으로 바로 건너뛸 수 있습니다. 다음과 같은 항목에 대해 설명합니다.
- 마이그레이션 설정
- 마이그레이션 생성 및 실행
- 마이그레이션 사용자 지정
- 데이터 동작 및 사용자 지정 SQL
- 특정 버전으로 마이그레이션(다운그레이드 포함)
- SQL 스크립트 생성
- 응용 프로그램 시작 시 자동 업그레이드(MigrateDatabaseToLatestVersion 이니셜라이저)
초기 모델 및 데이터베이스 빌드
마이그레이션을 사용하려면 먼저 작업할 프로젝트와 Code First 모델이 필요합니다. 이 연습에서는 정식 Blog 및 Post 모델을 사용합니다.
- 새 MigrationsDemo 콘솔 응용 프로그램을 만듭니다.
- 프로젝트에 최신 버전의 EntityFramework NuGet 패키지를 추가합니다.
- 도구 -> 라이브러리 패키지 관리자 -> 패키지 관리자 콘솔
- Install-Package EntityFramework 명령을 실행합니다.
- 아래에 표시된 코드의 Model.cs 파일을 추가합니다. 이 코드는 도메인 모델을 구성하는 단일 Blog 클래스와 EF Code First 컨텍스트인 BlogContext 클래스를 정의합니다.
using System.Data.Entity;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity.Infrastructure;
namespace MigrationsDemo
{
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
}
}
- 이제 생성된 모델을 사용하여 데이터 액세스를 수행해 보겠습니다. 아래에 표시된 코드로 Program.cs 파일을 업데이트합니다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MigrationsDemo
{
class Program
{
static void Main(string[] args)
{
using (var db = new BlogContext())
{
db.Blogs.Add(new Blog { Name = "Another Blog " });
db.SaveChanges();
foreach (var blog in db.Blogs)
{
Console.WriteLine(blog.Name);
}
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
- 응용 프로그램을 실행하면 MigrationsCodeDemo.BlogContext 데이터베이스가 생성됩니다.
SQL Express(Visual Studio 2010에 포함)가 설치된 경우 로컬 SQL Express 인스턴스(.\SQLEXPRESS)에 데이터베이스가 생성됩니다. SQL Express가 설치되지 않은 경우 Code First는 Visual Studio 2012에 포함된 LocalDb((localdb)\v11.0)를 사용합니다.
참고: Visual Studio 2012를 사용할 경우에도 SQL Express가 설치되어 있다면 항상 SQL Express가 우선 사용됩니다.
(LocaDb 데이터베이스)
(SQL Express 데이터베이스)
마이그레이션 설정
이제 모델에 몇 가지 추가 변경 사항을 만들어 보겠습니다.
- Blog 클래스에 Url 속성을 추가하겠습니다.
public string Url { get; set; }
다시 응용 프로그램을 실행하면 데이터베이스 생성 후 'BlogContext' 컨텍스트의 기본 모델이 변경되었습니다. Code First 마이그레이션을 사용하여 데이터베이스를 업데이트하십시오( http://go.microsoft.com/fwlink/?LinkId=238269).라는 메시지와 함께 InvalidOperationException이 발생합니다.
이 예외 메시지에 따라 Code First 마이그레이션을 사용해 보겠습니다. 첫 번째 단계는 컨텍스트에 대한 마이그레이션을 설정합니다.
- 패키지 관리자 콘솔에서 Enable-Migrations 명령을 실행합니다.
이 명령은 프로젝트에 Migrations 폴더를 추가하며, 이 새 폴더에는 다음 두 파일이 들어 있습니다.
- 구성 클래스입니다. 이 클래스를 사용하여 컨텍스트에 따라 마이그레이션이 작동되는 방식을 구성할 수 있습니다. 이 연습에서는 기본 구성을 사용하겠습니다.
프로젝트에 Code First 컨텍스트는 하나만 있기 때문에 이 구성이 적용되는 컨텍스트 형식에서 자동으로 Enable-Migrations가 작성됩니다. - InitialCreate 마이그레이션입니다. 마이그레이션을 설정하기 전에 Code First를 사용하여 데이터베이스를 만들었기 때문에 이 마이그레이션이 생성되었습니다. 스캐폴드된 마이그레이션의 코드는 데이터베이스에서 이미 생성된 개체를 나타냅니다. 이 예에서는 BlogId 및 Name 열을 가진 Blog 테이블입니다. 파일 이름에는 정렬을 위해 타임스탬프가 포함됩니다.
데이터베이스가 이미 생성되어 있지 않으면 이 InitialCreate 마이그레이션이 프로젝트에 추가되지 않습니다. 대신 처음으로 Add-Migration을 호출할 때 이 테이블을 만드는 코드가 새 마이그레이션으로 스캐폴드됩니다.
마이그레이션 생성 및 실행
Code First 마이그레이션의 두 가지 기본 명령에 대해 살펴보겠습니다.
- Add-Migration은 마지막 마이그레이션이 생성된 후 모델에 적용한 변경 사항을 바탕으로 다음 마이그레이션을 스캐폴드합니다.
- Update-Database는 보류 중인 마이그레이션을 데이터베이스에 적용합니다.
새로 추가한 Url 속성을 반영하기 위해 마이그레이션을 스캐폴드해야 합니다. Add-Migration 명령은 마이그레이션에 이름을 지정할 수 있으므로 AddBlogUrl이라고 이름을 지정하겠습니다.
- 패키지 관리자 콘솔에서 Add-Migration AddBlogUrl 명령을 실행합니다.
- Migrations 폴더에 이제 새 AddBlogUrl 마이그레이션이 생겼습니다. 마이그레이션 파일 이름은 정렬을 위해 타임스탬프로 사전 지정됩니다.
namespace MigrationsDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddBlogUrl : DbMigration
{
public override void Up()
{
AddColumn("Blogs", "Url", c => c.String());
}
public override void Down()
{
DropColumn("Blogs", "Url");
}
}
}
이제 이 마이그레이션을 편집하거나 항목을 추가할 수 있지만 현재로서는 수정할 것이 없습니다. Update-Database를 사용하여 이 마이그레이션을 데이터베이스에 적용하겠습니다.
- 패키지 관리자 콘솔에서 Update-Database 명령을 실행합니다.
- Code First 마이그레이션에서는 데이터베이스에 적용된 마이그레이션과 Migrations 폴더의 마이그레이션을 비교합니다. AddBlogUrl 마이그레이션을 적용해야 함을 인식하고 실행합니다.
이제 MigrationsDemo.BlogContext 데이터베이스가 업데이트되어 Blogs 테이블에 Url 열이 포함됩니다.
마이그레이션 사용자 지정
지금까지 마이그레이션을 생성하고 변경 사항 없이 그대로 실행해 보았습니다. 이제 기본적으로 생성되는 코드를 편집해 보겠습니다.
- 모델에 몇 가지 변경 사항을 적용하기 위해 Blog 클래스에 새 Rating 속성을 추가해 보겠습니다.
public int Rating { get; set; }
- 또한 새 Post 클래스를 추가하겠습니다.
public class Post
{
public int PostId { get; set; }
[MaxLength(200)]
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
- 또한 Blog 클래스에 Posts 컬렉션을 추가하여 Blog 및 Post 간 관계의 다른 End를 만듭니다.
public virtual List<Post> Posts { get; set; }
Add-Migration 명령을 사용하여 Code First 마이그레이션이 스캐폴드를 통해 필요한 마이그레이션을 결정하게 합니다. 이 마이그레이션을 AddPostClass라고 부르겠습니다.
- 패키지 관리자 콘솔에서 Add-Migration AddPostClass 명령을 실행합니다.
Code First 마이그레이션은 이러한 변경 사항에 대해 스캐폴드하지만 다음과 같은 몇 가지 사항을 변경해야 할 수 있습니다.
- 먼저 Posts.Title 열에 고유 인덱스를 추가하겠습니다.
(아래 코드의 22행 및 29행에 추가) - 또한 null을 허용하지 않는 Blogs.Rating 열을 추가합니다. 테이블에 기존 데이터가 있을 경우 새 열에 대한 데이터 형식의 CLR 기본값(Rating은 정수이므로 기본값은 0이 됨)을 할당합니다. 단, 이 경우 Blogs 테이블의 기존 행에 Rating 기본값으로 3을 지정하려고 합니다.
(아래 코드의 24행에 지정된 기본값을 확인할 수 있음)
namespace MigrationsCodeDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddPostClass : DbMigration
{
public override void Up()
{
CreateTable(
"Posts",
c => new
{
PostId = c.Int(nullable: false, identity: true),
Title = c.String(maxLength: 200),
Content = c.String(),
BlogId = c.Int(nullable: false),
})
.PrimaryKey(t => t.PostId)
.ForeignKey("Blogs", t => t.BlogId, cascadeDelete: true)
.Index(t => t.BlogId)
.Index(p => p.Title, unique: true);
AddColumn("Blogs", "Rating", c => c.Int(nullable: false, defaultValue: 3));
}
public override void Down()
{
DropIndex("Posts", new[] { "Title" });
DropIndex("Posts", new[] { "BlogId" });
DropForeignKey("Posts", "BlogId", "Blogs");
DropColumn("Blogs", "Rating");
DropTable("Posts");
}
}
}
마이그레이션 편집이 완료되었으므로 이제 Update-Database를 사용하여 데이터베이스를 최신 상태로 업데이트하겠습니다. 이번에는 Code First 마이그레이션을 실행하는 SQL을 볼 수 있도록 –Verbose 플래그를 지정하겠습니다.
- 패키지 관리자 콘솔에서 Update-Database –Verbose 명령을 실행합니다.
데이터 동작/사용자 지정 SQL
지금까지는 데이터를 변경 또는 이동하지 않는 마이그레이션 작업에 대해 살펴보았으며, 이제 데이터를 이동해야 하는 마이그레이션에 대해 알아보겠습니다. 데이터 동작에 대한 기본 지원은 없지만 스크립트에서 몇 가지 임의의 SQL 명령을 실행할 수 있습니다.
- 모델에 Post.Abstract 속성을 추가하겠습니다. 나중에 Content 열에서 첫 일부 텍스트를 사용하여 기존 Post의 Abstract를 미리 채울 것입니다.
public string Abstract { get; set; }
Add-Migration 명령을 사용하여 Code First 마이그레이션이 스캐폴드를 통해 필요한 마이그레이션을 결정하게 합니다.
- 패키지 관리자 콘솔에서 Add-Migration AddPostAbstract 명령을 실행합니다.
- 생성된 마이그레이션은 스키마 변경 사항을 반영하지만 이와 함께 각 Post의 첫 100자를 사용하여 Abstract 열을 미리 채우려고 합니다. 이를 위해 열이 추가된 후 SQL을 열고 UPDATE 문을 실행합니다.
(아래 코드의 12행에 추가됨)
namespace MigrationsCodeDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddPostAbstract : DbMigration
{
public override void Up()
{
AddColumn("Posts", "Abstract", c => c.String());
Sql("UPDATE Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL");
}
public override void Down()
{
DropColumn("Posts", "Abstract");
}
}
}
마이그레이션 편집이 완료되었으므로 이제 Update-Database를 사용하여 데이터베이스를 최신 상태로 업데이트하겠습니다. –Verbose 플래그를 지정하여 데이터베이스에 대해 SQL이 실행되는 것을 볼 수 있습니다.
- 패키지 관리자 콘솔에서 Update-Database –Verbose 명령을 실행합니다.
특정 버전으로 마이그레이션(다운그레이드 포함)
지금까지는 항상 최신 마이그레이션으로 업그레이드했지만 경우에 따라 특정 마이그레이션으로 업그레이드/다운그레이드해야 할 수 있습니다.
AddBlogUrl 마이그레이션을 실행한 후의 상태로 데이터베이스를 마이그레이션해 보겠습니다. –TargetMigration 스위치를 사용하여 이 마이그레이션으로 다운그레이드할 수 있습니다.
- 패키지 관리자 콘솔에서 Update-Database –TargetMigration: AddBlogUrl 명령을 실행합니다.
이 명령은 AddBlogAbstract 및 AddPostClass 마이그레이션에 대한 Down 스크립트를 실행합니다.
빈 데이터베이스로 롤백하려면 Update-Database –TargetMigration: $InitialDatabase 명령을 사용할 수 있습니다.
SQL 스크립트 가져오기
다른 개발자가 로컬 컴퓨터에 이 변경 사항을 적용하려는 경우 한 번만 동기화하면 시스템에서 자동으로 소스 변경 사항을 확인합니다. 새 마이그레이션을 적용한 후에는 Update-Database 명령만 실행하여 로컬에 변경 사항을 적용할 수 있습니다. 그러나 이 변경 사항을 다른 테스트 서버나 프로덕션 환경에 적용하려면 DBA가 사용할 SQL 스크립트가 필요합니다.
- 이를 위해 –Script 플래그를 지정하여 Update-Database 명령을 실행하면 데이터베이스에 변경 사항을 적용하는 대신 스크립트로 작성합니다. 또한 스크립트를 생성할 마이그레이션의 소스와 대상을 지정할 수 있습니다. 빈 데이터베이스($InitialDatabase)에서 최신 버전(AddPostAbstract 마이그레이션)으로 마이그레이션하는 스크립트를 만들겠습니다.
대상 마이그레이션을 지정하지 않으면 최신 마이그레이션이 대상이 되고, 소스 마이그레이션을 지정하지 않으면 데이터베이스의 현재 상태를 사용합니다. - 패키지 관리자 콘솔에서 Update-Database -Script -SourceMigration: $InitialDatabase -TargetMigration: AddPostAbstract 명령을 실행합니다.
Code First 마이그레이션은 마이그레이션 파이프라인을 실행하지만 실제로 변경 내용을 적용하는 대신 .sql 파일에 변경 사항을 기록합니다. 스크립트가 생성되면 Visual Studio에서 스크립트를 열어서 보거나 저장할 수 있습니다.
응용 프로그램 시작 시 자동 업그레이드(MigrateDatabaseToLatestVersion 이니셜라이저)
응용 프로그램을 배포할 경우 응용 프로그램을 시작할 때 보류 중인 마이그레이션을 적용하여 데이터베이스를 자동 업그레이드하도록 설정할 수 있습니다. 그러려면 MigrateDatabaseToLatestVersion 데이터베이스 이니셜라이저를 등록합니다. 데이터베이스 이니셜라이저에는 데이터베이스가 제대로 설치되었는지 확인하는 데 사용되는 몇 가지 논리가 포함됩니다. 이 논리는 응용 프로그램 프로세스(AppDomain) 내에서 처음으로 컨텍스트가 사용될 때 실행됩니다.
컨텍스트를 사용하기 전에 BlogContext용 MigrateDatabaseToLatestVersion 이니셜라이저를 설정하기 위해 아래에서와 같이 Program.cs 파일을 업데이트할 수 있습니다(14행). 또한 System.Data.Entity 네임스페이스를 위해 using 문을 추가해야 합니다(5행).
이 이니셜라이저의 인스턴스를 만들 때 컨텍스트 형식(BlogContext) 및 마이그레이션 구성(Configuration)을 지정해야 합니다. 마이그레이션 구성은 마이그레이션을 설정할 때 Migrations 폴더에 추가된 클래스입니다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using MigrationsDemo.Migrations;
namespace MigrationsDemo
{
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<BlogContext, Configuration>());
using (var db = new BlogContext())
{
db.Blogs.Add(new Blog { Name = "Another Blog " });
db.SaveChanges();
foreach (var blog in db.Blogs)
{
Console.WriteLine(blog.Name);
}
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
이제 응용 프로그램이 실행될 때마다 먼저 대상 데이터베이스가 최신 상태인지 확인하여 최신 상태가 아니면 보류 중인 마이그레이션을 적용합니다.
요약
이 연습에서는 데이터베이스를 업그레이드 및 다운그레이드하기 위해 코드 기반 마이그레이션을 스캐폴드하고 편집하며 실행하는 방법을 알아보았습니다. 또한 데이터베이스에 마이그레이션을 적용하기 위해 SQL 스크립트를 만들고, 응용 프로그램 시작 시 보류 중인 마이그레이션을 자동 적용하는 방법도 알아보았습니다.
'C# > Winform' 카테고리의 다른 글
(WCF) 서비스 서버단에서 접속한 Client IP 가져오기 (0) | 2017.04.13 |
---|---|
(WCF) IIS vs WinForm 속도 비교 (0) | 2017.04.13 |
(Entity) Entity Framework 소스 코드 (0) | 2017.04.13 |
(.NET) 갑자기 exe 파일이 실행되지 않을때 처리 하기 (fixexec) Info (0) | 2015.06.10 |
(.NET) winform 과 console 창을 같이 띄우기 (0) | 2015.06.10 |