sqlite Model Class ์„ค๊ณ„ ๋ฌธ์˜ ..!

image

์•ˆ๋…•ํ•˜์„ธ์š”. DB๊ด€๋ จ ๋ฌธ์˜๊ฐ€ ์žˆ์–ด ์งˆ๋ฌธ ๊ธ€ ์˜ฌ๋ฆฝ๋‹ˆ๋‹ค !

Sqlite, EntityFramework, CodeFirst ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค
Model ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•  ๋•Œ ์ด๋ก ์ ์œผ๋กœ ์ž˜ ์ดํ•ด๊ฐ€ ๊ฐ€์งˆ ์•Š์•„ ๋ฌธ์˜ ๋“œ๋ ค์š”

  1. ParentModel ์ƒ์„ฑ์‹œ์— Id๋ฅผ ๊ธฐ์ค€์œผ๋กœ ChildA, ChildB๊ฐ€ ๊ฐ™์ด ์ด์–ด์ง€๊ฒŒ ํ•˜๋Š” ๋ฐฉ๋ฒ•? ์ดํ•ด ? ์ด๋ก ์ ์œผ๋กœ ์ž˜ ์ดํ•ด๋ฅผ ๋ชปํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  2. ๋งŒ์•ฝ์— ParentModel ์˜ ChildA์•ˆ์˜ Listํ˜•์‹์˜ ์ž์‹์ด ๋˜ ์กด์žฌ ํ•œ๋‹ค๋ฉด ? Id๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์–ด๋–ป๊ฒŒ ์ด์–ด์ฃผ๋Š”๊ฒƒ ์ธ์ง€ โ€ฆ

๊ฐ€๋ฅด์นจ ๋ถ€ํƒ๋“œ๋ ค์šฉ

3๊ฐœ์˜ ์ข‹์•„์š”

์งˆ๋ฌธ์ด ์ž˜ ์ดํ•ด๊ฐ€ ๋˜์ง€๋ฅผ ์•Š์Šต๋‹ˆ๋‹ค. ๋จผ์ € ์˜๋„ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ์ข€ ๋” ์„ค๋ช…ํ•ด์ฃผ์‹œ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.
์•„๋ž˜ ๋‚ด์šฉ์€ ์ œ๊ฐ€ ์งˆ๋ฌธ์„ ์™„์ „ํžˆ ์ดํ•ดํ•˜์ง€ ๋ชปํ•œ ์ƒํƒœ์—์„œ ๋‹ต๋ณ€์„ ๋“œ๋ฆฌ๋Š” ๊ฒƒ์ด๋‹ˆ ์ฐธ๊ณ ๋งŒ ํ•ด์ฃผ์‹œ๊ณ , ๊ฐ€๋Šฅํ•˜์‹œ๋ฉด ์ข€ ๋” ์˜๋„(๊ฒฐ๊ณผ) ๊ด€์ ์—์„œ ๋ถ€์—ฐ ์„ค๋ช…์„ ํ•ด์ฃผ์‹œ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

image

์œ„์˜ ERD๋Š” ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.

EF์—์„œ๋Š” ํƒ์ƒ‰ ์†์„ฑ์ด ํ‘œํ˜„๋˜๋ฉด ๊ด€๊ณ„๋ฅผ ์œ„์˜ ERD์ฒ˜๋Ÿผ ๋งบ์–ด ์ฃผ๋Š”๋ฐ์š”,

ChildAs, ChildBs๋Š” ์ปฌ๋ ‰์…˜ ํƒ์ƒ‰ ์†์„ฑ, ParentDataModel์€ ์ฐธ์กฐ ํƒ์ƒ‰ ์†์„ฑ

ParentDataModel๋Š” ์ฃผ ์—”ํ„ฐํ‹ฐ๊ฐ€ ๋˜๊ณ  ChildA, ChildB, ChildAa๋Š” ์ข…์† ์—”ํ„ฐํ‹ฐ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

๋ณ„๋‹ค๋ฅธ ํ‚ค๋ฅผ ํŠน์„ฑ์œผ๋กœ ํ‘œํ˜„ํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด Id๋‚˜ [Class์ด๋ฆ„]Id์˜ ์ด๋ฆ„์œผ๋กœ ํ‚ค ์†์„ฑ์„ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํƒ์ƒ‰ ์†์„ฑ์— ์˜ํ•ด ์—”ํ„ฐํ‹ฐ ๊ฐ„์˜ ๊ด€๊ณ„๊ฐ€ ํ˜•์„ฑ๋ฉ๋‹ˆ๋‹ค.

ChildA์™€ ChildB๊ฐ€ ๊ฐ™์ด ์ด์–ด์ง€๊ฒŒ ํ‘œํ˜„ํ•˜๋Š”๊ฒƒ์„ ์˜๋ฏธํ•˜๋‚˜์š”? ๊ทธ๋ ‡๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ํƒ์ƒ‰ ์†์„ฑ์œผ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

