概述

需求分析总是 Web 项目搭建的第一步。通过需求分析,理清客户描述的业务逻辑,进而得到要构建的业务模型列表。

Web 程序的角色和职责

现代 Web 程序的核心组件通常是数据。通常,数据存储在数据库,数据仓库或其他数据管理系统中。我们 Web 程序的用户是数据的使用者。

理论上讲,如果所有用户都会直接使用数据库,那我们可能根本不必创建 Web 程序。我们可以直接把数据库连接和相应访问权限开放给他们,允许他们自己查询数据库。但现实是:

  1. 很大一部分用户不知道数据库是什么

  2. 向公众开放数据库连接信息是非常危险的

  3. 大多数用户只是进行一些在业务逻辑约束下的简单操作

因此,Web 程序成为了数据的包装器。

Web 程序应担负的职责包括:

  • 隐藏复杂的业务逻辑,例如表连接和数据验证

  • 简化数据操作为 UI 操作,例如单击超链接和提交表单

  • 通过身份验证和授权来控制数据访问,例如登录和用户组检查

  • 以人性化的界面展示数据,例如表格和图表

在下面的内容中,你将看到我们如何在简单的 ASP.NET Core Web 程序中履行这些职责。

需求分析

需求

需求比较简单,就是个管理产品的小程序:

  1. Web 程序用于产品管理

  2. 产品必须有 ID、名称、类型和价格

  3. 用户可以添加、删除、展示(列表)和更新产品

  4. 用户可以按类型和价格区间筛选产品

分析

通常,提出需求的用户并不是软件工程师。这意味着当他们提出他们的想法时,他们只关注他们真正想要的东西,很可能会忽略功能实现的优先级和顺序。此外,在大多数情况下,用户的需求描述不够全面,我们必须全面思考并与用户一同找出需求的缺失部分。

对于这个要求,我们就应该提出这些问题:

  1. 用户可以添加、删除、展示和更新产品类型吗?

  2. 如果用户可以删除产品类型,那如何处理该类型下的产品?

本示例中,用户的答案是:

  1. 是的,用户可以操作产品类型

  2. 产品类型下还包含产品时,就不允许删除产品类型。用数据库术语来说,这将创建孤立(orphaned)的记录

Model 设计

通过需求分析理清业务逻辑后,就可以开始设计模型类了。

ProductType Model

显然 ProductType 和 Product 之间有依赖关系,创建 Product 时必须明确它属于哪种 ProductType。所以此处要先设计 ProductType 模型类。

ProductType:

  1. namespace ProductManager.Models
  2. {
  3. public partial class ProductType
  4. {
  5. public int ID { get; set; }
  6. public string Name { get; set; }
  7. }
  8. public partial class ProductType
  9. {
  10. public IList<Product> Products { get; } = new List<Product>();
  11. public bool CanBeRemoved { get => Products.Count == 0; }
  12. }
  13. }

如你所见 ProductType 声明在 ProductManager.Models 命名空间,即 ProductType.cs 创建在 ~/Models 文件夹下,同理别的模型类例如 Product.cs 也将创建在此处。

仔细观察 ProductType 声明为了分部类(partial)。第一部分作为数据/实体映射数据库:

  1. public partial class ProductType
  2. {
  3. public int ID { get; set; }
  4. public string Name { get; set; }
  5. }

实际项目中这部分可以直接通过 EF 从数据库生成和更新。

注:本示例为了简化流程没有用到数据库,而是直接通过 C# 语句创建对象。

第二部分领域逻辑(domain logic)紧密相关,直接从现有数据计算得来。如果使用数据库,这部分也不会存储在 ProductType 表中,因为它会破坏数据库设计的规范并导致数据冗余。此外,这部分很可能会随着业务逻辑和 UI 逻辑而更迭。

Product Model

Product 依然是分部类:

  1. namespace ProductManager.Models
  2. {
  3. public partial class Product
  4. {
  5. public int ID { get; set; }
  6. public int TypeID { get; set; }
  7. public string Name { get; set; }
  8. public double Price { get; set; }
  9. }
  10. public partial class Product
  11. {
  12. public ProductType Type { get; set; }
  13. }
  14. }

数据关系

如你所见,ProductType 类中有 Products 属性,Product 类中有 Type 属性。即 ProductType 和 Product 之间的关系是一对多,也可以说 Product 与 ProductType 是多对一关系。

数据访问层

虽然数据访问层(Data Access Layer)不是本项目重点,但我们将通过它读取和写入数据,所以我们最好要对它有点了解。

DAL 可以理解为 CRUD 用的 API,本项目中通过 DataSource 类实现,它包含所有操作数据的静态方法。实际项目中的 DAL 通常通过 EF 或其他 ORM 框架实现。

DataSource.zip

一览 DAL 中操作数据的方法:

方法 功能
IList<Product> GetProducts() 获取所有 Product
Product GetProductByID(int id) 根据传入 ID 返回 Product,可能为 null
IList<Product> GetProductsByTypeID(int typeID) 返回类型为 typeID 的 Product 集合,可能为空集合
Product AddProduct(Product product) 添加一个新的 Product,同时为其指定一个合适的 ID
bool UpdateProductByID(int id, Product product) 更新指定 ID 的 Product,若无该 Product 则返回 false
bool RemoveProductByID(int id) 删除指定 ID 的 Procuct,若无该 Product 则返回false
IList<ProductType> GetProductTypes() 获取所有 Product Type
ProductType GetProductTypeByID(int id) 根据传入 ID 返回 Product Type,可能为 null
ProductType AddProductType(ProductType type) 添加一个新的 Product Type,同时为其指定一个合适的 ID
bool UpdateProductTypeByID(int id, ProductType type) 更新指定 ID 的 Product Type,若无该 Type 则返回 false
bool RemoveProductTypeByID(int id) 删除指定 ID 的 Procuct Type,若无该 Type 则返回false