Kon*_*ten 55 c# enums entity-framework ef-code-first entity-framework-6
我已经跟踪MSDN如何处理EF6的Code First中的枚举.它起作用,但是在创建的表中引用枚举器的字段是一个简单的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; }
}
Run Code Online (Sandbox Code Playgroud)
在研究这个问题时,我偶然发现了一个解决方案,它建议为枚举创建一个表并通过种子显式填充它.
在我看来,这是一种繁琐的方法和许多应该自动处理的工作.毕竟,系统知道构成枚举的实际值.从数据库的角度来看,它仍然是数据行,就像我创建的实体一样,但从OO方面来说,它实际上并不是一个数据 - 而是一种类型(松散表达),它可以假设一个有限的和预先知道的状态数.
是否建议"手动"填充表格的方法?
Alb*_*iro 97
由于EF不会自动处理,是的,这是推荐的方法.
我建议您在文章中进行一些修改.
public enum FacultyEnum { Eng, Math, Eco }
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
public class ExampleClass
{
public virtual Faculty Faculty { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
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));
}
Run Code Online (Sandbox Code Playgroud)
protected override void Seed(Temp.MyClass context)
{
context.Facultys.SeedEnumValues<Faculty, FacultyEnum>(@enum => @enum);
context.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)
public class MyClass : DbContext
{
public DbSet<ExampleClass> Examples { get; set; }
public DbSet<Faculty> Facultys { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
var example = new ExampleClass();
example.Faculty = FacultyEnum.Eng;
if (example.Faculty == FacultyEnum.Math)
{
//code
}
Run Code Online (Sandbox Code Playgroud)
如果未在Faculty属性中添加虚拟,则必须使用DbSet中的Include方法执行Eager Load
var exampleFromDb = dbContext.Examples.Include(x => x.Faculty).SingleOrDefault(e => e.Id == 1);
if (example.Faculty == FacultyEnum.Math)
{
//code
}
Run Code Online (Sandbox Code Playgroud)
如果Faculty属性是虚拟的,那么只需使用它
var exampleFromDb = dbContext.Examples.Find(1);
if (example.Faculty == FacultyEnum.Math)
{
//code
}
Run Code Online (Sandbox Code Playgroud)
uns*_*Ptr 10
基于@Alberto Monteiro回答我已经创建了泛型类,以防你有几个表.这里的通知是Id是TEnum的类型.以这种方式使用它将提供使用Enum声明属性类型的选项.
public class Question
{
public QuestionTypeEnum QuestionTypeId { get; set; } // field property
public QuestionType QuestionType { get; set; } // navigation property
}
Run Code Online (Sandbox Code Playgroud)
默认情况下,枚举使用整数,因此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;
}
Run Code Online (Sandbox Code Playgroud)
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)}");
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在您可以继承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!
}
Run Code Online (Sandbox Code Playgroud)
种子化的价值观
context.QuestionTypes.SeedEnumValues<QuestionType, QuestionTypeEnum>(e => new QuestionType(e));
Run Code Online (Sandbox Code Playgroud)
另一种在 EF Core 中有效(并且对我来说感觉更简单)的方法:
public enum Color
{
Red = 1,
Blue = 2,
Green = 3,
}
Run Code Online (Sandbox Code Playgroud)
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; }
}
Run Code Online (Sandbox Code Playgroud)
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
在代码中我基本上只使用枚举 Color(从不使用 ColorDto)。但是,在“CustomObjects”表中拥有带有 FK 的“Colors”表以用于 sql 查询和视图仍然很好。
另一种可能性,如果你想让你的模型更简单,就像POCO样式一样,那就是使用enum有一个属性,它将按实体框架存储为整数.
然后,如果您希望在数据库中创建和更新"枚举表",我建议使用nuget包https://github.com/timabell/ef-enum-to-lookup并在EF Migration种子中使用它方法例如:
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);
}
Run Code Online (Sandbox Code Playgroud)
这将创建"Shape"表,其中包含名为Square和Round的2行,并在表"Foo"中使用相关的外键约束
太棒了@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));
}
Run Code Online (Sandbox Code Playgroud)
OnModelCreating
DataContextprotected override void OnModelCreating(ModelBuilder builder)
{
builder.SeedEnumValues<Faculty, EnumEntityRole>(e => e);
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
40756 次 |
最近记录: |