Skip to content

3. 案例研究

我们计划开发一个 .NET 应用程序,用于模拟某市“Maison de la petite enfance”协会中儿童保育人员的薪资计算。我们将同样重视应用程序 .NET 代码的结构组织与代码本身。

3.1. 数据库

生成工资单所需的静态数据存储在名为 dbpam(pam = Paie Assistante Maternelle)的 SQL Server Express 数据库中。该数据库的管理员名为 sa,密码为 msde

 

该数据库包含三个表:EMPLOYEES、CONTRIBUTIONSALLOWANCES,其结构如下:


Table EMPLOYES : rassemble des informations sur les différentes assistantes maternelles

结构

SS
员工的社会保险号 - 主键
姓名
员工姓氏
名字
名字
地址
收件地址
城市
他的/她的城市
邮政编码
他的/她的邮政编码
索引
其处理索引 - [INDEMNITES] 表中 [INDICE] 字段的外键

其内容可能如下:

Image


Table COTISATIONS : rassemble les taux des cotisations sociales prélevées sur le salaire

结构

其内容可如下所示:

社会保险缴费率与员工无关。前面的表格只有一行。


Table INDEMNITES : rassemble les différentes indemnités dépendant de l'indice de l'employé
指数
薪资指数 - 主键
BASEHOUR
欧元净时薪
每日维护
每日待命津贴(欧元/天)
每日餐费
护理期间每日餐费补贴(欧元)
带薪休假
带薪休假津贴。该津贴按基本工资的一定比例计算。

其内容可能如下:

Image

请注意,不同托儿服务提供者的津贴可能有所不同。这些津贴通过该提供者的薪级与特定托儿服务提供者相关联。因此,薪级为2(见“员工”表)的玛丽·朱维纳尔女士,其时薪为2.1欧元(见“津贴”表)。

这三张表之间的关系如下:

EMPLOYEES(INDEX) 列与 ALLOWANCES(INDEX) 列之间存在外键关系。

通过这种方式创建的 [dbpam] 数据库会在 SQL Server Express 文件夹中生成两个文件:

可以将 [dbpam.mdf, dbpam_log.ldf] 文件传输到另一台计算机,并将其重新附加到该计算机的 SQL Server Express 数据库管理系统中。具体操作如下:

  • 将 [dbpam] 数据库文件复制到一个文件夹中
 
  • 启动 SQL Server Express
  • 使用 SQL Server Management Studio Express 客户端,将 [dbpam.mdf] 文件附加到数据库:
  1. 右键单击 [数据库] / 附加
  2. 使用 [添加] 按钮(未显示)选择 [dbpam.mdf] 文件
  3. 所附加的文件将创建一个数据库,该数据库不能已存在。在此,我们在 [附加为] 字段中将新数据库命名为 [dbpam2]。
  4. 您可以看到新数据库及其表

这种附加数据库的方法对于将数据库从一台计算机转移到另一台计算机非常有用,我们在此也会偶尔使用它。

3.2. 保育员薪资的计算方法

接下来我们将介绍计算保育员月薪的方法。以玛丽·朱维纳尔女士为例,她在该薪资周期内工作了20天,共计150小时。

计算时需考虑以下因素:

[TOTALHOURS]:总
 工作总时长
 本月

[TOTALDAYS]: 总天数
 当月工作天数
[TOTALHOURS]=150
[TOTALDAYS] = 20
保育员的基本工资按以下公式计算:
[BASESALARY]=([TOTALHOURS]*
[HOURLYRATE])*(1+
[CP津贴]/100)
[BASESALARY]=(150*[2.1])*(1+0.15)= 362.25
必须从该基本工资中扣除若干社会保险费:

一般社会保险费
 及债务
 用于债务偿还
[BASESALARY]*[CSGRDS/100]

免赔额
 扣除额:
 [BASESALARY]*[CSGD/100]

社会保障、遗属、
 养老保险:
 [基本工资]*[SECU/100]
