Our database has several tables that have a value that can be one of 4 things, so we made these columns instead have an id column with a foreign key to an enum table with an id and a value column to hold those 4 values. When interfacing this through an ORM, I came up with the following enum-like class to manage keeping things in sync:
using DataAccess.ECI.Models;
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace DataAccess.ECI
{
public class StatusEnum
{
private string type;
private static Dictionary<string, Status> lookupCache = null;
private static List<StatusEnum> members= new List<StatusEnum>();
private StatusEnum(string type)
{
this.type = type;
members.Add(this);
}
//the text needs to be kept consitent with the Status table
public static readonly StatusEnum ACTIVE = new StatusEnum("ACTIVE");
public static readonly StatusEnum COMPLETED= new StatusEnum("COMPLETED");
public static readonly StatusEnum DELETED= new StatusEnum("DELETED");
public static readonly StatusEnum INPROGRESS= new StatusEnum("INPROGRESS");
public static void Init(ECIContext ctx)
{
lookupCache=new Dictionary<string, Status>();
foreach(StatusEnum member in members)
{
try
{
lookupCache.Add(member.type, ctx.Status.Single(s => s.Name == member.type));
}catch(Exception ex)
{
throw new InvalidOperationException($"No record found with Name {member.type}",ex);
}
}
}
public Status asDBType()
{
if (lookupCache == null) throw new InvalidOperationException("Init(ECIContext) has not been called for this enum");
return lookupCache[type];
}
public override string ToString()
{
return type;
}
}
}
I thought it works pretty well, but I'm concerned about a few things.
- since it's mostly static methods, it forces lots of duplication when other enum tables are added that need the same code wrapper
- if we forget to call init, this error isn't caught until late in the runtime
I would appreciate any comments on the code and suggestions for how to make it better.
EDIT:
for more context, we are using aspnet core, so the context is added in Startup.cs:ConfigureServices(IServiceCollection services) :
...
services.AddDBContext<ECIContext>(options=>
options.UseSqlServer(/*connection string from config*/)
);
...
and Init
is currently called in Startup.cs:Configure(IApplicationBuilder app,...):
...
ECIContext ctx=app.ApplicationServices.GetService<ECIContext>();
StatusEnum.Init(ctx);
...
this enum will be used like
ctx.Document.add(new Document{
PageCount=4,
Status=StatusEnum.ACTIVE.asDBType(),
...other properties...
});
which works because our ORM, entity framework core, adds a field for the foreign key linked table, and stores the id when appropriate