ASP.NET8.0入门与实战
1、项目初始化
创建一个ASP.NET Core Web API的项目,取消Https和身份验证。
API项目实际上是一个控制台程序,这点可以在项目的属性的输出类型中看到。
launchSettings.json,在这里可以配置运行项目的名称,端口号,路径等。
在Program.cs文件中对项目的构造器和中间件进行了配置,首先是创建构造器,添加控制器,终结点,Swagger,构建构造器。在中间件模块里首先是判断是否是开发模式,如果是的话便开始注册Swagger和它的UI界面,授权,路由映射。
最后是运行项目,在Swagger中调用接口可以看到返回值,如果是简单的项目比如一个只有OCR功能的项目是不需要另外再创建其他项目的,除非是一个比较复杂的才需要创建多个层级的项目。
2、仓储+服务架构模式讲解
这一节开始搭建项目,搭建项目很重要,会影响日常开发和维护的流程,这里采用的是这种架构:
1、接口层(控制器)暴露在最外面,由它去调用服务层;
2、服务层是个中转站,它会去调用仓储层拿到数据库实体,并映射为数据模型对象Vo,这里的Vo应该与仓储层的数据库实体有差异,避免暴露一些关键信息,比如隐藏ID,修改字段名;另外一个好处是假如数据库的数据做了迁移,需要修改字段名,只需要修改数据实体即可,无需修改Vo,前端是不用动的,减少了开发量。
3、仓储层与数据库做交互,拿到数据库实体,它也会去调用公共层的代码,现在在底层引用了一个类库后,引用它的项目的引用的项目也能使用这个类库,就很方便不用重复引用了,而且避免出现不同的版本。
还有就是在服务层和仓储层都定义了一套实现类与接口类,设计到延迟的功能都使用了异步和等待。
Mock了一个数据 = 造假数据,具体来说就是将定义好的数据写到程序中,作为固定返回,C#中有专门做假数据的类库。
3、泛型基类的妙用
这一节在仓储层和服务层定义了一对抽象化的类与接口,IBaseRepository和BaseRepository,通过往里面传入泛型的方式简化了类和接口的创建,但这样写是无法处理复杂的业务逻辑的。
public interface IBaseRepository<TEntity> where TEntity : class{Task<List<TEntity>> Query();}
public class BaseRepository<TEntity> : IBaseRepository<TEntity> where TEntity : class, new(){public async Task<List<TEntity>> Query(){await Task.CompletedTask;var str = "[{\"Id\":1, \"Name\":\"李四\"}]";var list = Newtonsoft.Json.JsonConvert.DeserializeObject<List<TEntity>>(str) ?? new List<TEntity>();return list;}}
4、泛型对象的关系映射
由于上一节最终返回的是一个数据模型而不是视图模型,所以需要用到AutoMapper让两种类型实现一次配置后自动转换。
具体步骤:
1、下载Nuget包,AutoMapper 12.0.1,AutoMapper.Extensions.Microsoft.DependencyInjection 12.0.0
2、创建配置文件CustomProfile,可以看到里面是对两个类里面属性的一个映射,CreateMap的第一个是起始类Role,第二个是目标类RoleVo,值得注意的是ForMember的第一个参数却是目标类RoleVo的属性RoleName,第二个参数是起始类Role的属性
public class CustomProfile : Profile{public CustomProfile(){CreateMap<Role, RoleVo>().ForMember(a => a.RoleName, o => o.MapFrom(d => d.Name));CreateMap<RoleVo, Role>().ForMember(a => a.Name, o => o.MapFrom(d => d.RoleName));}}
另外还需要创建一个AutoMapper的配置文件AutoMapperConfig,这个在后面的注入环节会用到。
3、在Program的Main函数中对AutoMapper进行构建
builder.Services.AddAutoMapper(typeof(AutoMapperConfig));AutoMapperConfig.RegisterMappings();
然后在服务层使用它,这里需要进行依赖注入,然后使用AutoMapper的Map方法,泛型是目标类,入参是起始类。
public class BaseService<TEntity, TVo> : IBaseService<TEntity, TVo> where TEntity : class, new(){public readonly IMapper _mapper;public BaseService(IMapper mapper){_mapper = mapper;}public async Task<List<TVo>> Query(){var baseRepository = new BaseRepository<TEntity>();var entities = await baseRepository.Query();return _mapper.Map<List<TVo>>(entities);}}
5、依赖注入
虽然使用依赖注入需要进行额外的配置,但好处多多,比如不用担心内存泄漏,何时被GC回收。
在Main函数中对创建的类进行注册有三种模式:Singleton、Scoped、Transient
Singleton可以理解为单例模式,只有一个实例,是全局的。
Scoped可以理解为在一次请求中用到的是同一个实例,例如:在一个Action中,如果两次用到同一个实例,在这种模式下将会是相同的。
Transient可以理解为瞬时的,例如::在一个Action中,如果两次用到同一个实例,在这种模式下将会是不同的。
最常用的还是Scoped模式,在ASP.NET中在Action中使用new关键字创建的对象的时候也是这种方式。
ASP.NET Core和ASP.NET一个很明显的区别就在于应尽量避免去new一个对象,而是通过依赖注入来获得它。
具体步骤:
1、在Main函数注入服务类
builder.Services.AddScoped(typeof(IBaseService<,>), typeof(BaseService<,>));
2、在Controller中通过构造函数拿到它,然后在Action中使用