C# 代码优先枚举放入查找表

C# 代码优先枚举放入查找表,c#,entity-framework,enums,C#,Entity Framework,Enums,我在很多商店工作过,他们都运行数据库优先模型,所以总是需要查找表。为了保持数据库的完整性,查找表必须与枚举匹配。我100%同意这个想法,但是我发现,当涉及到代码优先模型时,这不是现成的。我确实在某个地方读到,EF团队可能会在EF7中添加将枚举动态添加到数据库(通过迁移)的功能,但他们警告说,这不是一个承诺 那么你(如果有的话)是如何做到这一点的呢?我将在下面的回答中提供我的解决方案,并期待您的反馈 我正在使用EF 6.1.3和.NET 4.5.1,所以我不想撒谎,我的解决方案有点深入,但我在过去

我在很多商店工作过,他们都运行数据库优先模型,所以总是需要查找表。为了保持数据库的完整性,查找表必须与枚举匹配。我100%同意这个想法,但是我发现,当涉及到代码优先模型时,这不是现成的。我确实在某个地方读到,EF团队可能会在EF7中添加将枚举动态添加到数据库(通过迁移)的功能,但他们警告说,这不是一个承诺

那么你(如果有的话)是如何做到这一点的呢?我将在下面的回答中提供我的解决方案,并期待您的反馈


我正在使用EF 6.1.3和.NET 4.5.1,所以我不想撒谎,我的解决方案有点深入,但我在过去几天里一直在使用它,我发现它完全符合我的需要

让我们从顶部开始,我创建的基类:

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 {}
StringValue属性是我创建的自定义属性(基于我在网上找到的示例),允许我调用:

AddressTypes.Home.GetStringValue();
它将返回字符串值:
Home

我将查找实体模型添加到我的数据库集中,以便创建表,但我从未在任何其他实体模型中直接引用查找实体模型。它的唯一目的是在数据库中创建查找表,以便我可以针对它们创建外键约束

public DbSet<AddressType> AddressTypes { get; set; }
在LookUpdateAUP方法中,我添加所有自定义外键和索引,在LookUpdateDown中删除所有自定义外键和索引

当我运行updatedatabase时,我所有的表都有一个值,该值以前表示某个内容(在本例中为AddressType),但没有实际值,现在通过将其链接到其查找表可以看到该值


我承认,仅仅为了向数据库中输入少量数据,这似乎需要做很多工作,但现在每次我删除/更改/向枚举中添加新项时,它都会自动推送到数据库中。另外,正如我在上面的问题中所述,这通过在“整数”字段上设置外键约束来创建数据库完整性。

如果您不想弄乱上下文,但仍希望在数据库中使用枚举进行故障排除和手动查询。这也不是很理想,但您可以在需要时立即进行迁移。显然,需要进行一些清理和调整,这可能会根据您的用例而有所不同

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));
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
运用系统反思;
使用System.Text.RegularExpressions;
使用LookupExample.Data;
使用Microsoft.AspNetCore.Mvc;
使用Microsoft.EntityFrameworkCore;
命名空间LookupExample.Areas.Admin.Controller
{
//[授权]
[区域(“管理”)]
公共类SetupController:控制器
{
私有应用程序上下文数据库;
公共设置控制器(ApplicationDbContext dbContext)
{
_db=dbContext;
}
公共IActionResult枚举()
{
var enums=Assembly.getExecutionGassembly().GetTypes()
.Where(ttype=>ttype.IsEnum&&ttype.IsPublic&&ttype.Namespace.StartsWith(“LookupExample.Models”);
var dictionary=enums.ToDictionary(EnumTableName,EnumDictionary);
if(dictionary.Count e,e=>Enum.GetName(eenum,e));
}
}
}

看看,我知道他们计划(在某个时候)添加它,但我已经等得不耐烦了。我仔细阅读了评论,看到了你的NuGet软件包帖子…很好。我将把它添加到我的项目中,谢谢。我喜欢你的项目,它很好…在一些事情上有一些问题,比如自定义模式,但总体来说它很好。我用叉子叉了一下,看看能不能帮上忙。谢谢,很高兴你喜欢。我在处理捐款方面比我希望的要落后一点,但这还远远没有结束。我会称之为更“稳定”,您的请求可能会解决我遇到的一些相同问题。主要是自定义模式使用。我还将添加一些配置选项,以允许将枚举表放入它们自己的模式中。我不清楚将查找实体模型添加到数据库集实际上如何创建外键约束?您是否需要从要对其进行约束的模型中引用查找实体模型?我想您可以使用Modelbuilder创建一个伪约束,但看起来您并没有这样做。我遇到了同样的问题,需要查找表,同时在我的项目中使用枚举,所以我希望这能起作用,但正如我所说的,我对如何使用上述示例创建外键约束感到困惑。@Nicholas抱歉,我很久以前写过这篇文章,但如果我没记错的话,我创建了一个新的类文件,我在迁移文件本身中调用了它(分别是LookUpdateAUP和LookUpdateDown)。在这个文件中,我为每个相应的查找表创建外键。老实说,这不是我想出的最佳解决方案,但却是第一个。我强烈推荐Tim Abell的NuGet软件包(),它最终使用的不仅仅是我的解决方案。。。对于(var i=1;imodelBuilder.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();
public void LookupDataUp()
public void LookupDataDown()
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));
        }
    }
}