今天主要学习内容知识点回顾:Asp. NET Core 从入门到学会如何做一个安全可复用的框架
4.1 如何做一个好架构?
4.1.1.架构简介
由此可见,维度不同,架构也是不同的。
有人对软件架构这样定义:有关软件整体结构与组件的抽象描述,用于指导大型软件系统各个方面的设计,软件体系结构是构建计算机软件实践的基础。
我们可以把这句话理解为软件架构即软件系统的顶层设计结构。这句话基本上把系统、模块、组件和架构这些概念都串起来了。
简单来说架构规则如下:
- 架构是顶层设计。
- 框架是面向编程或配置的半成品。
- 组件是从技术维度上的复用。
- 模块是从业务维度上职责的划分。
- 系统是相互协同可运行的实体。
- 系统是由无数个子系统组成的,比如支付、理财、账单等都是独立的系统,我们可以称它们是支付宝的子系统。
- 子系统由无数个模块构建而成,比如理财包含了基金、保险这些功能模块,它们由无数公司提供的AP接口组合而成。
- 模块则由无数个组件组合而成,比如之前开发的账户体系模块,它由登录、注册和忘记密码等功能组件组合而成。
跟着看看老师的例子:(PS老师讲课讲的太深入,对于小白的我比较吃力)
- 具体的功能模块如下:学生模块、课程模块、教师模块、学院管理、统计信息。
- 涉及的知识点:数据分页、异步说明、查询,排序、实体状态说明、非跟踪查询的启用、并发冲突、实体间的继承、EF Core执行SQL语句、表关系。
4.2 泛型仓储的最佳实践落地
采用仓储模式的优势包括:使代码更清晰,更易于重用和维护;允许创建松耦合的系统。
那么接下来要添加很多实体,比如 Course、 Department和 Teacher等,要怎么建立仓储呢?
按照目前的方式,我们会针对每个实体都创建两个文件
- 一个为 Repository的接口文件
- 一个为 Repository的类文件
大问题???
如果有50个实体,那么就会创建100个文件,然而每个类中的实体所做的功能都差不多(都是增、删、改、査等基础功能)。如果要增加一个通用功能,比如统计总数,那么就要修改100次,这不仅枯燥乏味,还极易出错。另外,我们还要将它们全部注入Configure Services(()方法中,此时会发现,我们根本无法维护这个 Startup类文件。
4.3 依赖注入服务注册
针对上面的问题,ASPNET Core提供了依赖注入,通过它可以创建松耦合的系统。C#提供了泛型仓储,可以实现对参数、方法、服务的复用。将它们结合在一起,就可以使系统充分松耦合。
现在可以在路径为/ Infrastructure/ Repositories的文件夹中创建两个文件。
- IRepository.cs接口文件,此接口是所有仓储的约定,它仅作为约定,用于标识这些仓储
- Repository Base.CS类文件,默认仓储的通用功能实现,用于所有的领域模型
IRepository接口,代码说明
IRepository<TEntity, TPrimaryKey>
Entity指传入的实体信息,如 Student、 Course这些领域模型
TPrimarykey指传入的主键类型ID,如long、int和GUID类型
where TEntity:dlas:指泛型约束,用于约東传入的实体文件类型必须是类文件,要防止开发者误用这个仓储文件。
(未完待续 课程比较紧张,源码比较难)
4.4 如何在 ASP.NET COre中处理并发冲突?
- 什么是并发冲突
数据库并发指多个进程或用户同时访问或更改数据库中的相同数据触发,并发控制指的是在发生并发更改时确保数据一致性的特定机制。
并发冲突按照官方解释:当某用户显示实体数据以对其进行编辑,而另一用户在上一用户更改写入数据库之前更新同一实体的数据时,会发生并发冲突,并发要解决的场景是防止数据丢失。我们通过一个场景来了解它,A、B两个用户打开同一条数据并对它进行编辑,A用户编辑好信息,并且将其保存到数据库中。而B用户编辑好信息,在保存到数据库时会覆盖A用户的编辑信息,在大多数系统中,这是可以被接受的,但是在某些场景下则不行,如图所示的报名统计流程。
- 乐观锁与悲观锁的区别
悲观并发又称悲观锁,在还没有 EF Core的ORM框架之前,往往会采用传统Ado.Net提供的开发方式,大多数的应用程序为了防止在并发情况下岀现数据丢失,会将数据库锁定。具体操作是从数据库中读取一条数据之前,将它锁定为只读或更新状态。
- 只读状态时,其他用户可以读取数据但是不能对数据进行更新。
- 更新状态时,其他用户无法对该行数据进行读取或者修改。
而管理这些锁定存在的问题也很明显,编程代码会很复杂,而且它会占用大量的数据库的资源,随着用户数量的增加还会导致性能问题。出于某些原因,并不是所有的数据库驱动或ORM组件都支持悲观并发,比如 EF Core就不支持悲观并发。
乐观并发又名开放式并发,也称作乐观锁。它允许多个进程或用户独立进行更改而不产生数据库锁,以节省开销。在理想情况下,这些更改将互不干扰,因此都能够执行成功。悲观锁每次读取数据的时候都会对数据加锁,而乐观并发每次读取数据的时候都默认不加锁,只有在更新的时候才判断当前数据有没有更新。通常乐观锁会通过给数据添加版本号的记录机制实现,乐观锁适用于多读的应用类型,这样可以提高吞吐量。接下来我们会通过 EF Core实现乐观并发。
- 如何在我们的系统控制并发
RowVersion的值是时间戳,它会自动增加,后面我们会通过它的值来进行版本处理
在实现并发控制之前,我们先了解一下它在 EF Core中的工作原理。
配置并发令牌( RowVersion属性)可以控制乐观并发,每当 Save Changes()方法执行更新或删除操作时,我们会将数据库上的并发令牌值与 EF Core中读取的原始值进行比较。
- 如果 RowVersion值匹配,则可以完成该操作。
- 如果 RowVersion值不匹配,则 EF Core会假设另一个用户已执行冲突操作,并中止当前事务。
另一个用户已执行的操作与当前操作冲突的情况称为并发冲突。
- 而 Row Version值的比较由数据库提供程序( EF Core)来完成。在关系型数据库中, EF Core会对代码中所有包含 UPDATE或 DELETE语句及 WHERE条件中的子句的并发令牌值进行检查。
- 如果正在更新的行数据已被其他用户更改,导致 RowVersion列中的值与原始值不同,则包含UPDATE或 DELETE语句及 WHERE条件中的子句将找不到要更新的行数据。
- 最终返回的结果是未影响任何行数据,此时会触发并发冲突,导致 EF Core触发DbUpdate Concurrency Exception的异常,以此让开发者来解决此冲突。
触发异常后需要3组值来解决并发冲突。
- 当前值,即应用程序尝试写入数据库中的值。
- 原始值,最初从数据库中读取的值,未进行仼何编辑的数据。
- 数据库中的值,即当前值以及最初存储在数据库中的值。
4.5 使用 EF Core调用原生SQL语句
- EF Core中如何实现实体之间的继承
继承是面向对象编程的三大特征之一,通过继承可以复用基类的属性。目前我们在一些视图模型和实体中已经使用过继承了,如 StudentEditView Mode继承了 StudentCreate view Model。在本章我们通过将 Student与 Teacher实体的公共属性提取到 Person类中,来实现对 Person类的继承。
在 EF Core中继承有如下3种不同的实现方式
- TPH( Table Per Hierarchy):所有的数据都放在同一个表内,但是使用辨别标志(Discriminator的方式来区分,即通过 Discriminator与 Discriminator来进行区分。
- TPC( Table per Concrete-Type):由具体类型的表来存放各自的数据,而各自没有任何关联,继承的实体会包含基类中的所有属性。
- TPT( Table Per Type):表示每个对象各自独立产生表,这样各表之间就没有直接关联,要额外实现关联性才能产生关联,子实体通过实体D关联 Discriminator找到父类。
TPC和TPH继承模式的性能通常比τπT继承模式好,因为TPT模式会导致复杂的联接査询。但是截止到 Entity Framework Core31仅支持TPH继承。
- EF Core中如何执行原生SQL语句
目前我们通过 EF Core完成了一个较为完整的学校管理系统,在此期间我们没有像传统的开发者一样通过sαL语句来实现业务逻辑,但是并不是说 EF Core不支持SQL语句。 EF Core的优点之一是它可避免读者编写和数据库过于耦合的代码,它会动态生成sαL査询和命令(也称为动态SαL)。但有一些特殊情况,还是需要执行原生sQL语句。对于这些情况, EF Core1.0提供了相关的APl,可以帮助我们执行原生SQL语句。从 EF Core1.0开始就支持原生SαL语句的执行方法,而具体的方式有以下两种
使用 DbSet. FromSql返回实体类型的查询方法。返回的对象必须是 DbSet对象期望的类型,并且它们会自动跟踪数据库上下文,除非读者手动关闭跟踪。
对于非查询命令使用 Database. Execute SqlComma。
如果返回类型不是实体本身,而是视图模型,那么可以使用由 EF Core提供的 ADO.NET来进行数据库连接。请注意ADO.NET的数据库上下文不会跟踪返回的数据,而 EF Core会,这是两者的不同。
(未完待续 课程比较紧张,源码比较难)
配套源码:yoyomooc / MockSchool · GitLab https://code.52abp.com/yoyomooc/MockSchool