應用 Enum 來做多個選項設定時只存一個資料欄位

在程式設計中,常常會遇到會給使用者透過CheckBox的方式,來選取是否開否指定的屬性,
針對這些設定,有許多種寫程式的方法,之前Dotjum常看到的方式,都是如果有五個要自行設定的屬性,
那就會在資料表中開好五個對應的欄位,但這樣的處理,針對未來自行設定資料的增加,勢必資料表欄位要重新調整,而採取 Enum 的方式,只需要一個欄位,未來再增加多少個設定屬性,都只需一個欄位,而且也能夠讓程式撰寫中,能夠更清晰的表達出自行設定欄位的定義。

摘要:
在程式設計中,常常會遇到會給使用者透過CheckBox的方式,來選取是否開否指定的屬性,
針對這些設定,有許多種寫程式的方法,之前Dotjum常看到的方式,都是如果有五個要自行設定的屬性,
那就會在資料表中開好五個對應的欄位,但這樣的處理,針對未來自行設定資料的增加,勢必資料表欄位要重新調整,
而採取 Enum 的方式,只需要一個欄位,未來再增加多少個設定屬性,都只需一個欄位,而且也能夠讓程式撰寫中,
能夠更清晰的表達出自行設定欄位的定義。

介紹:
這一段時間,開始接觸使用類似Entity的概念開發方式,來做系統的開發,對Dotjum來說改變很大,
在資料的傳遞及邏輯的判斷方面,很蠻多不同於自己之前的學習,而在長期的跟 瓶水相逢.Net 請益,
也慢慢的有些成長,希望透過第一篇透過「使用 Enum 來做多選項設定應用」能夠慢慢的介紹關於
Dotjum所瞭解的Entity的概念開發方式,當然本篇跟Entity的概念開發方式比較沒有太多的介紹,
主要還是在Enum的應用面,在之後的文章中,在介紹Dotjum自己用Entity的概念開發方式得心得。

之前 瓶水相逢.Net 所介紹 Enum 的設計與應用 - 簡易權限設計 ,有詳細的介紹 Enum在權限相關的使用,
而本篇Dotjum介紹使用的方法,設定的情境,是使用者管理介面中可以透過CheckBox勾選的方式,
來選取他是否要在顯示自己的相關資料在特定的公開網頁。
image
如同摘要所說明的,之前Dotjum會採用的方式,可能直接在資料表,開好四個對應的權限欄位,
但有可能過幾天,增加了新的欄位的時候,就會影響到資料表的ER。

而如何採用一個欄位來記住的方式呢?Dotjum這邊先想到的方法就是用Enum的方式,
首先,要先在做出一支設定的 Enum   AccessConfig.cs
AccessConfig.cs 的目的是用來在程式中,用來做位元計算。

public enum AccessConfig  
{
    None = 0, 
    OpenTel = 1,	 
    OpenFax = 2,
    OpenWebSite = 4,
    OpenEmail = 8

}

再來就是建立一個 TestMember.cs ,這邊關於Entity的概念開發方式,就簡單的跟各位介紹,
這邊先建立一個 TestMember 的類別,針對剛剛圖中所顯示的四個選取資料的屬性,做一個設定,
而重點就是在於 取的參數資訊  AccessPropertyCheck 及 設定參數資訊 AccessConfigSetter
這邊先說明在 AccessConfig.cs 中,他是利用位元組的概念,所以在設定中可以看到
OpenTel = 1 = 0001
OpenFax = 2 = 0010
OpenWebSite = 4 = 0100
OpenEmail =8 = 1000

所以我們用文字來模擬 AccessPropertyCheck 的流程,
在AccessPropertyCheck  使用 & 會針對其運算元進行邏輯 AND 運算,
所以會與傳入的參數做邏輯 AND 運算。
如果目前是 0001 (1) ,傳入的值是 1000 (8)
目前  0001 (1)
傳入  1000 (8)
--------------
結果  1001 (9)
在  AND 運算後,會變成 1001 ,在判斷是否跟傳入的 1000 相等,
在這個範例中本來只有開放 OpenTel 當傳入 OpenEmail ,
是詢問 OpenEmail  是否有開放,傳出的資料則為 false,OpenEmail  沒有開放。

而設定參數方面 AccessConfigSetter ,則是傳入設定哪一個參數及bool
在是要開啟設定(true)時,使用 | 會針對其運算元進行邏輯 OR 運算
如果目前是只選 0001 (1)傳入值是 1000 (8)
目前 0001 (1)
傳入 1000 (8)
--------------
結果 1001 (9)
則 OR 運算後,會變成 1001,再將值給入本身的 AccessConfig 設定,
在這個範例中本來只有開放 OpenTel ,當傳入要開啟 OpenEmail ,
整個設定檔的值就會變成  1001 (9),儲存到 AccessConfig  設定。

當開啟設定(false)時,則是先使用~ 運算子會在運算元上執行反轉每個位元的位元補數運算,
在進行 邏輯 AND 運算。

   目前 1001 (1)