public class ChildA : IEntity
{
    ....
    public virtual ChildB ChildB { get; set; }
}

public class ChildB : IEntity
{
    ...
    public virtual ChildA ChildA { get; set; }
}

๊ทธ๋ฆฌ๊ณ  ํƒ์ƒ‰ ์†์„ฑ์„ ํ†ตํ•ด ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

var info = context.ParentDataModels.Where(x => x.Id = 10);
foreach (var childA in info.ChildAs) { ... }
foreach (var childB in info.ChildBs) { ... }

ํƒ์ƒ‰ ์†์„ฑ์— ์˜ํ•ด์„œ ๊ด€๊ณ„๋ฅผ ๋”ฐ๋ผ๊ฐˆ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ด๋ ‡๊ฒŒ ์งˆ์˜๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

var result = context.ParentDataModels.Where(x => x.Id = 10).ChildAs
    .First()
    .ChildB;

Listํ˜•์‹์˜ ์ž์‹์ด ์ œ ๊ด€์ ์—์„œ๋Š” ๋งž์ง€ ์•Š๋Š” ํ‘œํ˜„ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ž์‹์ด ์•„๋‹ˆ๋ผ ์—”ํ„ฐํ‹ฐ ๊ด€๊ณ„์ƒ์˜ ์ฃผ/์ข… ๊ด€๊ณ„์ผ ๋ฟ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด ์‚ฌ์šฉ์ž์ •๋ณด๊ฐ€ ์žˆ๊ณ  ์‚ฌ์šฉ์ž๋กœ๊ทธ์ธ์ด๋ ฅ์ด ์žˆ๋‹ค๋ฉด ์‚ฌ์šฉ์ž์ •๋ณด๋Š” ์ฃผ ์—”ํ„ฐํ‹ฐ์ด๊ณ  ์‚ฌ์šฉ์ž๋กœ๊ทธ์ธ์ด๋ ฅ์€ ์ข… ์—”ํ„ฐํ‹ฐ ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์„ ํƒ์ƒ‰ ์†์„ฑ์œผ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒ ๊ณ ์š”, ์‚ฌ์šฉ์ž๋กœ๊ทธ์ธ์ด๋ ฅ์€ ์‚ฌ์šฉ์ž์ •๋ณด ๊ด€์ ์—์„œ ์ผ๋Œ€๋‹ค์ด๋ฏ€๋กœ ๋ชฉ๋ก์œผ๋กœ ํ‘œํ˜„๋ฉ๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ๊ด€๊ณ„๊ฐ€ ํ˜•์„ฑ๋˜์—ˆ๋‹ค๋Š” ์˜๋ฏธ๋Š” ๊ด€๊ณ„๊ฐ€ ํ˜•์„๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ์ด๊ณ  ๊ทธ ๊ด€๊ณ„๋Š” ID๋กœ ์‹๋ณ„ํ•˜๋ฏ€๋กœโ€ฆ ์ฃผ ์—”ํ„ฐํ‹ฐ ID๋กœ ์ด์–ด์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

3๊ฐœ์˜ ์ข‹์•„์š”

๋ถ€์—ฐํ•ด์„œ ์œ„์˜ ์ฝ”๋“œ๋Š” ํ…Œ์ŠคํŠธํ•˜์ง€ ์•Š๋Š” ์ฝ”๋“œ๋กœ ์ฐธ๊ณ ๋งŒ ํ•˜์…จ์œผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค. ์—”ํ„ฐํ‹ฐ์˜ ํƒ์ƒ‰ ์†์„ฑ์œผ๋กœ ๊ด€๊ณ„๋ฅผ ์–ด๋–ป๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€๋ฅผ ์ค‘์ ์œผ๋กœ ์‚ดํŽด๋ณด์‹œ๋ฉด ์ข€ ๋” ๋น ๋ฅด๊ฒŒ ์ดํ•ดํ•˜์‹ค ์ˆ˜ ์žˆ์ง€ ์•Š์„๊นŒ ์ƒ๊ฐํ•ด๋ด…๋‹ˆ๋‹ค.

2๊ฐœ์˜ ์ข‹์•„์š”

efcore๋Š” ์—”ํ‹ฐํ‹ฐ์˜ ์ƒํƒœ๋ฅผ ๋ณด๊ณ  ์ถ”๊ฐ€ํ• ์ง€ ์—…๋ฐ์ดํŠธํ• ์ง€๋ฅผ ํŒ๋‹จํ•ฉ๋‹ˆ๋‹ค.
Parent๋ฅผ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜์…จ๊ณ , Child์˜ Collection๋„ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜์…จ์œผ๋ฉด ๋‘˜๋‹ค insert ๊ฐ€ ๋ฐœ์ƒ ๋ฉ๋‹ˆ๋‹ค.
Query๋ฅผ ํ†ตํ•ด ์กฐํšŒ๋œ ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ Child์˜ ํŠน์ • index์˜ ์†์„ฑ์„ ๋ณ€๊ฒฝํ•˜์…จ์œผ๋ฉด ์—…๋ฐ์ดํŠธ๊ฐ€ ๋ฐœ์ƒ ๋˜์‹ค ๊ฒƒ ๊ฐ™์•„์š”.