补充养老金 +
AGPF + 失业保险:
[基本工资]*[养老金/100]
CSGRDS:12.64
CSGD:22.28
社会保障:34.02
养老金:28.55
社会保障缴费总额:
[SOCIALCONTRIBUTIONS] = [SALARY
EBASE]*(CSGRDS+CSGD+SECU+RETR
[SOCIALCONTRIBUTIONS]=97.48
此外,保育员有权获得每日生活津贴和餐费津贴。因此,她可获得以下津贴:
[薪酬] = [总天数] * (EN
每日津贴 + 每日餐费津贴)
[津贴]=104
最终,应支付给保育员的净薪资如下:
[基本工资]-
[社会保险费]+
[津贴]
[净薪]=368.77

3.3. ADO.NET 提示

薪资计算应用程序需要从 [dbpam] 数据库中获取信息。其架构如下:

  • 在 [1] 中,用户发起请求
  • 在 [2],薪资应用程序处理该请求。
  • 随后,它可能需要从数据库中获取数据。接着,它向所用数据库管理系统(DBMS)的 ADO.NET 提供程序发送查询 [4]。
  • 提供程序访问数据库 [5],并将结果返回给 ADO.NET 提供程序,后者再将结果传递回应用程序
  • 应用程序处理这些结果,并为用户生成响应 [5]

接下来,我们将回顾 ADO.NET 提供程序向其客户端提供的主要接口 [3]。

连接模式下,应用程序:

  1. 打开与数据源的连接
  2. 以读写模式操作数据源
  3. 关闭连接

这些操作主要涉及三个 ADO.NET 接口:

  • IDbConnection,封装了连接的属性和方法。
  • IDbCommand,封装了已执行 SQL 命令的属性和方法。
  • IDataReader,封装了 SQL SELECT 语句结果的属性和方法。

IDbConnection 接口

用于管理与数据库的连接。该接口的方法 (M) 和属性 (P) 包括以下内容:

名称
类型
角色
ConnectionString
P
数据库连接字符串。它指定了与特定数据库建立连接所需的所有参数。
打开
M
打开与由ConnectionString定义的数据库的连接
关闭
M
关闭连接
BeginTransaction
M
开始事务。
状态
P
连接状态:ConnectionState.ClosedConnectionState.OpenConnectionState.ConnectingConnectionState.ExecutingConnectionState.FetchingConnectionState.Broken

如果 Connection 是一个实现了 IDbConnection 接口的类,则可以按以下方式打开连接:

1
2
3
IDbConnection connexion=new Connection();
connexion.ConnectionString=...;
connexion.Open();

IDbCommand 接口

用于执行 SQL 语句或存储过程。该接口的方法 M 和属性 P 包括以下内容:

名称
类型
角色
CommandType
P
指定要执行的内容 - 其值取自一个枚举:
- CommandType.Text:执行在 CommandText 属性中定义的 SQL 语句。这是默认值。
- CommandType.StoredProcedure:执行数据库中的存储过程
CommandText
P
- 如果 CommandType= CommandType.Text,则为要执行的 SQL 语句的文本
- 若 CommandType=CommandType.StoredProcedure,则为要执行的存储过程的名称
连接
P
用于执行 SQL 语句的 IDbConnection 连接
事务
P
用于执行 SQL 语句的 IDbTransaction 事务
参数
P
带参数 SQL 语句的参数列表。语句 `update articles set price=price*1.1 where id=@id` 包含参数 `@id`。
ExecuteReader
M
用于执行 SELECT SQL 语句。此方法返回一个 IDataReader 对象,该对象表示 SELECT 语句的结果。
ExecuteNonQuery
M
用于执行 SQL Update、Insert 或 Delete 语句。该方法返回受操作影响的行数(即更新、插入或删除的行数)。
ExecuteScalar
M
用于执行返回单个结果的 SQL SELECT 语句,例如:select count(*) from articles
CreateParameter
M
用于创建参数化 SQL 语句的 IDbParameter 参数。
Prepare
M
可让您在参数化查询使用不同参数多次执行时,优化其执行效率。

如果 Command 是一个实现了 IDbCommand 接口的类,则不使用事务执行 SQL 语句将采用以下形式:

// ouverture connexion 
IDbConnection connexion=...
connexion.Open();
// préparation commande
IDbCommand commande=new Command();
commande.Connection=connexion;
// exécution ordre select
commande.CommandText="select ...";
IDbDataReader reader=commande.ExecuteReader();
...
// exécution ordre update, insert, delete
commande.CommandText="insert ...";
int nbLignesInsérées=commande.ExecuteNonQuery();
...
// fermeture connexion
connexion.Close();

IDataReader 接口

用于封装 SQL SELECT 语句的结果。一个 IDataReader 对象表示一个包含行和列的表,这些行和列将按顺序处理:先处理第一行,然后是第二行,依此类推。该接口的方法 (M) 和属性 (P) 包括以下内容:

名称
类型
作用
FieldCount
P
IDataReader 表中的列数
GetName
M
GetName(i) 返回 IDataReader 表中第 i 列的名称。
项目
P
Item[i] 代表 IDataReader 表中当前行的第 i 列。
读取
M
移至 IDataReader 表中的下一行。如果行已成功读取,则返回 True,否则返回 False
关闭
M
关闭 IDataReader 表。
GetBoolean
M
GetBoolean(i):返回 IDataReader 表当前行中第 i 列的布尔值。其他类似的方法包括:GetDateTimeGetDecimalGetDoubleGetFloatGetInt16GetInt32GetInt64GetString
Getvalue
M
Getvalue(i):将 IDataReader 表当前行中第 i 列的值作为对象类型返回。
IsDBNull
M
如果 IDataReader 表中当前行的第 i 列没有值(由 SQL NULL 值表示),则 IsDBNull(i) 返回 True

使用 IDataReader 对象通常如下所示:

// ouverture connexion 
IDbConnection connexion=...
connexion.Open();
// préparation commande
IDbCommand commande=new Command();
commande.Connection=connexion;
// exécution ordre select
commande.CommandText="select ...";
IDataReader reader=commande.ExecuteReader();
// exploitation résultats
while(reader.Read()){
    // exploiter ligne courante
        ...
}
// fermeture reader
reader.Close();
// fermeture connexion
connexion.Close();