2018/04/12

SampleCMS 資料層元件說明:基本的呼叫方式

SampleCMS 的資料存取幾乎都是透過事先寫好的 Stored Procedure(以下簡稱 SP)。
(讀取資料用的 SP 名稱為 dbo.spTableName_GetData 或是 dbo.spTableName_GetList、
 新增用的 SP 名稱為 dbo.spTableName_InsertData、
 更新用的 SP 名稱為 dbo.spTableName_UpdateData、
 刪除用的 SP 名稱為 dbo.spTableName_DeleteData)
因此本系統的資料層元件是以執行 SP 做為主軸來設計,
以下說明基本的呼叫方式,

下圖是資料層的主要元件關聯圖,

下列程式碼是呼叫者端要取得員工資料的範例,也就是上圖左側 Caller use 的位置。
(本系統遵循三層式系統設計原則,資料層的呼叫者只有邏輯層的元件)
    public DataSet GetEmployeeData(string empAccount)
    {
        IDataAccessCommand cmd = DataAccessCommandFactory.GetDataAccessCommand(DBs.MainDB);
        spEmployee_GetData cmdInfo = new spEmployee_GetData()
        {
            EmpAccount = empAccount
        };

        DataSet ds = cmd.ExecuteDataset(cmdInfo);
        dbErrMsg = cmd.GetErrMsg();

        return ds;
    }

    public class DBs
    {
        public static IDataAccessSource MainDB
        {
            get
            {
                return new DataAccessSource("DBConnString");
            }
        }
    }

    public class spEmployee_GetData : IDataAccessCommandInfo
    {
        // DataAccessCommand 會使用欄位變數當做 SqlParameter 的產生來源(使用名稱、值);屬性不包含在其中。
        // 輸出參數請加上屬性 [OutputPara]
        public string EmpAccount;

        public CommandType GetCommandType()
        {
            return CommandType.StoredProcedure;
        }

        public string GetCommandText()
        {
            return "dbo.spEmployee_GetData";
        }
    }


以下參照程式碼行號做說明,

Line 3: 呼叫者需要存取資料庫,因此透過簡單工廠 DataAccessCommandFactory 取得一個指令執行者的物件 (cmd),拿回來的物件只需透過介面 IDataAccessCommand 操作。

Line 4~7: spEmployee_GetData 就是元件關聯圖中 spDemo_WithPara 的位置,spEmployee_GetData 是一個實作 IDataAccessCommandInfo 介面的類別,它用來描述資料庫中的 SP: dbo.spEmployee_GetData 的名稱與參數資訊,使用它 new 出指令資訊物件 (cmdInfo) 並且設定好參數值,準備丟給指令執行者 (cmd) 去執行。

Line 9: 將指令資訊物件 (cmdInfo) 丟給指令執行者 (cmd) 去執行取回填入資料的 DataSet,執行過程中指令執行者 (cmd) 會自動抓取指令資訊物件 (cmdInfo) 的成員變數,依照成員變數的名稱,將其值傳送給 SP 的同名參數,此次範例的成員變數只有 string EmpAccount 一個(在倒數第 12 行)。

Line 10: 使用指令執行者 (cmd) 的 GetErrMsg() 取得錯誤訊息,萬一有異常錯誤時可使用。

不只是讀取資料,新增資料、更新資料、刪除資料皆以這樣的寫法去呼叫對應的 SP,差別只有在 Line 9 的部分,把 cmd.ExecuteDataset(cmdInfo); 更改為 cmd.ExecuteNonQuery(cmdInfo);


下列程式碼是呼叫者端要新增員工資料的範例,
        public bool InsertEmployeeData(AccountParams param)
        {
            IDataAccessCommand cmd = DataAccessCommandFactory.GetDataAccessCommand(DBs.MainDB);
            spEmployee_InsertData cmdInfo = new spEmployee_InsertData()
            {
                EmpAccount = param.EmpAccount,
                EmpPassword = param.EmpPassword,
                EmpName = param.EmpName,
                Email = param.Email,
                Remarks = param.Remarks,
                DeptId = param.DeptId,
                RoleId = param.RoleId,
                IsAccessDenied = param.IsAccessDenied,
                StartDate = param.StartDate,
                EndDate = param.EndDate,
                OwnerAccount = param.OwnerAccount,
                PasswordHashed = param.PasswordHashed,
                DefaultRandomPassword = param.DefaultRandomPassword,
                PostAccount = param.PostAccount
            };

            bool result = cmd.ExecuteNonQuery(cmdInfo);
            dbErrMsg = cmd.GetErrMsg();

            if (result)
            {
                param.EmpId = cmdInfo.EmpId;
            }
            else if (cmd.GetSqlErrNumber() == 50000 && cmd.GetSqlErrState() == 2)
            {
                param.HasAccountBeenUsed = true;
            }

            return result;
        }

    public class spEmployee_InsertData : IDataAccessCommandInfo
    {
        // DataAccessCommand 會使用欄位變數當做 SqlParameter 的產生來源(使用名稱、值);屬性不包含在其中。
        // 輸出參數請加上屬性 [OutputPara]
        public string EmpAccount;
        public string EmpPassword;
        public string EmpName;
        public string Email;
        public string Remarks;
        public int DeptId;
        public int RoleId;
        public bool IsAccessDenied;
        public DateTime StartDate;
        public DateTime EndDate;
        public string OwnerAccount;
        public bool PasswordHashed;
        public string DefaultRandomPassword;
        public string PostAccount;
        [OutputPara]
        public int EmpId;

        public CommandType GetCommandType()
        {
            return CommandType.StoredProcedure;
        }

        public string GetCommandText()
        {
            return "dbo.spEmployee_InsertData";
        }
    }