๊ทธ๋‚˜์ €๋‚˜ ๋ชจ๋ธ์— ๊ธฐ๋ณธํ‚ค ํŠน์„ฑ์ด ์—†์–ด์„œ Id ๊ฐ’์— ์ œ๋Œ€๋กœ ๋งคํ•‘์ด ์•ˆ๋  ๊ฒƒ ๊ฐ™์€๋ฐ DbContext ์ชฝ์— ์„ ์–ธํ•˜์…จ๋‚˜ ๋ชจ๋ฅด๊ฒ ๋„ค์š”.

3๊ฐœ์˜ ์ข‹์•„์š”

image

์ œ๊ฐ€ ์›ํ•˜๋˜ ๊ทธ๋ฆผ์„ ์ตœ๋Œ€ํ•œ ์ด๋ฏธ์ง€๋กœ ํ‘œ์‹œํ•ด๋ดค์Šต๋‹ˆ๋‹ค,

์•„์ง๋„ ๋จธ๋ฆฌ์— ๋”ฑ ํ•˜๊ณ  ์ดํ•ด๊ฐ€ ๋˜์งˆ ์•Š๋„ค์š”,

  1. ๊ณ ๊ฐ, ์ฃผ๋ฌธ, ์ƒ์„ธ ์ฃผ๋ฌธ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ๊ฐœ๋ณ„ ๋ชฉํ‘œ๋Š” ์ด๋ ‡์Šต๋‹ˆ๋‹ค.
    2-1. ๊ณ ๊ฐ์€ ์—ฌ๋Ÿฌ๊ฐœ์˜ ์ฃผ๋ฌธ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.
    2-2. ์—ฌ๋Ÿฌ๊ฐœ์˜ ์ฃผ๋ฌธ ์—๋Š” ์—ฌ๋Ÿฌ๊ฐœ์˜ ์ƒ์„ธ์ฃผ๋ฌธ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  3. ์ด์™€ ๊ฐ™์Œ์œผ๋กœ, ๊ณ ๊ฐ Class ์•ˆ์— List Orders๋ฅผ ๊ฐ€์ง€๊ณ  Order class ์•ˆ์— List OrderDetails ๋ฅผ ๋„ฃ๊ณ ์‹ถ์€๋ฐ์š”,
    id๋ฅผ ์ด์–ด์ฃผ๋Š” ๋ฐฉ๋ฒ•(?)์„ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค
3๊ฐœ์˜ ์ข‹์•„์š”

์ œ๊ฐ€ ์™ธ๋ถ€์— ์žˆ์–ด์„œ ์ €๋… ๋Šฆ๊ฒŒ ๊ด€๋ จ๋œ ์ƒ˜ํ”Œ์„ ๋งŒ๋“ค์–ด ๊ณต์œ ํ•ด๋ณผ๊ป˜์š”

1๊ฐœ์˜ ์ข‹์•„์š”

์•ˆ๋…•ํ•˜์„ธ์š”?
Customer : Orders = 1 : n
Orders : OrderDetails = 1 : n ์„ ์›ํ•˜์‹œ๋Š” ๊ฒƒ์œผ๋กœ ์ดํ•ดํ–ˆ๋Š”๋ฐ์š”,

์—ฌ๊ธฐ์— ๋ณด๋ฉด ์ผ๋Œ€ ๋‹ค ๊ด€๊ณ„์— ๋Œ€ํ•œ Entity Class ์ •์˜ ๋ฐฉ๋ฒ•์˜ ์˜ˆ์‹œ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š”๋ฐ์š”,
์ด ๋ถ€๋ถ„์„ ์ฐธ๊ณ ํ•ด์„œ ์ผ๋Œ€ ๋‹ค ๊ด€๊ณ„๋ฅผ ์ค‘์ฒฉํ•ด์„œ ๊ตฌ์„ฑํ•ด๋ณด์‹œ๋Š” ๊ฒƒ๋„ ๋‚˜์˜์ง€ ์•Š์•„ ๋ณด์ž…๋‹ˆ๋‹ค.

Annotation / Attribute ๋ฅผ ํ™œ์šฉํ•œ ๊ตฌ์„ฑ๋„ ์žˆ์ง€๋งŒ
์ข€ ๋ณต์žกํ•œ ๊ตฌ์„ฑ์„ ์›ํ•˜์‹ ๋‹ค๋ฉด FluentAPI๋ฅผ ์“ฐ์…”์•ผ ํ•  ๊ฒƒ ๊ฐ™์•„์š”.

