feat: fmBPSN GridView 改用 ObjectDataSource 並採 KEYSET 分頁

- BpsnDAC 新增 SelectPage/SelectCount/SelectBPENOIndex

- SelectPage 採 KEYSET:先在窄集合 BPSNView 算 ROWID,再 join 該頁明細

- SelectCount/SelectBPENOIndex 改用窄集合(免 join)

- fmBpsn.ascx dsBPSNGrid 改 ObjectDataSource(EnablePaging)

- fmBpsn.ascx.cs 調整 Search/Selected/OnInit

- 修復 BpsnDAC.cs 亂碼註解並改存 UTF-8 BOM

- 新增 Doc/GridView改用ObjectDataSource計畫.md(盤點、計畫、效能比較)
This commit is contained in:
2026-09-10 10:29:18 +08:00
parent 577060bc78
commit 9adbcfe0a4
4 changed files with 422 additions and 99 deletions
+243 -15
View File
@@ -1,14 +1,16 @@
using System;
using System;
using System.Data;
using System.Data.Common;
using System.Configuration;
using System.Text;
using System.Collections.Generic;
using System.ComponentModel;
using Wellan.Data;
/// <summary>
/// 員工資料的資料處理類別
/// 員工資料的資料處理類別
/// </summary>
[DataObject(true)]
public class BpsnDAC : Wellan.Data.BPSN_DAC
{
private DbCommand cmdSelect;
@@ -115,7 +117,7 @@ public class BpsnDAC : Wellan.Data.BPSN_DAC
}
/// <summary>
/// 取得人員目前派駐專案
/// 取得人員目前派駐專案
/// </summary>
/// <param name="bpeno"></param>
/// <returns></returns>
@@ -125,7 +127,7 @@ public class BpsnDAC : Wellan.Data.BPSN_DAC
}
/// <summary>
/// 取得人員目前派駐專案
/// 取得人員目前派駐專案
/// </summary>
/// <param name="bpeno"></param>
/// <returns></returns>
@@ -154,7 +156,7 @@ public class BpsnDAC : Wellan.Data.BPSN_DAC
}
/// <summary>
/// 取得人員目前派駐地點
/// 取得人員目前派駐地點
/// </summary>
/// <param name="bpeno"></param>
/// <returns></returns>
@@ -164,7 +166,7 @@ public class BpsnDAC : Wellan.Data.BPSN_DAC
}
/// <summary>
/// 取得人員目前派駐地點
/// 取得人員目前派駐地點
/// </summary>
/// <param name="bpeno"></param>
/// <returns></returns>
@@ -182,7 +184,7 @@ public class BpsnDAC : Wellan.Data.BPSN_DAC
}
/// <summary>
/// 取得人員電子郵件
/// 取得人員電子郵件
/// </summary>
/// <param name="bpeno"></param>
/// <returns></returns>
@@ -197,7 +199,7 @@ public class BpsnDAC : Wellan.Data.BPSN_DAC
}
/// <summary>
/// 取得人員電子郵件2
/// 取得人員電子郵件2
/// </summary>
/// <param name="bpeno"></param>
/// <returns></returns>
@@ -212,10 +214,10 @@ public class BpsnDAC : Wellan.Data.BPSN_DAC
}
/// <summary>
/// 取得某日期人員所屬公司名稱
/// 取得某日期人員所屬公司名稱
/// </summary>
/// <param name="bpeno">員工編號</param>
/// <param name="date">日期</param>
/// <param name="bpeno">員工編號</param>
/// <param name="date">日期</param>
/// <returns></returns>
public List<string> GetCompanyByDate(string bpeno, DateTime date)
{
@@ -243,7 +245,7 @@ public class BpsnDAC : Wellan.Data.BPSN_DAC
}
/// <summary>
/// 取得人員到職日期
/// 取得人員到職日期
/// </summary>
/// <param name="bpeno"></param>
/// <returns></returns>
@@ -258,7 +260,7 @@ public class BpsnDAC : Wellan.Data.BPSN_DAC
}
/// <summary>
/// 取得人員離職日期
/// 取得人員離職日期
/// </summary>
/// <param name="bpeno"></param>
/// <returns></returns>
@@ -287,12 +289,238 @@ public class BpsnDAC : Wellan.Data.BPSN_DAC
}
/// <summary>
/// 取出某日期在職的員工
/// 取出某日期在職的員工
/// </summary>
/// <param name="date">日期</param>
/// <param name="date">日期</param>
/// <returns></returns>
public DataTable GetOnBoardEmployee(DateTime date)
{
return Query.Select(Query.NewCommand("SELECT * FROM BPSNView WHERE dbo.EmployeeOnboard(BPENO, @DATE) = 1"));
}
private const string GridSelectColumns =
@" A2.*
, B.RAPYN, C.BPPYM RAPYNX, B.RABRH, D.BSSCR RABRHX, B.RAJNO, E.BPJME RAJNOX, B.RACNO
, F.BPCME RACNOX, ISNULL(H.RPCPY, G.RPCPY) AS RPCPY ";
// 只 join 該頁明細用 (明細主表為 A2)
private const string GridDetailJoins =
@" LEFT OUTER JOIN RASN B ON A2.BPENO=B.RANUM AND (B.RAEDT IS NULL OR (B.RASDT <= @TODAY AND B.RAEDT >= @TODAY))
LEFT OUTER JOIN BPAA C ON B.RAPYN=C.BPPYN
LEFT OUTER JOIN BCLS D ON D.BSCLS=B.RABRH AND D.BSPAR='BPSNBRH'
LEFT OUTER JOIN BPJA E ON E.BPPYN=B.RAPYN AND E.BPJNO=B.RAJNO
LEFT OUTER JOIN BPJB F ON F.BPPYN=B.RAPYN AND F.BPJNO=B.RAJNO AND F.BPCNO=B.RACNO
LEFT OUTER JOIN RPIR G ON A2.BPENO=G.RPENO AND G.RPREN=(SELECT MAX(RPREN) FROM RPIR WHERE RPENO=A2.BPENO)
LEFT OUTER JOIN RPIB H ON G.RPREN=H.RPREN AND H.RPITM=(SELECT MAX(RPITM) FROM RPIB WHERE RPREN=H.RPREN) ";
/// <summary>
/// 人員資料 Grid 分頁查詢
/// </summary>
[DataObjectMethod(DataObjectMethodType.Select)]
public DataTable SelectPage(
int startRowIndex,
int maximumRows,
string BPENO,
string BPNME,
string BPENM,
string BPIDN,
string BPEDY_TYPE,
string BPDPT_TYPE,
string BPSDY1,
string BPSDY2,
string BPEDY1,
string BPEDY2,
bool canSearch,
string userId)
{
DbCommand cmd = Query.NewCommand();
StringBuilder filter = new StringBuilder();
BuildGridFilter(filter, cmd, BPENO, BPNME, BPENM, BPIDN, BPEDY_TYPE, BPDPT_TYPE, BPSDY1, BPSDY2, BPEDY1, BPEDY2, canSearch, userId);
// 先在窄集合 (BPSNView) 依 BPENO 排序計算 ROWID,取出該頁的 BPENO 後,
// 才 join 明細表,避免深頁時對整個 join 結果計算 ROW_NUMBER()。
cmd.CommandText = String.Format(
@"SELECT {0}
FROM
(
SELECT A.BPENO, ROW_NUMBER() OVER (ORDER BY A.BPENO) AS ROWID
FROM BPSNView A WHERE 1=1 {1}
) R
JOIN BPSNView A2 ON A2.BPENO = R.BPENO
{2}
WHERE R.ROWID BETWEEN @STARTROWINDEX + 1 AND @STARTROWINDEX + @MAXIMUMROWS
ORDER BY R.ROWID",
GridSelectColumns, filter.ToString(), GridDetailJoins);
cmd.Parameters.Add(Query.NewParameter("STARTROWINDEX", startRowIndex));
cmd.Parameters.Add(Query.NewParameter("MAXIMUMROWS", maximumRows));
return Query.Select(cmd);
}
/// <summary>
/// 人員資料 Grid 查詢筆數
/// </summary>
[DataObjectMethod(DataObjectMethodType.Select)]
public int SelectCount(
string BPENO,
string BPNME,
string BPENM,
string BPIDN,
string BPEDY_TYPE,
string BPDPT_TYPE,
string BPSDY1,
string BPSDY2,
string BPEDY1,
string BPEDY2,
bool canSearch,
string userId)
{
DbCommand cmd = Query.NewCommand();
StringBuilder filter = new StringBuilder();
BuildGridFilter(filter, cmd, BPENO, BPNME, BPENM, BPIDN, BPEDY_TYPE, BPDPT_TYPE, BPSDY1, BPSDY2, BPEDY1, BPEDY2, canSearch, userId);
cmd.CommandText = String.Format("SELECT COUNT(*) FROM BPSNView A WHERE 1=1 {0}", filter.ToString());
return WDAC.GetInt32Value(Query.ExecuteScalar(cmd));
}
/// <summary>
/// 取得指定人員編號在查詢結果中的序號 (0 起算)
/// </summary>
public int SelectBPENOIndex(
string targetBPENO,
string BPENO,
string BPNME,
string BPENM,
string BPIDN,
string BPEDY_TYPE,
string BPDPT_TYPE,
string BPSDY1,
string BPSDY2,
string BPEDY1,
string BPEDY2,
bool canSearch,
string userId)
{
DbCommand cmd = Query.NewCommand();
StringBuilder filter = new StringBuilder();
BuildGridFilter(filter, cmd, BPENO, BPNME, BPENM, BPIDN, BPEDY_TYPE, BPDPT_TYPE, BPSDY1, BPSDY2, BPEDY1, BPEDY2, canSearch, userId);
cmd.CommandText = String.Format(
@"SELECT ROWID - 1 FROM
(
SELECT A.BPENO, ROW_NUMBER() OVER (ORDER BY A.BPENO) AS ROWID
FROM BPSNView A WHERE 1=1 {0}
) AS TMP WHERE BPENO = @TARGET",
filter.ToString());
cmd.Parameters.Add(Query.NewParameter("TARGET", targetBPENO));
object o = Query.ExecuteScalar(cmd);
return (o is Int32 || o is Int64) ? WDAC.GetInt32Value(o) : -1;
}
/// <summary>
/// 組出人員資料 Grid 的查詢條件 (與舊 AddSearch 行為一致)
/// </summary>
private void BuildGridFilter(
StringBuilder sb,
DbCommand cmd,
string BPENO,
string BPNME,
string BPENM,
string BPIDN,
string BPEDY_TYPE,
string BPDPT_TYPE,
string BPSDY1,
string BPSDY2,
string BPEDY1,
string BPEDY2,
bool canSearch,
string userId)
{
cmd.Parameters.Add(Query.NewParameter("TODAY", DateTime.UtcNow.AddHours(8).Date));
if (!canSearch)
{
if (!string.IsNullOrEmpty(userId))
{
sb.Append(" AND BPENO LIKE @BPENO1 ");
cmd.Parameters.Add(Query.NewParameter("BPENO1", userId + "%"));
}
return;
}
if (!string.IsNullOrEmpty(BPENO))
{
sb.Append(" AND BPENO LIKE @BPENO1 ");
cmd.Parameters.Add(Query.NewParameter("BPENO1", BPENO + "%"));
}
if (!string.IsNullOrEmpty(BPNME))
{
sb.Append(" AND BPNME LIKE @BPNME1 ");
cmd.Parameters.Add(Query.NewParameter("BPNME1", BPNME + "%"));
}
if (!string.IsNullOrEmpty(BPENM))
{
sb.Append(" AND BPENM LIKE @BPENM1 ");
cmd.Parameters.Add(Query.NewParameter("BPENM1", BPENM + "%"));
}
if (!string.IsNullOrEmpty(BPIDN))
{
sb.Append(" AND BPIDN LIKE @BPIDN1 ");
cmd.Parameters.Add(Query.NewParameter("BPIDN1", BPIDN + "%"));
}
switch (BPEDY_TYPE)
{
case "1":
sb.Append(" AND (BPEDY IS NULL OR BPEDY >= @TODAY) ");
break;
case "2":
sb.Append(" AND (BPEDY < @TODAY) ");
break;
}
switch (BPDPT_TYPE)
{
case "1":
sb.AppendFormat(" AND BPDPT IN {0} ", PublicVariable.InnerDepartment);
break;
case "2":
sb.AppendFormat(" AND BPDPT NOT IN {0} ", PublicVariable.InnerDepartment);
break;
}
AppendRange(sb, cmd, "BPSDY", BPSDY1, BPSDY2);
AppendRange(sb, cmd, "BPEDY", BPEDY1, BPEDY2);
if (!IsManager(userId))
{
sb.Append(" AND BPENO NOT IN ('00001','00002','00003') ");
}
}
private void AppendRange(StringBuilder sb, DbCommand cmd, string field, string value1, string value2)
{
bool has1 = !string.IsNullOrEmpty(value1);
bool has2 = !string.IsNullOrEmpty(value2);
if (has1 && has2)
{
sb.AppendFormat(" AND {0} >= @{0}1 AND {0} <= @{0}2 ", field);
cmd.Parameters.Add(Query.NewParameter(field + "1", value1));
cmd.Parameters.Add(Query.NewParameter(field + "2", value2));
}
else if (has1)
{
sb.AppendFormat(" AND {0} LIKE @{0}1 ", field);
cmd.Parameters.Add(Query.NewParameter(field + "1", value1 + "%"));
}
else if (has2)
{
sb.AppendFormat(" AND {0} LIKE @{0}2 ", field);
cmd.Parameters.Add(Query.NewParameter(field + "2", value2 + "%"));
}
}
}
+19 -6
View File
@@ -215,12 +215,25 @@
TextMessageId="" Visible="False"></cc1:WLabel>
</asp:View>
<asp:View ID="EditView" runat="server">
<asp:SqlDataSource ID="dsBPSNGrid"
runat="server" ConflictDetection="CompareAllValues"
SelectCommand="SELECT * FROM BPSNView WHERE 1=0"
ConnectionString="Data Source=(local);Initial Catalog=THINKYU_WEB;Persist Security Info=True;User ID=sa"
ProviderName="System.Data.SqlClient"
OnSelected="dsBPSNGrid_Selected"></asp:SqlDataSource>
<asp:ObjectDataSource ID="dsBPSNGrid"
runat="server" TypeName="BpsnDAC"
SelectMethod="SelectPage" SelectCountMethod="SelectCount"
EnablePaging="true" OnSelected="dsBPSNGrid_Selected">
<SelectParameters>
<asp:Parameter Name="BPENO" Type="String" />
<asp:Parameter Name="BPNME" Type="String" />
<asp:Parameter Name="BPENM" Type="String" />
<asp:Parameter Name="BPIDN" Type="String" />
<asp:Parameter Name="BPEDY_TYPE" Type="String" />
<asp:Parameter Name="BPDPT_TYPE" Type="String" />
<asp:Parameter Name="BPSDY1" Type="String" />
<asp:Parameter Name="BPSDY2" Type="String" />
<asp:Parameter Name="BPEDY1" Type="String" />
<asp:Parameter Name="BPEDY2" Type="String" />
<asp:Parameter Name="canSearch" Type="Boolean" />
<asp:Parameter Name="userId" Type="String" />
</SelectParameters>
</asp:ObjectDataSource>
<asp:SqlDataSource ID="dsBPSNForm"
runat="server"
ConflictDetection="CompareAllValues"
+17 -78
View File
@@ -24,23 +24,6 @@ public partial class forms_worker_fmBpsn : Wellan.Web.UI.WUserControlBase
private string _BPEDY1 = "";
private string _BPEDY2 = "";
private string select =
@" SELECT
A.*
, B.RAPYN, C.BPPYM RAPYNX, B.RABRH, D.BSSCR RABRHX, B.RAJNO, E.BPJME RAJNOX, B.RACNO
, F.BPCME RACNOX, ISNULL(H.RPCPY, G.RPCPY) AS RPCPY
, ROW_NUMBER() OVER (ORDER BY A.BPENO) ROWID ";
private string from =
@" FROM BPSNView A
LEFT OUTER JOIN RASN B ON A.BPENO=B.RANUM AND (B.RAEDT IS NULL OR (B.RASDT <= @TODAY AND B.RAEDT >= @TODAY))
LEFT OUTER JOIN BPAA C ON B.RAPYN=C.BPPYN
LEFT OUTER JOIN BCLS D ON D.BSCLS=B.RABRH AND D.BSPAR='BPSNBRH'
LEFT OUTER JOIN BPJA E ON E.BPPYN=B.RAPYN AND E.BPJNO=B.RAJNO
LEFT OUTER JOIN BPJB F ON F.BPPYN=B.RAPYN AND F.BPJNO=B.RAJNO AND F.BPCNO=B.RACNO
LEFT OUTER JOIN RPIR G ON A.BPENO=G.RPENO AND G.RPREN=(SELECT MAX(RPREN) FROM RPIR WHERE RPENO=A.BPENO)
LEFT OUTER JOIN RPIB H ON G.RPREN=H.RPREN AND H.RPITM=(SELECT MAX(RPITM) FROM RPIB WHERE RPREN=H.RPREN) ";
private string where = " WHERE 1=1 ";
// private string order = " ORDER BY BPENO ";
private bool fileOK = false;
internal BprmDAC bprmDAC;
@@ -163,7 +146,6 @@ public partial class forms_worker_fmBpsn : Wellan.Web.UI.WUserControlBase
dsBPSNForm.DeleteCommand = @"DELETE BPSN WHERE BPENO = @OLD_BPENO AND APPNO = @OLD_APPNO";
dsBPSNForm.ConnectionString = ConnectionString;
dsBPSNGrid.ConnectionString = ConnectionString;
dsBPSN_BPTYP.ConnectionString = ConnectionString;
dsBPSN_BPGNR.ConnectionString = ConnectionString;
dsBPSN_BPSGR.ConnectionString = ConnectionString;
@@ -298,70 +280,27 @@ public partial class forms_worker_fmBpsn : Wellan.Web.UI.WUserControlBase
_BPEDY_TYPE = ddlBPEDY.SelectedValue;
_BPDPT_TYPE = ddlBPDPT.SelectedValue;
StringBuilder sql = new StringBuilder();
sql.Append(select);
sql.Append(from);
sql.Append(where);
dsBPSNGrid.SelectParameters.Clear();
dsBPSNGrid.SelectParameters.Add("TODAY", String.Format(WGuard1.InvariantCulture, "{0:yyyy-MM-dd}", DateTime.UtcNow.AddHours(8).Date));
if (canSearch)
{
AddSearch(sql, dsBPSNGrid, "BPENO", _BPENO, "", true);
AddSearch(sql, dsBPSNGrid, "BPNME", _BPNME, "", true);
AddSearch(sql, dsBPSNGrid, "BPENM", _BPENM, "", true);
AddSearch(sql, dsBPSNGrid, "BPIDN", _BPIDN, "", true);
switch (_BPEDY_TYPE)
{
case "1":
sql.Append(" AND (BPEDY IS NULL OR BPEDY >= @TODAY) ");
break;
case "2":
sql.Append(" AND (BPEDY < @TODAY)");
break;
}
switch (_BPDPT_TYPE)
{
case "1":
sql.Append(String.Format(" AND BPDPT IN {0} ", PublicVariable.InnerDepartment));
break;
case "2":
sql.Append(String.Format(" AND BPDPT NOT IN {0} ", PublicVariable.InnerDepartment));
break;
}
AddSearch(sql, dsBPSNGrid, "BPSDY", _BPSDY1, _BPSDY2, true);
AddSearch(sql, dsBPSNGrid, "BPEDY", _BPEDY1, _BPEDY2, true);
if (!BpsnDAC.IsManager(UserId))
{
sql.Append(" AND BPENO NOT IN ('00001','00002','00003') ");
}
}
else
{
AddSearch(sql, dsBPSNGrid, "BPENO", UserId, "", true);
}
// sql.Append(order);
dsBPSNGrid.SelectCommand = sql.ToString();
dsBPSNGrid.SelectParameters["BPENO"].DefaultValue = _BPENO;
dsBPSNGrid.SelectParameters["BPNME"].DefaultValue = _BPNME;
dsBPSNGrid.SelectParameters["BPENM"].DefaultValue = _BPENM;
dsBPSNGrid.SelectParameters["BPIDN"].DefaultValue = _BPIDN;
dsBPSNGrid.SelectParameters["BPEDY_TYPE"].DefaultValue = _BPEDY_TYPE;
dsBPSNGrid.SelectParameters["BPDPT_TYPE"].DefaultValue = _BPDPT_TYPE;
dsBPSNGrid.SelectParameters["BPSDY1"].DefaultValue = _BPSDY1;
dsBPSNGrid.SelectParameters["BPSDY2"].DefaultValue = _BPSDY2;
dsBPSNGrid.SelectParameters["BPEDY1"].DefaultValue = _BPEDY1;
dsBPSNGrid.SelectParameters["BPEDY2"].DefaultValue = _BPEDY2;
dsBPSNGrid.SelectParameters["canSearch"].DefaultValue = canSearch.ToString();
dsBPSNGrid.SelectParameters["userId"].DefaultValue = UserId;
if (!string.IsNullOrEmpty(GetStringParam("BPENO")) || !string.IsNullOrEmpty(WDAC.GetStringValue(ViewState["RPENO"])))
{
string bpeno = "";
if (!string.IsNullOrEmpty(GetStringParam("BPENO"))) bpeno = GetStringParam("BPENO");
if (!string.IsNullOrEmpty(WDAC.GetStringValue(ViewState["RPENO"]))) bpeno = WDAC.GetStringValue(ViewState["RPENO"]);
var cmdIndex = Query.NewCommand();
cmdIndex.CommandText = string.Format("SELECT ROWID - 1 FROM ( {0} ) T WHERE BPENO = @TARGET", dsBPSNGrid.SelectCommand);
Query.AddParam(cmdIndex, "TODAY", DateTime.UtcNow.AddHours(8).Date);
Query.AddParam(cmdIndex, "BPENO1", _BPENO);
Query.AddParam(cmdIndex, "BPNME1", _BPNME);
Query.AddParam(cmdIndex, "BPENM1", _BPENM);
Query.AddParam(cmdIndex, "BPIDN1", _BPIDN);
Query.AddParam(cmdIndex, "BPSDY1", _BPSDY1);
Query.AddParam(cmdIndex, "BPSDY2", _BPSDY2);
Query.AddParam(cmdIndex, "BPEDY1", _BPEDY1);
Query.AddParam(cmdIndex, "BPEDY2", _BPEDY2);
Query.AddParam(cmdIndex, "TARGET", bpeno);
var o = Query.ExecuteScalar(cmdIndex);
int idx = (o is Int32 || o is Int64) ? WDAC.GetInt32Value(o) : -1;
int idx = new BpsnDAC(Query).SelectBPENOIndex(
bpeno, _BPENO, _BPNME, _BPENM, _BPIDN, _BPEDY_TYPE, _BPDPT_TYPE, _BPSDY1, _BPSDY2, _BPEDY1, _BPEDY2, canSearch, UserId);
if (idx >= 0)
{
@@ -455,9 +394,9 @@ public partial class forms_worker_fmBpsn : Wellan.Web.UI.WUserControlBase
GridView1.DataBind();
}
protected void dsBPSNGrid_Selected(object sender, SqlDataSourceStatusEventArgs e)
protected void dsBPSNGrid_Selected(object sender, ObjectDataSourceStatusEventArgs e)
{
if (e.AffectedRows == 0)
if (e.ReturnValue is Int32 && WDAC.GetInt32Value(e.ReturnValue) == 0)
{
if (MultiView1.ActiveViewIndex == 0)
{