[英]How to convert the jsonconverter type from integer enum values in asp.net using c#
[英]How to create ENUM using values from the database C#
我在數據庫中有一個狀態表,我正在整個應用程序中使用其值。 狀態表將具有(ID,NAME)。 我想創建一個StatusEnum,可以在應用程序的代碼中使用它。 如何使用數據庫中的值創建ENUM?
目前我有這樣的枚舉
enum StatusCode: int
{
Open = 20,
Received = 21,
Delivered= 22,
Cancelled = 23
}
但是我想從數據庫設置值。
您可以
手動使您的枚舉定義與數據庫同步。 這是最簡單的方法。
您可以為Visual Studio 編寫一個文件生成器 (也稱為“自定義工具” ),並從某個參考數據庫生成枚舉定義。 單個文件生成器獲取一些源文件(例如* .aspx),並從中生成代碼(例如* .designer.cs)。 能夠做到這一點非常有用。
第三種技術是前面兩種技術的一種雌雄同體的變種:編寫一個獨立的工具來從數據庫中生成您的枚舉定義。 針對您的參考數據庫運行該工具以重新生成文件並重新簽入。
無論哪種方式,您都無法在不影響應用程序的情況下隨意更改數據庫中的查找表。 應用程序將不知道新添加的值; 刪除或更改值可能會破壞事情。
但是大概,您想要枚舉要完成的事情都是相對穩定的事情。
我將嘗試一個答案。
如果您的想法是您的數據庫中有魔術數字,它們本質上是const
,這意味着它們很少更改,那么我可以理解為什么您希望將它們表示為代碼中可讀的東西。
根據查詢數據的方式,有多種方法可以將數據表示為枚舉值:對於SqlDataReader
只需將記錄中的整數值轉換為枚舉即可:
int statusCodeValue = row["status"];
// But BEWARE as undefined values also upcast to the enum just fine
// but just won't have any of the defined values
if (!Enum.IsDefined(typeof(StatusCode), statusCodeValue)) throw new Exception();
StatusCode statusCode = (StatusCode)statusCodeValue;
如果您使用的是nHibernate或EF之類的ORM,則可以直接使用此映射。 只需在數據模型上定義enum屬性,它將正確映射。
您需要考慮誰是數據的所有者。 這些數據是來自另一個系統,還是您的應用程序生成了它?
如果是外部數據,則需要手動保持數據和應用程序枚舉同步。 如果它是本地的,則只需確保為每個枚舉成員分配一個整數值,並且切勿更改該整數值的語義表示形式。
你真的做不到。 您將需要一些輔助功能來幫助您。
enum StatusCode
{
Open,
Received,
Delivered,
Cancelled
}
private Dictionary<StatusCode, int> storedCodes = new Dictionary<StatusCode, int>();
public static int GetValue(StatusCode code)
{
//Return code from database
return storedCodes[code];
}
public static void SetValue(StatusCode code, int value)
{
storedCodes[code] = value;
//Set values from database
//Note: you can't set the value of your enums here
//Just a place to set some other variables to remember what the values are.
}
從技術上講,您可以通過使用文本模板來實現(例如折疊后的示例)。 但是,正如已經指出的那樣,好的文本模板化並不容易,通常需要與應用程序代碼一樣多的維護。
相反,請查看並確定您的Enum是否真的是所有可能狀態的受限列表。 如果您將狀態存儲在數據庫表中,我會說不是。 在表中,從技術上來說,這些值將有可能更改(可以插入新值,刪除舊值,甚至更改名稱以使其不再反映其原始含義)。 只有用戶約定才能阻止這種情況,並且在您的應用程序代碼中不會立即顯現出來。
重新考慮數據庫設計可能會更好。 下一個陳述對我來說很難解釋,但是我不希望基於子關系來推斷實體狀態。 相反,我更喜歡根據給定數據行的各個列值進行代碼決策。
例如,而不是按照兩個表:
CREATE TABLE status_codes (
key INT PRIMARY KEY,
value VARCHAR(32)
);
CREATE TABLE entity (
id INT PRIMARY KEY,
name VARCHAR(32),
status INT
CONSTRAINT fk_status FOREIGN KEY status REFERENCES status_codes(id)
);
INSERT INTO status_codes
VALUES (20, 'Open')
,(21, 'Received')
,(22, 'Delivered')
,(23, 'Cancelled');
我會比較喜歡:
CREATE TABLE entity (
id INT,
name VARCHAR(32),
is_received BIT NOT NULL,
is_delivered BIT NOT NULL,
is_cancelled BIT NOT NULL,
is_closed BIT NOT NULL
);
對我來說,我發現在沒有幻數的情況下更容易編寫邏輯代碼。
這樣,您就不再需要執行以下操作:
/* SQL */
SELECT * FROM entity WHERE status = 20;
/* C# */
if (entity.Status == StatusCodes.Open) { /* do something */ }
if (entity.Status == 20) { /* do something */ }
而是這樣做:
/* SQL */
SELECT * FROM entity WHERE is_closed = 0;
/* C# */
if (!entity.IsClosed) { /* do something */ }
您也可以采用數據庫約束進行處理(例如,確保項目在交付之前不能被標記為已接收,並且如果已經交付則不能取消)。
我也將“是開放的”語義更改為“已關閉”,但這只是個人喜好,使做某事(即關閉)比什么都不做(即開放)更重要。
我還注意到有時您確實需要事物的“軟”,用戶可維護狀態。 但是,我建議這些僅用於顯示目的,並且不應該圍繞這些“軟”狀態編寫“硬”代碼。
(如果您的應用程序打算成為高度可定制的現成產品,則可以考慮使軟狀態可用於腳本或規則引擎,因此定義狀態的用戶還可以圍繞它們定義業務規則,但這超出了范圍。)
綜上所述,如果您真的真的需要做自己想做的事,下面是一種可能的方法...
首先,您需要一個枚舉值的規范來源。 您的C#枚舉正確,或者SQL定義正確。
一旦確定,我可能會使用某種形式的文本模板,例如T4或自定義腳本文件/已編譯的exe來生成非規范源代碼。
使用Enum.GetValues(typeof(StatusCode))
和Enum.GetName(typeof(StatusCode), value)
反映您的Enum,然后使用它在目標表中生成CHECK
約束。 將基本int
類型存儲在數據庫表中(例如, ALTER TABLE my_table ADD status_code INT
)。
// (untested, pseudo-ish code -- WATCH FOR INJECTION!)
StringBuilder sql = new StringBuilder();
sql.Append(@"ALTER TABLE [my_table] ADD CONSTRAINT chk_status_code CHECK (status_code IN (");
bool first = true;
foreach (var v in Enum.GetValues(typeof(StatusCode)) {
if (!first) sql.Append(", ");
sql.Append(v);
first = false;
}
sql.Append("));");
// write sql to file, or run against the development database
這將使您非常接近可以在構建/安裝上運行的SQL語句。 請注意,此操作不打算在數據庫的正常操作期間運行。
如果需要此功能,則可能還需要生成一個內聯表函數以將數字映射到名稱,例如:
// (untested, pseudo-ish code -- WATCH FOR INJECTION!)
StringBuilder sql = new StringBuilder();
sql.AppendLine(@"IF OBJECT_ID('dbo.tf_status_codes') IS NULL EXECUTE('
CREATE FUNCTION dbo.tf_status_codes RETURNS TABLE AS RETURN (
SELECT ''not yet built'' AS err
)
')");
sql.AppendLine(@"ALTER FUNCTION dbo.tf_status_codes RETURNS TABLE AS RETURN (")
.AppendLine(@" SELECT value, name FROM (VALUES ")
bool first = true;
foreach (var v in Enum.GetValues(typeof(StatusCode)) {
if (!first) sql.AppendLine(",");
sql.Append(@" ({0}, '{1}')",
v,
Enum.GetName(typeof(StatusCode), v));
first = false;
}
sql.AppendLine(@" ) e(value, name);")
.AppendLine(@")";
// write sql to file, or run against the development database
作為構建后事件運行構建工具,以便在約束/表之前更新代碼。
盡管我會確保表源在生產迭代的生存期內不會更改(即僅在部署時定義),但這一點更為簡單。 為此,我將枚舉值定義為內聯表函數,而不是表:
CREATE FUNCTION dbo.status_codes
RETURNS TABLE
AS RETURN (
SELECT value, name
FROM (VALUES (20, 'Open')
,(21, 'Received')
,(22, 'Delivered')
,(23, 'Cancelled')) AS v(value, name)
)
然后,在我的構建工具中,連接到數據庫,檢索值並生成枚舉:
// untested, pseudo, assumes an existing database connection routine
IDataReader reader = DB.GetReader("SELECT value, name FROM dbo.status_codes()");
StringBuilder code = new StringBuilder();
code.AppendLine("namespace MyApp {")
.AppendLine(" public enum StatusCodes : int {");
bool first = true
while (reader.Read()) {
if (!first) code.AppendLine(",");
code.Append(" {0} = {1}", reader["name"], reader["value"]);
first = false;
}
code.AppendLine(" }")
.AppendLine("}");
// ...write the code to the Enum class file, and exit with 0 code
將構建工具作為Pre-Build事件運行(以便在構建代碼之前生成代碼)。
(正如我所說,上面的代碼未經測試,也沒有試圖確保將其注入的安全性。使用風險自負並進行徹底測試)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.