๊ทธ๋ฆฌ๊ณ  ๋ณดํ†ต Parent์˜ ID๋งŒ ์ฐธ์กฐํ•˜๊ณ 
GrandParent์˜ ID๋Š” ์ฐธ์กฐ ์•ˆ ํ•˜์ง€ ์•Š๋‚˜์š”?;;

4๊ฐœ์˜ ์ข‹์•„์š”

๊ทธ๋ ‡๊ตฐ์š”, GrandParent์˜ ID๋Š” ์ฐธ์กฐ๋ฅผ ์ž˜ ์•ˆํ•˜๋Š”์ง€ ๋ชฐ๋ž์—ˆ์Šต๋‹ˆ๋‹ค.
ํ˜น์—ฌ๋‚˜ id๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ฐœ๋ณ„๋กœ ์ฐพ์•„์•ผํ•  ๊ฒฝ์šฐ๋ฅผ ์ƒ๊ฐํ•ด์„œ ์›ํ–ˆ๋˜ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
๊ด€๋ จ ๋‚ด์šฉ๋ณด๊ณ  ํ•œ๋ฒˆ ๋”ฐ๋ผํ•ด๋ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค,
๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค -.

3๊ฐœ์˜ ์ข‹์•„์š”

์•„์˜ˆ ํ‹€๋ฆฐ ์•„์ด๋””์–ด๋Š” ์•„๋‹ˆ๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.
SQL์„ ๊ธฐ์ค€์œผ๋กœ ์ƒ๊ฐํ•˜๋ฉด ๋Œ€๊ฐœ join์œผ๋กœ ํ•ด๊ฒฐํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์„ ๋ฟ์ด์—์š”.

์‰ฝ๊ฒŒ ์“ฐ๊ธฐ ์œ„ํ•ด์„œ GrandParentID , ParentID๋ฅผ ๋‘˜ ๋‹ค ๋ณ‘๊ธฐ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๊ธฐ๋„ ํ•˜๊ฒ ๋„ค์š”
์ด๋Ÿฐ ๊ฒฝ์šฐ์—” ๋” ์ด์ƒ์€ ์˜๋ฏธ๋กœ๋Š” ์กฐ๋ถ€๋ชจID๊ฐ€ ์•„๋‹ˆ๊ฒŒ ๋˜๊ฒ ์ง€์š”.

์‚ฌ์šฉ ํ•  ๋•Œ ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ์ด ๋” ํšจ์œจ์ ์ธ์ง€๋Š” ์„ค๊ณ„๋„ ํ•ด๋ณด๊ธฐ๋„ ํ•˜๊ณ , ํ”„๋กœํŒŒ์ผ๋ง๋„ ํ•˜๊ณ  ๊ทธ๋Ÿฝ๋‹ˆ๋‹ค. ์ œ ์–•์€ ์‹ค๋ฌด ๊ฒฝํ—˜์—์„œ๋„์š” ^^;

2๊ฐœ์˜ ์ข‹์•„์š”
  1. ParentModel ์ƒ์„ฑ์‹œ์— Id๋ฅผ ๊ธฐ์ค€์œผ๋กœ ChildA, ChildB๊ฐ€ ๊ฐ™์ด ์ด์–ด์ง€๊ฒŒ ํ•˜๋Š” ๋ฐฉ๋ฒ•? ์ดํ•ด ? ์ด๋ก ์ ์œผ๋กœ ์ž˜ ์ดํ•ด๋ฅผ ๋ชปํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • @suwoo ๋‹˜์ด ๋ง์”€ํ•ด์ฃผ์‹  ๊ฒƒ์ฒ˜๋Ÿผ Id ์†์„ฑ์— Key ํŠน์„ฑ์„ ์ ์šฉํ•˜์‹œ๊ฑฐ๋‚˜ DbContext ์ƒ์˜ FluentAPI๋กœ ํ•ด๊ฒฐ ํ•ด์•ผ ๋  ๊ฒƒ ๊ฐ™์•„์š”.
  • Id๊ฐ€ ์ž๋™ ์ƒ์„ฑ ๋˜๊ธธ ๋ฐ”๋ผ์‹ ๋‹ค๋ฉด [DatabaseGenerated(DatabaseGeneratedOption.Identity)] ํŠน์„ฑ์„ ์ ์šฉํ•˜์‹œ๊ฑฐ๋‚˜ DbContext ์ƒ์—์„œ ValueGeneratedOnAdd ํ•จ์ˆ˜๋กœ ๋งคํ•‘ํ•ด์ค˜์•ผ ๋˜๊ฒ ๋„ค์š”.
  1. ๋งŒ์•ฝ์— ParentModel ์˜ ChildA์•ˆ์˜ Listํ˜•์‹์˜ ์ž์‹์ด ๋˜ ์กด์žฌ ํ•œ๋‹ค๋ฉด ? Id๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์–ด๋–ป๊ฒŒ ์ด์–ด์ฃผ๋Š”๊ฒƒ ์ธ์ง€ โ€ฆ
  • ๊ธ€ ๋‚ด์šฉ ์ค‘์— GrandParent ์— ๋Œ€ํ•œ ์–ธ๊ธ‰์ด ์—†์–ด์„œ ์–ด๋–ค ์˜๋ฏธ์ธ์ง€ ์ž˜ ์ดํ•ดํ•˜์ง€๋Š” ๋ชปํ–ˆ๋Š”๋ฐ์š”. ใ… ใ…  ๊ถ๊ธˆํ•˜์‹  ๋ถ€๋ถ„์ด ์‹œ์ ์ƒ ์ฟผ๋ฆฌํ• ๋•Œ๋ฅผ ์˜๋ฏธ ํ•˜๋Š” ๊ฒƒ์ธ์ง€ ์•„๋‹ˆ๋ฉด ์ถ”๊ฐ€๋‚˜ ์—…๋ฐ์ดํŠธ ํ•˜๋Š” ์‹œ์ ์„ ๋ง์”€ํ•˜์‹œ๋Š” ๊ฒƒ์ธ์ง€์— ๋”ฐ๋ผ ๋‹ค๋ฅผ ๊ฒƒ ๊ฐ™์•„์š”.
  • ์ผ๋ฐ˜์ ์œผ๋กœ๋Š” GrandParent Id๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๋งŽ์ง€ ์•Š์ฃ ~
