我曾在许多商店工作,他们运行数据库优先模型,因此始终需要查找表。您的查找表必须与您的枚举匹配,以便保持数据库的完整性。我100%同意这个想法,但已经发现,当涉及到Code First Model时,这不是开箱即用的。我确实读到了EF团队可能会添加在EF7中动态地将Enums添加到您的数据库(通过迁移)的功能,但是他们警告说它不是一个承诺。
那你怎么做(如果有的话)呢?我将在下面的答案中提供我的解决方案,并期待您的反馈。
我使用的是EF 6.1.3和.NET 4.5.1
答案 0 :(得分:5)
所以我不会撒谎,我的解决方案有点深入,但我过去几天一直在使用它,而且我发现它完全符合我的需要。
让我们从我创建的基类开始:
public abstract class LookupTableBase
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
以下是我的一个查找表实体模型的示例:
/// <summary>
/// Lookup Table for Enumeration AddressTypes
/// File Reference: DataAccessLayer/Enumerations/Locators.cs
/// DO NOT USE
/// SHOULD NOT BE AVAILABLE IN ENTITY MODELS
/// </summary>
[Table("AddressTypes", Schema = "Lookup")]
public class AddressType : LookupTableBase {}
以下是与查找表一起使用的枚举:
public enum AddressTypes
{
[StringValue("")]
Unknown = 0,
[StringValue("Home")]
Home = 1,
[StringValue("Mailing")]
Mailing = 2,
[StringValue("Business")]
Business = 3
}
StringValue属性是我创建的自定义属性(基于我在网上找到的示例),允许我调用:
AddressTypes.Home.GetStringValue();
将返回字符串值:Home
。
我将查找实体模型添加到我的DbSets中,因此将创建表,但我从未在任何其他实体模型中直接引用查找实体模型。它的唯一目的是在DB中创建查找表,以便我可以针对它们创建外键约束。
public DbSet<AddressType> AddressTypes { get; set; }
在我的Context的OnModelCreating方法中,我确实必须添加它,因为Data Annotation似乎没有完全保留:
modelBuilder.Entity<AddressType>()
.Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
在我的移植配置文件中,我将其添加到种子方法:
var addressTypeCount = Enum.GetValues(typeof (AddressTypes)).Length;
var addressTypes = new List<AddressType>();
for (var i = 1; i < addressTypeCount; i++) {
addressTypes.Add(new AddressType {
Id = i,
Name = ((AddressTypes)i).GetStringValue()
});
}
context.AddressTypes.AddOrUpdate(c => c.Id, addressTypes.ToArray());
context.SaveChanges();
最后,在Migration文件本身中,我将所有查找表创建方法移到列表顶部,现在我可以将外键约束添加到引用该枚举的任何表中。就我而言,我更进了一步。由于Migration Class是部分的,我创建了另一个部分类来匹配它。创建了两种方法:
public void LookupDataUp()
public void LookupDataDown()
在LookupDataUp方法中,我添加了所有自定义外键和索引,并在LookupDataDown中删除了所有自定义外键和索引。
当我运行Update-Database时,我以前的所有表都有一些表示某些东西的整数值(在这种情况下是一个AddressType)但是没有实际值,现在有一个值可以通过将它链接到它的查找来看到表
我承认,这似乎只是为了将一些少量数据存入数据库,但现在每次我删除/更改/添加新项目到我的枚举时,它都会被自动推送到D B。正如我在上面的问题中所说的,这通过在&#39;整数&#39;上设置外键来创建数据库完整性。字段。
答案 1 :(得分:0)
如果您不想弄乱上下文,但仍然希望数据库中的枚举可用于故障排除和手动查询。这也不理想,但是您可以在需要时进行一次迁移。显然,需要进行一些清理和调整,具体取决于您的用例。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using LookupExample.Data;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace LookupExample.Areas.Admin.Controllers
{
// [Authorize]
[Area("Admin")]
public class SetupController : Controller
{
private ApplicationDbContext _db;
public SetupController(ApplicationDbContext dbContext)
{
_db = dbContext;
}
public IActionResult Enums()
{
var enums = Assembly.GetExecutingAssembly().GetTypes()
.Where(ttype => ttype.IsEnum && ttype.IsPublic && ttype.Namespace.StartsWith("LookupExample.Models"));
var dictionary = enums.ToDictionary(EnumTableName, EnumDictionary);
if (dictionary.Count <= 0) return Json(dictionary);
#pragma warning disable EF1000 // Possible SQL injection vulnerability.
foreach (var kvp in dictionary)
{
var table = kvp.Key;
var tableSql = $"IF OBJECT_ID('{table}', 'U') IS NOT NULL DROP TABLE {table}; CREATE TABLE {table} ( Id int, Val varchar(255));";
_db.Database.ExecuteSqlCommand(tableSql);
if (kvp.Value.Count <= 0) continue;
var insertSql = $"INSERT INTO {table} (Id, Val) VALUES ( @P0, @P1);";
foreach (var row in kvp.Value)
{
_db.Database.ExecuteSqlCommand(insertSql, row.Key, row.Value);
}
}
#pragma warning restore EF1000 // Possible SQL injection vulnerability.
return Json(dictionary);
}
private string EnumTableName(Type eenum)
{
var namespaceModifier = Regex.Replace(Regex.Replace(eenum.Namespace, @"^LookupExample\.Models\.?", ""), @"\.?Enums$", "").Replace(".", "_");
if (namespaceModifier.Length > 0)
{
namespaceModifier = namespaceModifier + "_";
}
return "dbo.Enum_" + namespaceModifier + eenum.Name; // TODO enum schema?
}
private Dictionary<int, string> EnumDictionary(Type eenum)
{
return Enum.GetValues(eenum).Cast<int>().ToDictionary(e => e, e => Enum.GetName(eenum, e));
}
}
}