概述
需求分析总是 Web 项目搭建的第一步。通过需求分析,理清客户描述的业务逻辑,进而得到要构建的业务模型列表。
Web 程序的角色和职责
现代 Web 程序的核心组件通常是数据。通常,数据存储在数据库,数据仓库或其他数据管理系统中。我们 Web 程序的用户是数据的使用者。
理论上讲,如果所有用户都会直接使用数据库,那我们可能根本不必创建 Web 程序。我们可以直接把数据库连接和相应访问权限开放给他们,允许他们自己查询数据库。但现实是:
很大一部分用户不知道数据库是什么
向公众开放数据库连接信息是非常危险的
大多数用户只是进行一些在业务逻辑约束下的简单操作
因此,Web 程序成为了数据的包装器。
Web 程序应担负的职责包括:
隐藏复杂的业务逻辑,例如表连接和数据验证
简化数据操作为 UI 操作,例如单击超链接和提交表单
通过身份验证和授权来控制数据访问,例如登录和用户组检查
以人性化的界面展示数据,例如表格和图表
在下面的内容中,你将看到我们如何在简单的 ASP.NET Core Web 程序中履行这些职责。
需求分析
需求
需求比较简单,就是个管理产品的小程序:
Web 程序用于产品管理
产品必须有 ID、名称、类型和价格
用户可以添加、删除、展示(列表)和更新产品
用户可以按类型和价格区间筛选产品
分析
通常,提出需求的用户并不是软件工程师。这意味着当他们提出他们的想法时,他们只关注他们真正想要的东西,很可能会忽略功能实现的优先级和顺序。此外,在大多数情况下,用户的需求描述不够全面,我们必须全面思考并与用户一同找出需求的缺失部分。
对于这个要求,我们就应该提出这些问题:
用户可以添加、删除、展示和更新产品类型吗?
如果用户可以删除产品类型,那如何处理该类型下的产品?
本示例中,用户的答案是:
是的,用户可以操作产品类型
产品类型下还包含产品时,就不允许删除产品类型。用数据库术语来说,这将创建孤立(orphaned)的记录
Model 设计
ProductType Model
显然 ProductType 和 Product 之间有依赖关系,创建 Product 时必须明确它属于哪种 ProductType。所以此处要先设计 ProductType 模型类。
ProductType:
namespace ProductManager.Models
{
public partial class ProductType
{
public int ID { get; set; }
public string Name { get; set; }
}
public partial class ProductType
{
public IList<Product> Products { get; } = new List<Product>();
public bool CanBeRemoved { get => Products.Count == 0; }
}
}
如你所见 ProductType 声明在 ProductManager.Models 命名空间,即 ProductType.cs 创建在 ~/Models 文件夹下,同理别的模型类例如 Product.cs 也将创建在此处。
仔细观察 ProductType 声明为了分部类(partial)。第一部分作为数据/实体映射数据库:
public partial class ProductType
{
public int ID { get; set; }
public string Name { get; set; }
}
实际项目中这部分可以直接通过 EF 从数据库生成和更新。
注:本示例为了简化流程没有用到数据库,而是直接通过 C# 语句创建对象。
第二部分与领域逻辑(domain logic)紧密相关,直接从现有数据计算得来。如果使用数据库,这部分也不会存储在 ProductType 表中,因为它会破坏数据库设计的规范并导致数据冗余。此外,这部分很可能会随着业务逻辑和 UI 逻辑而更迭。
Product Model
Product 依然是分部类:
namespace ProductManager.Models
{
public partial class Product
{
public int ID { get; set; }
public int TypeID { get; set; }
public string Name { get; set; }
public double Price { get; set; }
}
public partial class Product
{
public ProductType Type { get; set; }
}
}
数据关系
如你所见,ProductType 类中有 Products 属性,Product 类中有 Type 属性。即 ProductType 和 Product 之间的关系是一对多,也可以说 Product 与 ProductType 是多对一关系。
数据访问层
虽然数据访问层(Data Access Layer)不是本项目重点,但我们将通过它读取和写入数据,所以我们最好要对它有点了解。
DAL 可以理解为 CRUD 用的 API,本项目中通过 DataSource 类实现,它包含所有操作数据的静态方法。实际项目中的 DAL 通常通过 EF 或其他 ORM 框架实现。
一览 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 |