4๊ฐœ์˜ ์ข‹์•„์š”

์•„, Doc์˜ ๋งํฌ๋ฅผ ๋ง๋ถ™์ด๋ ค๊ณ  ๋ง๊ธ€์„ ์“ฐ๋‹ค๋ณด๋‹ˆ ์ด๋ ‡๊ฒŒ ๋˜์–ด๋ฒ„๋ ธ๋„ค์š” :pray:
์˜๋„๋งŒ ๋†“๊ณ  ๋ณด์ž๋ฉด ์šธ๋‹˜์ด ์ ์–ด์ฃผ์‹  ๋Œ“๊ธ€์— ๋ง๋ถ™์ด๋ ค๋‹ค ์ด๋Ÿฐ๊ฒŒ ๋œ ๊ฑฐ๊ตฌ์š” ใ… ใ… 

hask๋‹˜์ด ๋ฐ‘์— diagram์„ ๋ง๋ถ™์—ฌ ๊ทธ๋ ค์ฃผ์‹ ๊ฑธ ๋ณด๋‹ˆ
1:n์ด ๋‘๋ฒˆ ์ค‘์ฒฉ๋œ ๊ฒƒ์œผ๋กœ ๋ณด์—ฌ์„œ ๊ทธ๋ ‡๊ฒŒ ๋‚ด์šฉ์ด ์„ ํšŒ๋œ ๊ฒƒ์ž…๋‹ˆ๋‹คใ…Žใ…Ž

๊ทธ ์™ธ์—”, ์ œ๊ฐ€ ๋ง์”€๋“œ๋ฆฌ๊ณ  ์‹ถ์€๋ถ€๋ถ„๊ณผ ๊ฑฐ์˜ ์ผ์น˜ํ•˜๋Š” ๊ฒƒ ๊ฐ™๋„ค์š”.

3๊ฐœ์˜ ์ข‹์•„์š”

์ฒ˜์Œ ๊ธ€์—์„œ ์„ธ ๊ฐ€์ง€ Child์˜ ID๊ฐ€ ์ „๋ถ€ ๋˜‘๊ฐ™์ด Id๊ฐ€ ๋˜์–ด๋ฒ„๋ ค์„œ ๋”์šฑ ํ—ท๊ฐˆ๋ฆฌ๊ฒŒ ๋œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
OrderDetails Entity๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ณธ๋‹ค๋ฉด
๋งŒ์•ฝ ๋‘˜ ๋‹ค ์กฐํšŒ๊ฐ€ ํ•„์š”ํ•˜์‹œ๋‹ค๊ณ  ํ–ˆ์œผ๋‹ˆ,

ID
CustomerID
OrdersID
์„ธ๊ฐœ๊ฐ€ ๋“ค์–ด๊ฐ€๊ฒŒ ๋˜๊ฒ ์ง€์š”.
๊ทธ๋ ‡๋‹ค๊ณ  ํ•ด์„œ Customer Entity๊ฐ€ ICollection<OrderDetails> ๋ฅผ ๊ฐ–๋Š”๋‹ค๋Š” ๋ณด์žฅ์€ ํ•  ์ˆ˜ ์—†๊ฒ ๋„ค์š”.

2๊ฐœ์˜ ์ข‹์•„์š”
public class Entity
{
    [Key]
    [Column(Order = 0)]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get;set; }
}

