IT TIP

EF6 Code First의 enum에 해당하는 테이블을 만드는 방법은 무엇입니까?

itqueen 2021. 1. 6. 20:37
반응형

EF6 Code First의 enum에 해당하는 테이블을 만드는 방법은 무엇입니까?


EF6의 Code First에서 열거 형을 처리하는 방법에 대해 MSDN따랐습니다 . 예상대로 작동 했지만 열거자를 참조하는 생성 된 테이블의 필드는 간단한 int 입니다.

두 번째 테이블이 생성되는 것을 선호하는데, 그 값은 C # 코드의 열거 자 정의를 따릅니다. 따라서 MSDN의 예에서 Department해당하는 테이블 만 얻는 대신 Faculty 의 항목으로 채워진 두 번째 테이블도보고 싶습니다 .

public enum Faculty { Eng, Math, Eco }     

public partial class Department 
{ 
  [Key] public Guid ID { get; set; } 
  [Required] public Faculty Name { get; set; } 
}

이 문제를 조사하면서 열거 형 테이블을 만들고 시드를 통해 명시 적으로 채우는 솔루션을 발견 했습니다.

저에게는 번거로운 접근 방식과 자동으로 처리되어야하는 많은 작업으로 보입니다. 결국 시스템은 열거를 구성하는 실제 값을 알고 있습니다. DB 관점에서는 내가 생성하는 엔티티와 마찬가지로 여전히 데이터 행이지만 OO 측면에서는 실제로 데이터가 아니라 유한하고 미리 알려진 수의 상태를 가정 할 수있는 유형 (느슨하게 표현 된)입니다.

테이블을 "수동으로"채우는 방법이 권장됩니까?


EF는 자동으로 처리하지 않기 때문에 , 이것이 권장되는 방법입니다.

귀하가 제공 한 기사에서 몇 가지 수정을 제안합니다.

열거 형 이름 바꾸기

public enum FacultyEnum { Eng, Math, Eco }

테이블을 나타내는 클래스 만들기

public class Faculty
{
    private Faculty(FacultyEnum @enum)
    {
        Id = (int)@enum;
        Name = @enum.ToString();
        Description = @enum.GetEnumDescription();
    }

    protected Faculty() { } //For EF

    [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }

    [Required, MaxLength(100)]
    public string Name { get; set; }

    [MaxLength(100)]
    public string Description { get; set; }

    public static implicit operator Faculty(FacultyEnum @enum) => new Faculty(@enum);

    public static implicit operator FacultyEnum(Faculty faculty) => (FacultyEnum)faculty.Id;
}

모델이 클래스를 참조합니다.

public class ExampleClass
{
    public virtual Faculty Faculty { get; set; }
}

열거 형 및 시드 값에서 설명을 가져 오는 확장 메서드 만들기

using System;
using System.ComponentModel;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;

public static class Extensions
{
    public static string GetEnumDescription<TEnum>(this TEnum item)
        => item.GetType()
               .GetField(item.ToString())
               .GetCustomAttributes(typeof(DescriptionAttribute), false)
               .Cast<DescriptionAttribute>()
               .FirstOrDefault()?.Description ?? string.Empty;

    public static void SeedEnumValues<T, TEnum>(this IDbSet<T> dbSet, Func<TEnum, T> converter)
        where T : class => Enum.GetValues(typeof(TEnum))
                               .Cast<object>()
                               .Select(value => converter((TEnum)value))
                               .ToList()
                               .ForEach(instance => dbSet.AddOrUpdate(instance));
}

Configuration.cs에 시드 추가

protected override void Seed(Temp.MyClass context)
{
    context.Facultys.SeedEnumValues<Faculty, FacultyEnum>(@enum => @enum);
    context.SaveChanges();
}

DbContext에 열거 형 테이블 추가

public class MyClass : DbContext
{
    public DbSet<ExampleClass> Examples { get; set; }
    public DbSet<Faculty> Facultys { get; set; }
}

그걸 써

var example = new ExampleClass();
example.Faculty = FacultyEnum.Eng;

if (example.Faculty == FacultyEnum.Math)
{
    //code
}

기억하기 위해

Faculty 속성에 가상을 추가하지 않는 경우 Eager Load를 수행하려면 DbSet의 Include 메서드를 사용해야합니다.

var exampleFromDb = dbContext.Examples.Include(x => x.Faculty).SingleOrDefault(e => e.Id == 1);
if (example.Faculty == FacultyEnum.Math)
{
    //code
}

교수 속성이 가상 인 경우 사용하십시오.