~傳入1000 (9)
--------------
結果 1000(8)
在這個範例中本來開放了 OpenTel 、OpenEmail ,當傳入要取消 OpenTel ,
整個設定檔的值就會變成  1000(8),儲存到 AccessConfig  設定。

 

using System;
 
/// <summary>
/// School 的摘要描述
/// </summary>
 
public class TestMember
{
    public TestMember()
    {

    }
    //設定針對前UI的四個屬性
    public bool IsOpenTel
    {
        get { return AccessPropertyCheck(AccessConfig.OpenTel); }
        set { AccessConfigSetter(AccessConfig.OpenTel, value); }
    }
    public bool IsOpenFax
    {
        get { return AccessPropertyCheck(AccessConfig.OpenFax); }
        set { AccessConfigSetter(AccessConfig.OpenFax, value); }
    }
    public bool IsOpenWebSite
    {
        get { return AccessPropertyCheck(AccessConfig.OpenWebSite); }
        set { AccessConfigSetter(AccessConfig.OpenWebSite, value); }
    }
    public bool IsOpenEmail
    {
        get { return AccessPropertyCheck(AccessConfig.OpenEmail); }
        set { AccessConfigSetter(AccessConfig.OpenEmail, value); }
    }
   
    private AccessConfig _AccessConfig = AccessConfig.None;
    public AccessConfig AccessConfig
    {
        get { return this._AccessConfig; }
        set { this._AccessConfig = value; }
    }

    //取的參數資訊
    protected bool AccessPropertyCheck(AccessConfig ac)
    {
        //& 會針對其運算元進行邏輯 AND 運算
        //如果目前是 0001 (1) ,傳入的值是 1000 (8) 
        //則 AND 運算後,會變成 1001 ,在判斷是否跟傳入的 1000 相等
        return (this.AccessConfig & ac) == ac;
    }
    //設定參數資訊
    protected void AccessConfigSetter(AccessConfig ac, bool select)
    {
        if (select)
        {
            // | 會針對其運算元進行邏輯 OR 運算
            //如果目前是只選 0001 (1)傳入值是 1000 (8)
            //則 OR 運算後,會變成 1001,再將值給入本身的 AccessConfig 設定
            this.AccessConfig = AccessConfig | ac;
        }
        else
        {
            // ~ 運算子會在運算元上執行反轉每個位元的位元補數運算
            //如果目前是只選 0001 (1)傳入值是 0001 (1)
            //則 ~ 運算後,會變成 0000,再將值給入本身的 AccessConfig 設定
            this.AccessConfig = AccessConfig & ~ac;
        }
    }


}

在剛剛這麼多說明後,在使用上如何使用呢? 

 

 protected void Button1_Click(object sender, EventArgs e)
    {
        //模擬從資料庫中給值流程
        //先建構出 TestMember
        TestMember member = new TestMember();
        //模擬給值 10 開放 OpenFax 及 OpenEmail
        member.AccessConfig = (AccessConfig)10;

        Response.Write("OpenTel:" + member.IsOpenTel.ToString() + "---OpenTel = 1" + "<br/>");
        Response.Write("OpenFax:" + member.IsOpenFax.ToString() + "---OpenFax = 2" + "<br/>");
        Response.Write("OpenWebSite:" + member.IsOpenWebSite.ToString() + "---OpenWebSite = 4" + "<br/>");
        Response.Write("OpenEmail:" + member.IsOpenEmail.ToString() + "---OpenEmail = 8" + "<br/>");
        Response.Write("AccessConfig:"+ member.AccessConfig.ToString());    
         

    }


  執行顯示畫面

image
 

 

 

 protected void Button2_Click(object sender, EventArgs e)
    {
        //模擬給值到資料庫
        TestMember member = new TestMember();
        member.IsOpenTel = true; //OpenTel = 1,	
        member.IsOpenFax = true; //OpenFax = 2,
        member.IsOpenWebSite = false; // OpenWebSite = 4,
        member.IsOpenEmail = true; // OpenEmail = 8        

        Response.Write("OpenTel:" + member.IsOpenTel.ToString() + "---OpenTel = 1" + "<br/>");
        Response.Write("OpenFax:" + member.IsOpenFax.ToString() + "---OpenFax = 2" + "<br/>");
        Response.Write("OpenWebSite:" + member.IsOpenWebSite.ToString() + "---OpenWebSite = 4" + "<br/>");
        Response.Write("OpenEmail:" + member.IsOpenEmail.ToString() + "---OpenEmail = 8" + "<br/>");
        Response.Write("AccessConfig:" + member.AccessConfig.ToString());
    }

image

透過這樣設定的方式,就能夠將多個自訂的CheckBox屬性,使用一個欄位的方式,儲存到資料庫,
而在UI檢視資料的時候,也可以直接透過 if(member.Isxxxx) 的方式,來決定是否要顯示資料,
而未來在增加新的CheckBox屬性時,就更動的範圍不會到ER資料表這邊,當然.cs這邊不管這樣,
一定都要增加對應的屬性,來針對新的CheckBox欄位,給UI做資料的存取。

參考資料:
Enum 的設計與應用 - 簡易權限設計
http://subtextproject.com/