public class Customer : Entity
{
    public virtual ICollection<Order> Orders { get; set; } = new List<Order>();
}

public class Order : Entity
{
    [Required]
    public int CustomerId { get; set; }

    [Required]
    [ForeignKey(nameof(CustomerId))]
    public Customer Customer { get; set; }

    public virtual ICollection<OrderDetail> Details { get; set; } = new List<OrderDetail>();
}

public class OrderDetail : Entity
{
    [Required]
    public int OrderId { get; set; }

    [Required]
    [ForeignKey(nameof(OrderId))]
    public Order Order { get; set; }
}

์ด๋Ÿฐ ๊ตฌ์กฐ์ผ๊นŒ์š”?

4๊ฐœ์˜ ์ข‹์•„์š”

์•„ ๋„ค๋„ค ๋งž๋Š”๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค,
ํ—Œ๋ฐ TEST ํ•ด๋ณด์•˜๋Š”๋ฐ ๊ด€๊ณ„๊ตฌ์„ฑ์ด ์•ˆ๋˜๊ณ  ์žˆ๋Š”๋ฐ ์ œ๊ฐ€ ๋นผ๋จน์€ ๋ถ€๋ถ„์ด ์žˆ๋Š”๊ฑธ๊นŒ์š” ?
Code์™€ ๋™์ผํ•˜๊ฒŒ ๊ตฌ์„ฑํ•˜์—ฌ DB์ƒ์„ฑ๊นŒ์ง€ ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

2๊ฐœ์˜ ์ข‹์•„์š”
// See https://aka.ms/new-console-template for more information

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;
using Microsoft.EntityFrameworkCore;

await using var context = new CustomerContext();
var customer = new Customer { Name = "hoya" };
var product = new Product { Customer = customer, Name = "p1" };
var jobDetail = new JobDetail { Product = product, Name = "j1"};

product.JobDetails.Add(jobDetail);
customer.Products.Add(product);
context.Add(customer);
await context.SaveChangesAsync().ConfigureAwait(false);


public class Entity
{
    [Key]
    [Column(Order = 0)]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get;set; }
}

public class Customer : Entity
{
    [Required]
    public string Name { get; set; }
    public virtual ICollection<Product> Products { get; set; } = new List<Product>();
}

public class Product : Entity
{
    [Required]
    public int CustomerId { get; set; }

    [Required]
    [ForeignKey(nameof(CustomerId))]
    public Customer Customer { get; set; }

    [Required]
    public string Name { get; set; }

    public virtual ICollection<JobDetail> JobDetails { get; set; } = new List<JobDetail>();
}

public class JobDetail : Entity
{
    [Required]
    public int ProductId { get; set; }

    [Required]
    public string Name { get; set; }

    [Required]
    [ForeignKey(nameof(ProductId))]
    public Product Product { get; set; }
}

public class CustomerContext : DbContext
{
    public DbSet<Customer> Customers { get; set; }
    public DbSet<Product> Products { get; set; }
    public DbSet<JobDetail> JobDetails { get; set; }

    public string DbPath { get; }

    public CustomerContext()
    {
        var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        DbPath = System.IO.Path.Join(path, "customer.db");
    }

    // The following configures EF to create a Sqlite database file in the
    // special "local" folder for your platform.
    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseSqlite($"Data Source={DbPath}");
}

์ž˜ ๋˜๋Š”๊ฑฐ ๊ฐ™์€๋ฐ ์–ด๋–ค ์ ์ด ๋‹ค๋ฅผ๊นŒ์š”?
sqlite ์ƒ์—์„œ foreign key๋„ ์žกํ˜€ ์žˆ๋„ค์š”.
์ถ”๊ฐ€ ํ•˜์‹ค ๋•Œ parent๊ฐ€ ๋˜๋Š” Customer๋‚˜ Product์— ์ธ์Šคํ„ด์Šค๊ฐ€ ์„ค์ • ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ด ๋ณด์‹œ๋Š” ๊ฒƒ๋„ ์ข‹์„ ๊ฒƒ ๊ฐ™๋„ค์š”.

image

5๊ฐœ์˜ ์ข‹์•„์š”