var exampleFromDb = dbContext.Examples.Find(1);
if (example.Faculty == FacultyEnum.Math)
{
    //code
}

@Alberto Monteiro 답변을 기반으로 여러 테이블이있는 경우 일반 클래스를 만들었습니다. 여기서 알림은 Id가 TEnum의 유형이라는 것입니다. 이러한 방식으로 사용하면 속성 유형을 선언하는 데 Enum을 사용할 수있는 옵션이 제공됩니다.

public class Question
{
    public QuestionTypeEnum QuestionTypeId { get; set; } // field property

    public QuestionType QuestionType { get; set; } // navigation property
}

기본적으로 정수를 사용하는 Enum이므로 db 공급자는 "int"유형으로 필드를 만듭니다.

EnumTable.cs

    public class EnumTable<TEnum>
        where TEnum : struct
    {
        public TEnum Id { get; set; }
        public string Name { get; set; }

        protected EnumTable() { }

        public EnumTable(TEnum enumType)
        {
            ExceptionHelpers.ThrowIfNotEnum<TEnum>();

            Id = enumType;
            Name = enumType.ToString();
        }

        public static implicit operator EnumTable<TEnum>(TEnum enumType) => new EnumTable<TEnum>(enumType);
        public static implicit operator TEnum(EnumTable<TEnum> status) => status.Id;
    }

ExceptionHelpers.cs

static class ExceptionHelpers
{
    public static void ThrowIfNotEnum<TEnum>()
        where TEnum : struct
    {
        if (!typeof(TEnum).IsEnum)
        {
            throw new Exception($"Invalid generic method argument of type {typeof(TEnum)}");
        }
    }
}

이제 EnumTable을 상속 할 수 있습니다.

public enum QuestionTypeEnum
{
    Closed = 0,
    Open = 1
}

public class QuestionType : EnumTable<QuestionTypeEnum>
{
    public QuestionType(QuestionTypeEnum enumType) : base(enumType)
    {
    }

    public QuestionType() : base() { } // should excplicitly define for EF!
}

값을 시드

context.QuestionTypes.SeedEnumValues<QuestionType, QuestionTypeEnum>(e => new QuestionType(e));

모델을 더 단순하게 유지하려는 경우 POCO 스타일을 유지하려면 enum을 엔터티 프레임 워크에 의해 정수로 저장 될 속성으로 사용하십시오.

그런 다음 DB에서 "열거 형 테이블"을 만들고 업데이트하려면 nuget 패키지 https://github.com/timabell/ef-enum-to-lookup 을 사용하고 EF 마이그레이션 시드에서 사용하는 것이 좋습니다. 예를 들어 방법 :

public enum Shape
{
    Square,
    Round
}

public class Foo
{
    public int Id { get; set; }
    public Shape Shape { get; set; }
}

public class MyDbContext : DbContext
{
    public DbSet<Foo> Foos { get; set; }
}

using(var context = new MyDbContext())
{
    var enumToLookup = new EnumToLookup
    {
        TableNamePrefix = string.Empty,
        NameFieldLength = 50,
        UseTransaction = true
    };
    enumToLookup.Apply(context);
}

그러면 "Foo"테이블에 관련 외래 키 제약 조건이있는 Square 및 Round라는 2 개의 행이있는 "Shape"테이블이 생성됩니다.


선언 : byte앞에 추가해야합니다 enum.

enum MyFieldEnum : byte{
    one = 1,
    two = 2,
    three = 4
}

데이터베이스에서 볼 수 있어야 TINYINT하며 캐스팅 할 필요가 없습니다!


@AlbertoMonterio 훌륭합니다! 이 작업을 ASP.NET CORE / EF Core와 함께 사용하기 위해 Alberto의 솔루션을 몇 가지 조정했습니다.

간결하게하기 위해 수정 사항 만 아래에 표시됩니다.

열거 형 및 시드 값에서 설명을 가져 오는 확장 메서드 만들기

using System;
using System.ComponentModel;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
using Microsoft.EntityFrameworkCore; //added
using Microsoft.EntityFrameworkCore.Metadata.Builders; //added

public static class Extensions
{
    //unchanged from alberto answer
    public static string GetEnumDescription<TEnum>(this TEnum item)
        => item.GetType()
               .GetField(item.ToString())
               .GetCustomAttributes(typeof(DescriptionAttribute), false)
               .Cast<DescriptionAttribute>()
               .FirstOrDefault()?.Description ?? string.Empty;

    //changed
    public static void SeedEnumValues<T, TEnum>(this ModelBuilder mb, Func<TEnum, T> converter)
    where T : class => Enum.GetValues(typeof(TEnum))
                           .Cast<object>()
                           .Select(value => converter((TEnum)value))
                           .ToList()
                            .ForEach(instance => mb.Entity<T>().HasData(instance));
}

