DB 첫 번째 방법은 ID 열 이름이 테이블 이름과 일치하는 각 열거 형에 대해 일관된 테이블을 만들어서 사용할 수 있습니다. 외부 키 제약 조건과보기의 친숙한 열을 지원하기 위해 데이터베이스 내에서 열거 형 값을 사용할 수있는 것이 유리합니다. 우리는 현재 수많은 버전 데이터베이스에 흩어져있는 ~ 100 개 열거 형을 지원하고 있습니다.
코드 우선 환경 설정의 경우 아래에 표시된 T4 전략을 데이터베이스에 기록하기 위해 되돌릴 수 있습니다.
create table SomeSchema.SomeEnumType (
SomeEnumTypeId smallint NOT NULL primary key,
Name varchar(100) not null,
Description nvarchar(1000),
ModifiedUtc datetime2(7) default(sysutcdatetime()),
CreatedUtc datetime2(7) default(sysutcdatetime()),
);
각 테이블은 T4 template (*.tt) script을 사용하여 C#으로 가져올 수 있습니다.
- "열거 프로젝트"를 만듭니다. 아래에 표시된 .tt 파일을 추가하십시오.
- 각 데이터베이스 스키마 이름에 대한 하위 폴더를 만듭니다.
- 각 열거 형식에 대해 이름이 SchemaName.TableName.tt 인 파일을 만듭니다. 파일 내용은 항상 같은 한 줄입니다 < #를 @가 파일을 포함 = ".. \ EnumGenerator.ttinclude"#>
- 그런 다음 생성/우 1 개 이상의 파일을 클릭하고 "실행을, 열거를 업데이트 맞춤 도구 "(아직 자동 업데이트가 없습니다). 그것은 추가/업데이트하려면 .cs 파일을 프로젝트에합니다 :
using System.CodeDom.Compiler;
namespace TheCompanyNamespace.Enumerations.Config
{
[GeneratedCode("Auto Enum from DB Generator", "10")]
public enum DatabasePushJobState
{
Undefined = 0,
Created = 1,
}
public partial class EnumDescription
{
public static string Description(DatabasePushJobState enumeration)
{
string description = "Unknown";
switch (enumeration)
{
case DatabasePushJobState.Undefined:
description = "Undefined";
break;
case DatabasePushJobState.Created:
description = "Created";
break;
}
return description;
}
}
// select DatabasePushJobStateId, Name, coalesce(Description,Name) as Description
// from TheDefaultDatabase.[SchName].[DatabasePushJobState]
// where 1=1 order by DatabasePushJobStateId
}
을 그리고 마지막으로, (다수의 해결에서 간체) 다소 울퉁불퉁 한 T4 스크립트. 사용자 환경에 맞게 사용자 정의해야합니다. 디버그 플래그는 메시지를 C#으로 출력 할 수 있습니다. 또한 .tt 파일을 마우스 오른쪽 버튼으로 클릭 할 때 "Debug T4 Template"옵션이 있습니다. EnumGenerator.ttinclude :
<#@ template debug="true" hostSpecific="true" #>
<#@ output extension=".generated.cs" #>
<#@ Assembly Name="EnvDTE" #>
<#@ Assembly Name="System.Core" #>
<#@ Assembly Name="System.Data" #>
<#@ assembly name="$(TargetPath)" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#
bool doDebug = false; // include debug statements to appear in generated output
string schemaTableName = Path.GetFileNameWithoutExtension(Host.TemplateFile);
string schema = schemaTableName.Split('.')[0];
string tableName = schemaTableName.Split('.')[1];
string path = Path.GetDirectoryName(Host.TemplateFile);
string enumName = tableName;
string columnId = enumName + "Id";
string columnName = "Name";
string columnDescription = "Description";
string currentVersion = CompanyNamespace.Enumerations.Constants.Constants.DefaultDatabaseVersionSuffix;
// Determine Database Name using Schema Name
//
Dictionary<string, string> schemaToDatabaseNameMap = new Dictionary<string, string> {
{ "Cfg", "SomeDbName" + currentVersion },
{ "Common", "SomeOtherDbName" + currentVersion }
// etc.
};
string databaseName;
if (!schemaToDatabaseNameMap.TryGetValue(schema, out databaseName))
{
databaseName = "TheDefaultDatabase"; // default if not in map
}
string connectionString = @"Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=" + databaseName + @";Data Source=Machine\Instance";
schema = "[" + schema + "]";
tableName = "[" + tableName + "]";
string whereConstraint = "1=1"; // adjust if needed for specific tables
// Get containing project
IServiceProvider serviceProvider = (IServiceProvider)Host;
DTE dte = (DTE)serviceProvider.GetService(typeof(DTE));
Project project = dte.Solution.FindProjectItem(Host.TemplateFile).ContainingProject;
#>
using System;
using System.CodeDom.Compiler;
namespace <#= project.Properties.Item("DefaultNamespace").Value #><#= Path.GetDirectoryName(Host.TemplateFile).Remove(0, Path.GetDirectoryName(project.FileName).Length).Replace("\\", ".") #>
{
/// <summary>
/// Auto-generated Enumeration from Source Table <#= databaseName + "." + schema + "." + tableName #>. Refer to end of file for SQL.
/// Please do not modify, your changes will be lost!
/// </summary>
[GeneratedCode("Auto Enum from DB Generator", "10")]
public enum <#= enumName #>
{
<#
SqlConnection conn = new SqlConnection(connectionString);
// Description is optional, uses name if null
string command = string.Format(
"select {0}, {1}, coalesce({2},{1}) as {2}" + "\n from {3}.{4}.{5}\n where {6} order by {0}",
columnId, // 0
columnName, // 1
columnDescription, // 2
databaseName, // 3
schema, // 4
tableName, // 5
whereConstraint); // 6
#><#= DebugCommand(databaseName, command, doDebug) #><#
SqlCommand comm = new SqlCommand(command, conn);
conn.Open();
SqlDataReader reader = comm.ExecuteReader();
bool loop = reader.Read();
while(loop)
{
#> /// <summary>
/// <#= reader[columnDescription] #>
/// </summary>
<#= Pascalize(reader[columnName]) #> = <#= reader[columnId] #><# loop = reader.Read(); #><#= loop ? ",\r\n" : string.Empty #>
<#
}
#> }
/// <summary>
/// A helper class to return the Description for each enumeration value
/// </summary>
public partial class EnumDescription
{
public static string Description(<#= enumName #> enumeration)
{
string description = "Unknown";
switch (enumeration)
{<#
conn.Close();
conn.Open();
reader = comm.ExecuteReader();
loop = reader.Read();
while(loop)
{#>
case <#= enumName #>.<#= Pascalize(reader[columnName]) #>:
description = "<#= reader[columnDescription].ToString().Replace("\"", "\\\"") #>";
break;
<# loop = reader.Read(); #>
<#
}
conn.Close();
#>
}
return description;
}
}
/*
<#= command.Replace("\n", "\r\n ") #>
*/
}
<#+
private string Pascalize(object value)
{
Regex rxStartsWithKeyWord = new Regex(@"^[0-9]|^abstract$|^as$|^base$|^bool$|^break$|^byte$|^case$|^catch$|^char$|^checked$|^class$|^const$|^continue$|^decimal$|^default$|^delegate$|^do$|^double$|^else$|^enum$|^event$|^explicit$|^extern$|^$false|^finally$|^fixed$|^float$|^for$|^foreach$|^goto$|^if$|^implicit$|^in$|^int$|^interface$|^internal$|^is$|^lock$|^long$|^namespace$|^new$|^null$|^object$|^operator$|^out$|^overrride$|^params$|^private$|^protected$|^public$|^readonly$|^ref$|^return$|^sbyte$|^sealed$|^short$|^sizeof$|^stackalloc$|^static$|^string$|^struct$|^switch$|^this$|^thorw$|^true$|^try$|^typeof$|^uint$|^ulong$|^unchecked$|^unsafe$|^ushort$|^using$|^virtual$|^volatile$|^void$|^while$", RegexOptions.Compiled);
Regex rx = new Regex(@"(?:[^a-zA-Z0-9]*)(?<first>[a-zA-Z0-9])(?<reminder>[a-zA-Z0-9]*)(?:[^a-zA-Z0-9]*)");
string rawName = rx.Replace(value.ToString(), m => m.Groups["first"].ToString().ToUpper() + m.Groups["reminder"].ToString());
if (rxStartsWithKeyWord.Match(rawName).Success)
rawName = "_" + rawName;
return rawName;
}
private string DebugCommand(string databaseName, string command, bool doDebug)
{
return doDebug
? " // use " + databaseName + "; " + command + ";\r\n\r\n"
: "";
}
#>
이 희망 엔티티 프레임 워크는 언젠가 기록 및 데이터베이스 값의 미러링 내에서 C#을 열거 강력한 입력을 제공하기 위해 이러한 답변의 조합을 지원합니다.
+1 누군가가보기를 원한다면 이런 종류의 일이 별도의 테이블과 FK 제약 또는 규칙적인 제약으로 적용되어야하는지에 대한 의견에 관심이 있습니까? –
@Martin 이것은 주관적인 경향이 있으므로 응답을 계획하지 않지만 OLTP/ODS의 경우 FK 제약 조건이있는 별도의 테이블을 사용합니다. DSS보고 솔루션의 경우 엔 enum의 기호 이름 (또는 설명)을 비정규 화하여보고 테이블에 다른 사실과 함께 저장합니다. –