์•„ ๋„ค๋„ค๋„ค ! ์•„์ฃผ ์ž˜๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
instance ์„ค์ •์ด์•ˆ๋˜์–ด ์žˆ์–ด์„œ ์•ˆ๋“ค์–ด๊ฐ”์—ˆ์Šต๋‹ˆ๋‹ค !! ใ…Žใ…Ž
๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์ถ”๊ฐ€์ ์œผ๋กœ ๊ถ๊ธˆํ•œ๊ฒƒ์ด ์žˆ๋Š”๋ฐ
ModelConfiguration ์—์„œ ๊ด€๊ณ„๋ฅผ ํ˜•์„ฑ ์ง“๋Š” ๋ฐฉ๋ฒ•์ด ๋”ฐ๋กœ ์—†๋Š”๋“ฏ ํ•ด์„œ
๋ง‰์—ฐํ•˜๊ฒŒ ๋”ฐ๋ผํ•˜๊ธด ํ–ˆ์ง€๋งŒ ์กฐ๊ธˆ ๋” ์„ธ๋ถ€์ ์œผ๋กœ ๋‹ค์‹œ ํ•œ๋ฒˆ ์•Œ๋ ค์ฃผ์‹ค ์ˆ˜ ์žˆ์„๊นŒ์š” โ€ฆ?
ํŠนํžˆ ์ดํ•ด์•ˆ๊ฐ€๋Š” ๋ถ€๋ถ„์ด ์ด์ „์— 1:N ๋ฐฉ์‹์„ ํ–ˆ์„๋•Œ๋Š”
[Key]
[Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[ForeignKey(nameof(ProductId))]
์š”๋Ÿฐ ๋ฌธ๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์‚ฌ์šฉํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค.
์ œ ๋ˆˆ์น˜๋กœ๋Š” ์ € attribute ๊ฐ€ ๊ด€๊ณ„ ํ˜•์„ฑ์„ ์ด์–ด์ฃผ๋Š”๊ฒƒ ๊ฐ™์€๋ฐ ์•„๋‹๊นŒ์š”โ€ฆ?

๋งˆ์ง€๋ง‰์œผ๋กœ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค !! ์ž˜๋ฉ๋‹ˆ๋‹ค ! ใ…Žใ…Ž

5๊ฐœ์˜ ์ข‹์•„์š”

์ข‹์€ ์• ํ‹ฐํŠœ๋“œ์— ๋” ๋งŽ์ด ์•Œ๋ ค๋“œ๋ฆฌ๊ณ  ์‹ถ์€ ๋งˆ์Œ์ด ๋ฟœ๋ฟœํ•˜๋„ค์š”. :slightly_smiling_face:

๊ฐ ํŠน์„ฑ์˜ ์˜๋ฏธ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. Column ํŠน์„ฑ ๋ฐ Order ์†์„ฑ
    ์ปฌ๋Ÿผ ์ƒ์„ฑ ์ˆœ์„œ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

  2. Key ํŠน์„ฑ + DatabaseGenerated ํŠน์„ฑ

  • Key ํŠน์„ฑ์€ ๊ธฐ๋ณธํ‚ค๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
  • DatabaseGenerated๋Š” Row ์ฒ˜๋ฆฌ ๋ฐฉ์‹์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
  • intํ˜• ์†์„ฑ์— Key ํŠน์„ฑ์„ ์ฃผ๊ณ  DatabaseGeneratedOption.Identity๋ฅผ ์ ์šฉํ–ˆ์œผ๋‹ˆ AUTO INCREMENT๊ฐ€ ๋œ๋‹ค๊ณ  ๋ณด์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.
  • ๊ทธ ์™ธ์—๋„ DateTimeํ˜• ์†์„ฑ์— ์ค„์ˆ˜๋„ ์žˆ๊ณ , DatabaseGeneratedOption.Computed ๊ฐ’์œผ๋กœ ๋‹ค๋ฅธ ํ˜•ํƒœ์˜ ์‹œ๋‚˜๋ฆฌ์˜ค๋„ ์ ์šฉํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋Œ“๊ธ€๋กœ ๋ชจ๋‘ ์•Œ๋ ค๋“œ๋ฆฌ๊ธฐ๋Š” ์–ด๋ ค์›Œ ๊ด€๋ จ ์‹œ๋‚˜๋ฆฌ์˜ค ๋ฐœ์ƒ์‹œ ๊ด€๋ จ ๋ฌธ์„œ๋ฅผ ์ฐพ์•„ ๋ณด์‹œ๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™๋„ค์š”.
  1. ForeignKey ํŠน์„ฑ
  • ์ด ํŠน์„ฑ์ด 1:N์„ ๋งŒ๋“œ๋Š” ํŠน์„ฑ์ž…๋‹ˆ๋‹ค.
  • Parent ํด๋ž˜์Šค์— Child ํด๋ž˜์Šค์˜ ICollection ์†์„ฑ์„ ์„ ์–ธํ•˜๊ณ , Child ํด๋ž˜์Šค์— Parent ์†์„ฑ์„ ์„ ์–ธํ•˜์‹œ๋ฉด ๊ธฐ๋ณธ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.
    ๊ทธ๋ž˜์„œ ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ๋ณด์‹œ๋ฉด, Customer ํด๋ž˜์Šค์— ICollection Products ์†์„ฑ์ด ์žˆ๊ณ , Product ํด๋ž˜์Šค์—๋Š” Customer ์†์„ฑ์ด ์„ ์–ธ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
public class Customer : Entity
{
    [Required]
    public string Name { get; set; }
    public virtual ICollection<Product> Products { get; set; } = new List<Product>();
}

public class Product : Entity
{
    [Required]
    public int CustomerId { get; set; }

    [Required]
    [ForeignKey(nameof(CustomerId))]
    public Customer Customer { get; set; }

    [Required]
    public string Name { get; set; }

    public virtual ICollection<JobDetail> JobDetails { get; set; } = new List<JobDetail>();
}

๊ฒฐ๋ก ์€ 1,2๋ฒˆ์€ ๊ด€๊ณ„ ์„ค์ •๊ณผ ์—ฐ๊ด€์ด ์—†๊ณ , 3๋ฒˆ์ด ๊ด€๊ณ„ ์„ค์ •๊ณผ ์—ฐ๊ด€์ด ์žˆ์Šต๋‹ˆ๋‹ค.
์•„ ๊ทธ๋ฆฌ๊ณ  ํŠน์„ฑ์„ ์„ ์–ธํ•˜๋Š” ๊ฒƒ ์™ธ์— @suwoo ๋‹˜์ด ์–ธ๊ธ‰ํ•˜์‹  ๊ฒƒ์ฒ˜๋Ÿผ ModelConfiguration ์—์„œ FluentAPI๋กœ ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

builder.HasMany(c => c.Comments)
   .WithOne(c => c.Ticket)
   .OnDelete(DeleteBehavior.Cascade); // cascade

builder
   .HasOne(c => c.User)
   .WithMany(c => c.Notices)
   .HasForeignKey(x => x.UserId)
   .OnDelete(DeleteBehavior.Cascade); // cascade

์ด๋Ÿฐ์‹์œผ๋กœ์š”.

5๊ฐœ์˜ ์ข‹์•„์š”

๋„ค๋„ค ! ForeignKey์— ๋Œ€ํ•ด ์ด์ œ ์กฐ๊ธˆ ์•Œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค!! ์„ธ์„ธํžˆ ์„ค๋ช…ํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค !ใ…Žใ…Ž

KeyํŠน์„ฑ, DatabaseGenerated ์„ ์ฐพ์•„์„œ ๋‹ค์‹œํ•œ๋ฒˆ ํ•™์Šตํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค

์ถ”๊ฐ€์ ์œผ๋กœ, (๊ณ„์†์ƒ๊ธฐ๊ฒŒ ๋˜๋„ค์š”, ํ—ˆํ—ˆ )
์ƒ์„ฑ์‹œํ‚ฌ ๋•Œ๋Š” ์ •์ƒ์ ์œผ๋กœ ์ƒ์„ฑ์ด ์ž˜ ๋˜๊ณ ์žˆ์Šต๋‹ˆ๋‹ค.
ํ—Œ๋ฐ ์‚ญ์ œ [Products ์™€ JobDetails์˜ ์š”์†Œ์ค‘ ํ•œ๊ฐœ] ์‹œํ‚ค๋ ค๊ณ  ํ•˜๋‹ˆ
DbSet.FInd(item) ํ–‰์œ„๋ฅผ ํ•˜๋‹ˆ ์ฐพ์ง€๋ฅผ ๋ชปํ•˜๋”๋ผ๊ตฌ์š”
์ด๋ถ€๋ถ„์—์„œ ์‹ค์ˆ˜ํ•œ๊ฒŒ ์žˆ์„๊นŒ์š”?

3๊ฐœ์˜ ์ข‹์•„์š”

image
Find์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ์„ค๋ช…์„ ๋ณด์‹œ๋ฉด key ๊ฐ’ ์ „๋‹ฌ์ด ํ•„์š”ํ•œ ๊ฑธ ์•Œ์ˆ˜ ์žˆ๋Š”๋ฐ์š”.
์Šค์ƒท์ฒ˜๋Ÿผ ํŠน์ • ๋„๋ฉ”์ธ DbSet์˜ Id๋ฅผ ๋„˜๊ฒจ์ฃผ์…”์•ผ ์ฐพ์•„์งˆ๊ฒ๋‹ˆ๋‹ค.
Products DbSet์ด๋ฉด, Product์˜ Id๋‹ˆ๊นŒ 1,2,3์™€ ๊ฐ™์€ ์ˆซ์ž๊ฐ€ ๋˜๊ฒ ๋„ค์š”.

3๊ฐœ์˜ ์ข‹์•„์š”

image

image

์š”๋ ‡๊ฒŒ ํ•˜๊ณ ์žˆ์Šต๋‹ˆ๋‹ค.

image
Find ๋ฏธ์‹คํ–‰ ํ•˜๊ณ  Remove๋งŒ ํ• ์‹œ์—๋Š” ์ •์ƒ๋™์ž‘ ํ•ฉ๋‹ˆ๋‹ค..!

2๊ฐœ์˜ ์ข‹์•„์š”