Configuration.cs에 시드 추가

OnModelCreatingDataContext에 시드 추가

protected override void OnModelCreating(ModelBuilder builder)
{
    builder.SeedEnumValues<Faculty, EnumEntityRole>(e => e);
}

EF Core에서 작동하고 더 간단하게 느껴지는 또 다른 접근 방식 :

귀하의 열거 형

public enum Color
{
    Red = 1,
    Blue = 2,
    Green = 3,
}

DB 테이블

public class CustomObjectDto
{
    public int ID { get; set; }

    // ... other props

    public Color ColorID { get; set; }
    public ColorDto ColorDto { get; set; }
}

public class ColorDto
{
    public Color ID { get; set; }
    public string Name { get; set; }
}

귀하의 DbContext

public class Db : DbContext
{
    public Db(DbContextOptions<Db> options) : base(options) { }

    public DbSet<CustomObjectDto> CustomObjects { get; set; }
    public DbSet<ColorDto> Colors { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Seed database with all Colors
        foreach (Color color in Enum.GetValues(typeof(Color)).Cast<Color>())
        {
            ColorDto colorDto = new ColorDto
            {
                ID = color,
                Name = color.ToString(),
            };

            modelBuilder.Entity<ColorDto>().HasData(colorDto);
        }
    }
}

코드에서는 기본적으로 열거 형 Color 만 사용합니다 (ColorDto 아님). 그러나 SQL 쿼리 및 뷰에 대한 'CustomObjects'테이블에 FK가있는 'Colors'테이블이있는 것은 여전히 ​​좋습니다.


Alberto Monteiro는 이에 대해 매우 잘 대답했습니다. EF 코어와 함께 작동하도록 몇 가지 조정을해야했습니다.

열거 형 이름을 바꾸고 설명 데코레이터 추가

public enum FacultyEnum 
{
    [Description("English Professor")]
    Eng, 
    [Description("Math Professor")]
    Math, 
    [Description("Economics Professor")]
    Eco 
}

테이블을 나타내는 클래스 만들기

public class Faculty
{
    private Faculty(FacultyEnum @enum)
    {
        Id = (int)@enum;
        Name = @enum.ToString();
        Description = @enum.GetEnumDescription();
    }

    protected Faculty() { } //For EF

    [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }

    [Required, MaxLength(100)]
    public string Name { get; set; }

    [MaxLength(100)]
    public string Description { get; set; }

    public static implicit operator Faculty(FacultyEnum @enum) => new Faculty(@enum);

    public static implicit operator FacultyEnum(Faculty faculty) => (FacultyEnum)faculty.Id;
}

모델이 클래스를 참조합니다.

public class ExampleClass
{
    public virtual Faculty Faculty { get; set; }
}

열거 형 및 시드 값에서 설명을 가져 오는 확장 메서드 만들기

using System;
using System.ComponentModel;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;

public static class Extensions
{
    public static string GetEnumDescription<TEnum>(this TEnum item)
        => item.GetType()
               .GetField(item.ToString())
               .GetCustomAttributes(typeof(DescriptionAttribute), false)
               .Cast<DescriptionAttribute>()
               .FirstOrDefault()?.Description ?? string.Empty;
}

YourDbContext.cs에 시드 추가

protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Faculty>().HasData(FacultyEnum.Eng, FacultyEnum.Math, FacultyEnum.Eco);
    }

DbContext에 열거 형 테이블 추가

public class MyClass : DbContext
{
    public DbSet<ExampleClass> Examples { get; set; }
    public DbSet<Faculty> Facultys { get; set; }
}

그걸 써

var example = new ExampleClass();
example.Faculty = FacultyEnum.Eng;

if (example.Faculty == FacultyEnum.Math)
{
    //code
}

기억하기 위해

Faculty 속성에 가상을 추가하지 않는 경우 Eager Load를 수행하려면 DbSet의 Include 메서드를 사용해야합니다.

var exampleFromDb = dbContext.Examples.Include(x => x.Faculty).SingleOrDefault(e => e.Id == 1);
if (example.Faculty == FacultyEnum.Math)
{
    //code
}

If Faculty property is virtual, then just use it

var exampleFromDb = dbContext.Examples.Find(1);
if (example.Faculty == FacultyEnum.Math)
{
    //code
}

ReferenceURL : https://stackoverflow.com/questions/34557574/how-to-create-a-table-corresponding-to-enum-in-ef6-code-first

반응형