在這個範例中比較特別的是,SP 有定義一個 output 參數,下列為 SP 的參數定義部分。
create procedure dbo.spEmployee_InsertData
@EmpAccount varchar(20)
,@EmpPassword varchar(128)
,@EmpName nvarchar(50)
,@Email varchar(100)
,@Remarks nvarchar(200)
,@DeptId int
,@RoleId int
,@IsAccessDenied bit
,@StartDate datetime
,@EndDate datetime
,@OwnerAccount varchar(20)
,@PasswordHashed bit
,@DefaultRandomPassword varchar(50)
,@PostAccount varchar(20)
,@EmpId int output
as

為了要讓指令執行者 (cmd) 能夠幫忙接回執行 SP 之後的 @EmpId 值,指令資訊 class spEmployee_InsertData 必須要在 public int EmpId 加上 [OutputPara] 這個自訂屬性。
這樣子指令執行者 (cmd) 在執行 SP 之後就會把 SP 的 @EmpId 值傳給指令資訊物件 (cmdInfo) 的 EmpId。

Line 27: 從指令資訊物件 (cmdInfo) 取回 EmpId 值。
Line 29: 若執行 SP 發生錯誤時,利用 cmd.GetSqlErrNumber(), cmd.GetSqlErrState() 取回資料庫的 Error Number 和 Error State 值,可做進一步的處理。


下列程式碼是呼叫者端要更新員工資料的範例,
        public bool UpdateEmployeeData(AccountParams param)
        {
            IDataAccessCommand cmd = DataAccessCommandFactory.GetDataAccessCommand(DBs.MainDB);
            spEmployee_UpdateData cmdInfo = new spEmployee_UpdateData()
            {
                EmpId = param.EmpId,
                EmpPassword = param.EmpPassword,
                EmpName = param.EmpName,
                Email = param.Email,
                Remarks = param.Remarks,
                DeptId = param.DeptId,
                RoleId = param.RoleId,
                IsAccessDenied = param.IsAccessDenied,
                StartDate = param.StartDate,
                EndDate = param.EndDate,
                OwnerAccount = param.OwnerAccount,
                PasswordHashed = param.PasswordHashed,
                DefaultRandomPassword = param.DefaultRandomPassword,
                MdfAccount = param.PostAccount
            };
            bool result = cmd.ExecuteNonQuery(cmdInfo);
            dbErrMsg = cmd.GetErrMsg();

            return result;
        }

    public class spEmployee_UpdateData : IDataAccessCommandInfo
    {
        // DataAccessCommand 會使用欄位變數當做 SqlParameter 的產生來源(使用名稱、值);屬性不包含在其中。
        // 輸出參數請加上屬性 [OutputPara]
        public int EmpId;
        public string EmpPassword;
        public string EmpName;
        public string Email;
        public string Remarks;
        public int DeptId;
        public int RoleId;
        public bool IsAccessDenied;
        public DateTime StartDate;
        public DateTime EndDate;
        public string OwnerAccount;
        public bool PasswordHashed;
        public string DefaultRandomPassword;
        public string MdfAccount;

        public CommandType GetCommandType()
        {
            return CommandType.StoredProcedure;
        }

        public string GetCommandText()
        {
            return "dbo.spEmployee_UpdateData";
        }
    }


下列程式碼是呼叫者端要刪除員工資料的範例,
        public bool DeleteEmployeeData(int empId)
        {
            IDataAccessCommand cmd = DataAccessCommandFactory.GetDataAccessCommand(DBs.MainDB);
            spEmployee_DeleteData cmdInfo = new spEmployee_DeleteData()
            {
                EmpId = empId
            };
            bool result = cmd.ExecuteNonQuery(cmdInfo);
            dbErrMsg = cmd.GetErrMsg();

            return result;
        }

    public class spEmployee_DeleteData : IDataAccessCommandInfo
    {
        // DataAccessCommand 會使用欄位變數當做 SqlParameter 的產生來源(使用名稱、值);屬性不包含在其中。
        // 輸出參數請加上屬性 [OutputPara]
        public int EmpId;

        public CommandType GetCommandType()
        {
            return CommandType.StoredProcedure;
        }

        public string GetCommandText()
        {
            return "dbo.spEmployee_DeleteData";
        }
    }



  • 上述資料層元件皆放置於組件 Common.DataAccess.dll 之中。
  • 簡單工廠 DataAccessCommandFactory、資料存取來源介面 IDataAccessSource 以及指令執行者介面 IDataAccessCommand,放在 namespace Common.DataAccess。
  • 帳號與權限系統相關的 SP 指令資訊類別,皆放在 namespace Common.DataAccess.EmployeeAuthority。
  • 網頁內容發佈系統相關的 SP 指令資訊類別,皆放在 namespace Common.DataAccess.ArticlePublisher。
  • class DBs 屬於呼叫者的管理範圍,放在 namespace Common.LogicObject。


沒有留言:

張貼留言