오래된 강의(2017~2019년) 따라서 .NET Core 프로젝트 세팅하다가 만난 에러들 정리. 강의는 EF Core 초기 버전 + .NET Framework 시절 문법이라, 지금(.NET 8, EF Core 8) 기준으로 옮기면서 겪은 문제들을 기록해둔다.
-Context 필수프로젝트에 CodeFirstDbContext, DBFirstDbContext 두 개를 등록해두면, 마이그레이션 명령어를 칠 때마다 아래 에러가 뜬다.
More than one DbContext was found. Specify which one to use.
Use the '-Context' parameter for PowerShell commands...
해결은 간단하다. Add-Migration, Update-Database 어떤 명령어든 -Context를 명시해주면 됨.
Add-Migration AddingUserTable -Context CodeFirstDbContext -Project NetCore.Services
Update-Database -Context CodeFirstDbContext -Project NetCore.Services
-Project NetCore.Migrations로 지정했는데, 실제로는 NetCore.Services에 마이그레이션 폴더가 생기는 현상을 겪었다.
Your target project 'NetCore.Migrations' doesn't match your migrations assembly 'NetCore.Services'.
원인은 Program.cs에서 MigrationsAssembly("NetCore.Migrations")를 지정해도, 프로젝트 안에 IDesignTimeDbContextFactory를 구현한 클래스가 있으면 EF 툴이 Program.cs를 무시하고 그 팩토리를 최우선으로 사용한다는 것.
public class CodeFirstDBContextFactory : IDesignTimeDbContextFactory<CodeFirstDbContext>
{
public CodeFirstDbContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<CodeFirstDbContext>();
optionsBuilder.UseSqlServer(
connectionString,
mig => mig.MigrationsAssembly("NetCore.Migrations")); // 여기 추가해야 함
return new CodeFirstDbContext(optionsBuilder.Options);
}
}
팩토리가 있는 경우엔 팩토리 안의 UseSqlServer 옵션에도 똑같이 MigrationsAssembly를 지정해줘야 원하는 프로젝트로 마이그레이션 파일이 떨어진다. Program.cs 설정만 믿고 있으면 계속 삽질하게 됨.
프로젝트 두 곳에 각각 IDesignTimeDbContextFactory를 구현한 클래스가 있으면, EF 툴이 어느 걸 써야 할지 애매해지면서 계속 엉뚱한 프로젝트를 잡는다. 팩토리는 딱 하나만 유지하는 게 정신건강에 이롭다.
로컬 SQL Server(Express)에 접속할 때마다 나던 에러.
서버에 연결했지만 로그인하는 동안 오류가 발생했습니다.
(provider: SSL 공급자, error: 0 - 신뢰되지 않은 기관에서 인증서 체인을 발급했습니다.)
최근 SQL Server가 기본적으로 암호화 연결을 요구하도록 바뀌었는데, 로컬 개발용 서버엔 정식 SSL 인증서가 없어서 나는 에러다. 연결 문자열에 옵션 하나만 추가하면 해결된다.
Server=localhost\SQLEXPRESS;Database=CodeFirstDB;User Id=coreuser;Password=corepwd;Encrypt=Optional;TrustServerCertificate=True;
SSMS로 접속할 때도 마찬가지로 연결 창에서 "서버 인증서 신뢰" 체크박스를 켜주면 된다.
강의는 Startup.cs에 ConfigureServices / Configure를 나눠 쓰는 옛날 방식인데, .NET 8은 Program.cs 하나로 통합된 minimal hosting 방식을 쓴다. 대응 관계는 이렇게 정리하면 편하다.
| Startup.cs | Program.cs |
|---|---|
ConfigureServices(IServiceCollection services) 안의 코드 | builder.Services.XXX() (builder.Build() 이전) |
Configure(IApplicationBuilder app, ...) 안의 코드 | app.XXX() (builder.Build() 이후) |
Configuration.GetConnectionString(...) | builder.Configuration.GetConnectionString(...) |
강의에서 Startup.cs 코드가 나오면, "이게 서비스 등록이냐 미들웨어 파이프라인이냐"만 구분해서 위/아래 절반으로 나눠 옮기면 됨.
builder.Build() 이후에 builder.Services 건드리면 안 됨무심코 코드 복붙하다가 var app = builder.Build(); 아래에 builder.Services.AddDbContext(...)를 또 넣어서 에러 났었다. Build() 시점 이후로는 서비스 컨테이너가 이미 확정된 상태라, 그 이후엔 절대 builder.Services를 추가로 건드리면 안 된다.
EF Core 3.0부터 FromSql(string sql)이라는 메서드 자체가 사라졌다. 2017~2019년 강의 코드엔 그대로 남아있어서 컴파일 에러가 남.
// 옛날 (지금은 존재하지 않는 메서드)
_context.Users.FromSql("SELECT * FROM User")
// 지금 - 변수 없는 순수 SQL 문자열
_context.Users.FromSqlRaw("SELECT * FROM User")
// 지금 - SQL 안에 변수가 들어가는 경우 (SQL Injection 방지용 파라미터화)
_context.Users.FromSqlInterpolated($"SELECT * FROM User WHERE UserId = {userId}")
이름만 바뀐 게 아니라, Interpolated 계열은 문자열 보간을 자동으로 파라미터화해서 SQL Injection을 막아주는 안전장치가 추가된 거다. 변수가 없으면 Raw, 있으면 Interpolated 쓰면 됨.
복붙하다가 흔히 나는 실수. DBFirstDbContext 클래스인데 생성자 이름이 CodeFirstDbContext로 남아있으면 컴파일러가 "메서드에는 반환 형식이 있어야 합니다"라는, 원인 파악하기 애매한 에러를 던진다.
public class DBFirstDbContext : DbContext
{
// 클래스 이름이랑 다르면 생성자가 아니라 이상한 메서드로 인식됨
public CodeFirstDbContext(DbContextOptions<CodeFirstDbContext> options) : base(options) { } // ❌
public DBFirstDbContext(DbContextOptions<DBFirstDbContext> options) : base(options) { } // ✅
}
CS0060: 일관성 없는 액세스 가능성: 'IUser' 기본 클래스가 'UserService' 클래스보다 액세스하기 어렵습니다.
public class UserService : IUser인데 interface IUser가 public이 아니면 나는 에러. C#은 public 클래스가 구현/상속하는 타입도 최소한 같은 수준으로 공개되어 있어야 한다는 규칙이 있다. interface IUser → public interface IUser로 고치면 해결.

오늘 삽질 요약하면, 결국 대부분 "강의는 5~7년 전 버전, 지금은 최신 버전"이라는 시차에서 오는 문제였다. 에러 메시지를 찬찬히 읽으면 대부분 원인이 메시지 안에 다 적혀있다는 것도 새삼 느낌.
이게 뭥미!! 너무 어려워ㅛ ㅜ
실수로 팔취해버려서 다시 팔로우했땅.ㅋㅋㅎ..
.NET은 참 어려운 것 같애여!!!!