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 的位置。
(本系統遵循三層式系統設計原則,資料層的呼叫者只有邏輯層的元件)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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);


下列程式碼是呼叫者端要新增員工資料的範例,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
    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 的參數定義部分。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 值,可做進一步的處理。


下列程式碼是呼叫者端要更新員工資料的範例,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
    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";
    }
}


下列程式碼是呼叫者端要刪除員工資料的範例,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    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。


沒有留言:

張貼留言