출처 : https://code-maze.com/configuring-nonrelational-properties/
EF Core는 entity model을 만들기 위해 conventions, annotation attributes, Fluent API의 combination을 사용한다.
1. By Convention
property type과 name에 따라서 database를 구성한다.
테이블 이름은 DbContext class의 DbSet<T> property 이름과 동일하다고 여겨진다.
컬럼이름은 entity model class의 properties이름과 동일하다고 여겨진다.
public class Student
{
public Guid StudentId { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
string .NET type은 nvarchat type이다.
int .NET type은 int type이다.
primary key는 Id 또는 ID 또는 classname뒤에 Id, ID가 붙는 property로 여겨진다. 만약에 이 property가 integer type 또는 Guid type이면 IDENTITY column으로 여겨진다.
property name이 'Id'이거나, '클래스명 + Id' 인 경우 database에서 PK로 전환된다.
위 class에서는 StudentId가 PK가 된다. class에 복합키가 있으면 Convention방식으로 구성이 안되고 Data Annotations이나 Fluent API를 사용해야 한다.
public class ApplicationContext : DbContext
{
public ApplicationContext(DbContextOptions options)
:base(options)
{
}
public DbSet<Student> Students { get; set; }
}
migration을 실행하면 아래와 같은 migration 파일이 생성된다.
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace TEST.Migrations
{
public partial class test : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Students",
schema: "dbo",
columns: table => new
{
StudentId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Name = table.Column<string>(type: "nvarchar(max)", nullable: false),
Age = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Students", x => x.StudentId);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Students",
schema: "dbo");
}
}
}
update-database를 하면 아래와 같이 테이블이 생성된다.
property type에 따라 컬럼의 nullability가 결정된다. Name은 string이고 기본값 null이므로 Null이다. Age는 value type이므로 NOT NULL이다.
? suffix를 사용해서 int? Age나, Nullable<T> generic를 사용해서 Nullable<int> Age로 수정하면 Age도 nullable이 될 수 있다.
2. Data Annotations
Convention으로 부족할 때가 있다.
Data Annotations는 db features를 구성하거나 validate하기 위해서 사용하는 .NET attribute이다.
아래 namespaces가 사용된다.
System.ComponentModel.DataAnnotations : property 유효성
Sustem.ComponentModel.DataAnnotations.Schema : db 구성
using System.ComponentModel.DataAnnotations;
[Table("Student")]
public class Student
{
[Column("StudentId")]
public Guid Id { get; set; }
[Required]
[MaxLength(50, ErrorMessage = "Length must be less then 50 characters")]
public string Name { get; set; }
public int? Age { get; set; }
}
Required attribute를 붙이면 Name field는 nullable이 될 수 없다. MaxLength를 사용하면 컬럼 길이 제한이 생긴다.
흔히 쓰이는 attributes는 다음과 같다.
Attribute | 설명 |
[Required] | NOT NULL |
[StringLength(50)] | 최대 50자 |
[RegularExpression(expression)] | 특정 정규식에 일치함 |
[Column(TypeName=”money”, Name=”UnitPrice”)] | column type, name을 지정함 |
Table attribute
ApplicationContext class의 DbSet<T> property name에 따라 db table name이 정해진다.
하지만 [Table] attribute는 property name을 override할 수 있다.
non-default schema를 사용하면 [Table("TableName", Schema="SchemaName")] attribute를 사용할 수 있다.
Column attribute
Order, Database Type 을 Column attribute와 함께 제공할 수 있다.
[Column("ColumnName", Order = 1, TypeName="nvarchar(50)")]
해당 attribute를 적용한 class로 migration을 실행해보자.
3. Fluent API
Fluent API는 ModelBuilder class에서 제공하는 함수로, context class의 OnModelCreating 함수에서 사용이 가능하다.
entity model class를 단순하게 유지할 수 있다. 또 다른 장점은, 기초 데이터를 제공할 수 있다.
Data Annotation으로 설정한 것과 동일하게 구성해보자.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>()
.ToTable("Student");
modelBuilder.Entity<Student>()
.Property(s => s.Id)
.HasColumnName("StudentId");
modelBuilder.Entity<Student>()
.Property(s => s.Name)
.IsRequired()
.HasMaxLength(50);
modelBuilder.Entity<Student>()
.Property(s => s.Age)
.IsRequired(false);
}
매핑 시, Entities or Classes 제외
- Data Annotations
public class Student
{
[Column("StudentId")]
public Guid Id { get; set; }
[Required]
[MaxLength(50, ErrorMessage = "Length must be less then 50 characters")]
public string Name { get; set; }
public int? Age { get; set; }
[NotMapped]
public int LocalCalculation { get; set; }
}
[NotMapped]
public class NotMappedClass
{
//properties
}
- Fluent API
modelBuilder.Entity<Student>()
.Ignore(s => s.LocalCalculation);
modelBuilder.Ignore<NotMappedClass>();
PrimaryKey Configuration
property name이 naming convention에 맞지 않으면 PK가 생성되지 않는다. 이럴때는 아래의 방법으로 PK를 지정한다.
- Data Annotations
public class Student
{
[Key]
[Column("StudentId")]
public Guid Id { get; set; }
[Required]
[MaxLength(50, ErrorMessage = "Length must be less then 50 characters")]
public string Name { get; set; }
public int? Age { get; set; }
[NotMapped]
public int LocalCalculation { get; set; }
}
- Fluent API
modelBuilder.Entity<Student>()
.HasKey(s => s.Id);
Composite PrimaryKey Configuration
복합키는 Fluent API로만 구성할 수 있다.
[Table("Student")]
public class Student
{
[Column("StudentId")]
public Guid Id { get; set; }
public Guid AnotherKeyProperty { get; set; }
[Required]
[MaxLength(50, ErrorMessage = "Length must be less then 50 characters")]
public string Name { get; set; }
public int? Age { get; set; }
[NotMapped]
public int LocalCalculation { get; set; }
}
AppDbContext.cs
modelBuilder.Entity<Student>()
.HasKey(s => new { s.Id, s.AnotherKeyProperty });
Indexes and Default Values
Fluent API를 통해서 테이블에 인덱스를 추가할 수 있다.
modelBuilder.Entity<Student>()
.HasIndex(s => s.PropertyName);
//다중 인덱스
modelBuilder.Entity<Student>()
.HasIndex(s => new { s.PropertyName1, s.PropertyName2 });
//인덱스명 설정
modelBuilder.Entity<Student>()
.HasIndex(s => s.PropertyName)
.HasName("index_name");
//유니크 제약조건
modelBuilder.Entity<Student>()
.HasIndex(s => s.PropertyName)
.HasName("index_name")
.IsUnique();
row 생성 시, 세팅될 기본값을 설정할 수 있다.
public bool IsRegularStudent { get; set; }
modelBuilder.Entity<Student>()
.Property(s => s.IsRegularStudent)
.HasDefaultValue(true);
Configuring preconvention models
EF Core 6에 configuring preconvention models 기능이 추가되었다. model이 복잡해지면서, conventions에만 의지하여 table에 맞는 model을 정의하기가 어려워졌다. model을 만들기 전에, conventions을 미리 구성해놓으면 유용하다.
예를들어, 모든 string properties는 반드시, 기본으로 최대 50자 길이를 갖고, 또는 custom interface를 구현하는 property type은 map하지 않는다고 미리 정의할 수 있다.
protected override void ConfigureConventions(
ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Properties<string>().HaveMaxLength(50);
configurationBuilder.IgnoreAny<IDoNotMap>();
}
'Entity Framework Core' 카테고리의 다른 글
Entity Framework Core] .AsNoTracking()이란? (0) | 2023.06.09 |
---|---|
ASP.NET Core] Entity Framework Core - Migrations (0) | 2023.05.24 |
ASP.NET Core] Entity Framework Core란? (1) | 2023.05.23 |
ASP.NET Core] Entity Framework Core - SQL Queries (0) | 2023.05.09 |
ASP.NET Core] Entity Framework Core - 2. One To Many (0) | 2022.02.08 |
댓글