Web开发:ABP框架5——入门级别的常见问题和报错解析

目录

一、常见问题

1. Swagger界面找不到接口

2.注释不显示

步骤一:新建xml输出

步骤二:代码引用xml文件

 3.日期格式化带T

4.输出变量格式化(小驼峰->大驼峰)

二、报错解析

1.Swagger界面报错

 2.底层代码调用失败

3.仓储引用失败 

三、通用错误排查方法

1.查控制台程序输出

2.查日志

四、疑难解答

1. 两个仓储的区别

 2.前端传入枚举,让传参更清晰

3.ABP和 Volo.ABP区别

4.ABP框架下实体映射的两种方式

五、开发人员对接口测试流程 

六、EFcore疑难专项

1.主键配置

2.映射失败的几大原因

3.任务中途取消

4.派生类型无法配置密钥

5.实体继承 Entity和继承AuditedAggregateRoot的区别

 6.EFcore如何打印sql

7.EFcore实体和数据库的对接错误


一、常见问题

1. Swagger界面找不到接口

  • 代码问题:WebApi控制器(服务)没有继承ApplicationService
  • 缓存问题:给浏览器清理下缓存,重启VS2022

2.注释不显示

步骤一:新建xml输出

方法一:哪个类库不显示,改对应类库的工程文件

    <GenerateDocumentationFile>true</GenerateDocumentationFile>
    <DocumentationFile>bin\Debug\net6.0\Acme.BookStore.HttpApi.Host.xml</DocumentationFile>

方法二: 对应类库的右键属性设置

 输入框填写的内容应该和工程文件种的一致:

bin\Debug\net6.0\Acme.BookStore.HttpApi.Host.xml

步骤二:代码引用xml文件

        API模块种补上代码,当然也可以直接字符串写死  xmlFile = " Acme.BookStore.HttpApi.Host.xml " ,怎么命名都好,总之,xmlFile这个文件名和上面步骤一设置的文件名一致。

private static void ConfigureSwaggerServices(ServiceConfigurationContext context, IConfiguration configuration)
{
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";//"Acme.BookStore.HttpApi.Host.xml"
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);

    context.Services.AddAbpSwaggerGenWithOAuth(
        configuration["AuthServer:Authority"],
        new Dictionary<string, string>
        {
                {"BookStore", "BookStore API"}
        },
        options =>
        {
            options.SwaggerDoc("v1", new OpenApiInfo { Title = "BookStore API", Version = "v1" });
            options.DocInclusionPredicate((docName, description) => true);
            options.CustomSchemaIds(type => type.FullName);
            options.IncludeXmlComments(xmlPath); // 添加这一行
        });
}

修改后的效果:

注意:工程文件只需要配置对应的应用层即可!不要再配置Host,否则会覆盖你应用层生成的xml。

 3.日期格式化带T

需求如下,输出的日期带T,我们不需要显示T

代码编写位置 

 加上这个:

Configure<AbpJsonOptions>(options =>
{
    options.DefaultDateTimeFormat = "yyyy-MM-dd HH:mm:ss";
});

如果你想全局配置,那也可以将上述代码加在Program.cs这里,所有模块都会实现这样的效果:

        此外,也可以通过 编写 WebAPI 的拦截器 处理这个问题。 

(注意:我觉得日期格式化,虽然上面的方法可以解决问题,但是始终是不够灵活,因为有时候返回出去的是yyyy-MM-dd而不是完整的时间,因此我觉得用tostring方法手动调整,返回字符串出去 或者让前端处理,或许会更好) 

4.输出变量格式化(小驼峰->大驼峰)

方法一:全局配置

builder.Services.AddMvc().AddJsonOptions(options =>
{
    options.JsonSerializerOptions.PropertyNamingPolicy = null;
});

 效果:

方法二:每个字段都用特性配置

例如:


 效果:

二、报错解析

1.Swagger界面报错

错误:Fetch errorInternal Server Error /swagger/v1/swagger.json

方法:查看控制台程序报错情况

可能情况:

  1. WebApi控制器(高层代码服务,即继承了ApplicationService的服务)某个方法使用了public 或者 static 这些修饰符,但是没有[HTTPGet]或其他请求标识特性,ABP 框架默认将其视为接口,因此会报错。(解决方法:①加上请求特性,用作高层服务接口;②或者用private(不加 static)修饰符声明私有方法,仅供本服务使用,不作为对外开放接口;③不加路由特性且不继承ApplicationService,仅实现ITransientDependency作为底层服务,供高层服务使用)
  2. WebApi控制器(服务)出现路由同名现象,引发冲突。

 2.Castle.Proxies依赖注入问题

问题一:

错误:Castle.Proxies.BookAppServiceProxy

原因1:底层代码接口未注入到依赖容器,服务找不到这个底层代码接口

解决方案:底层代码继承ApplicationService或实现ITransientDependency

 原因2:EFcore未设置Dbset,却通过EFcore仓储查表 

 解决方案:设置好Dbset即可

问题二:

{
  "code": null,
  "message": "An internal error occurred during your request!",
  "details": null,
  "data": {
    "ActivatorChain": "Castle.Proxies.StudentServiceProxy -> Application.TreeSelect.TeacherService -> Application.StudentService"
  },
  "validationErrors": null
}

Web开发:ABP框架中的服务调用原则--避免服务之间的直接依赖_abp 服务之间调用-CSDN博客

3.仓储引用失败 

报错信息:“Acme.BookStore.Books.Book”不能用作泛型类型或方法“IRepository<TEntity, TKey>”中的类型参数“TEntity”。没有从“Acme.BookStore.Books.Book”到“Volo.Abp.Domain.Entities.IEntity<System.Guid>”的隐式引用转换。

 原因:实体类没有继承AuditedAggregateRoot<Guid>或者AggregateRoot<Guid>或者DOEntity,因为IRepository<TEntity, TKey>需要继承自 IEntity<TKey> 接口。

三、通用错误排查方法

1.查控制台程序输出

2.查日志

  • 位置参考:\src\xxxx.HttpApi.Host\Logs

四、疑难解答

1. 两个仓储的区别

        一个指定了主键类型,调用GetAsync(根据id查表)、DeleteAsync(根据id删数据)等方法时,直接传入对应类型的id即可,否则需要传入LINQ的表达式树

private readonly IRepository<Book, Guid> _bookRepository;//await _bookRepository.DeleteAsync(id);
private readonly IRepository<Book> _bookRepository;//await _bookRepository.DeleteAsync(x=>x.Id==Guid.NewGuid());

 2.前端传入枚举,让传参更清晰

//前端传入方式:post请求或者get请求直接传一个枚举值(string) 或者 索引(int) 都可(例如querytype="Grade1",或者querytype=1)

//接收前端的实体:
public class SearchInput
{
   //可以写其他属性
   public StudentEnum querytype { set; get; }
}

//枚举示例:
public enum StudentEnum
{
    Grade1 = 1,
    Grade2 = 2,
    Grade3 = 3
}

//后端可以这样判断:
bool isgrade1 =  input.querytype == StudentEnum.Grade1;// 上述两种入参,都可以输出True

3.ABP和 Volo.ABP区别

        ABP和 Volo.ABP 是两个不同的框架,后者版本更加新,有基于最新的 ASP.NET Core 技术栈且ORM-EFcore采用高版本的。

4.ABP框架下的AutoMapper

Web开发:使用Abp.AutoMapper进行实体映射的demo_abp automapper-CSDN博客

五、开发人员对接口测试流程 

  1. 检索条件是否生效
  2. 分页效果(测试第一到第三页)
  3. 比对数据
  4. 查看控制台程序和日志是否存在报错现象

【完善】排序、模糊搜索

六、EFcore疑难专项

1.主键配置

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<xxxModel>(b => 
    {
        b.ConfigureByConvention();
        // b.HasKey(x => new { x.Id }); // 其实不需要显式指定主键,只要你的实体中含有属性名称为Id的属性,EF Core 会自动识别 Id 为主键(注意不可识别id)
    });
}

        不需要显式指定主键,只要你的实体中含有属性名称为Id的属性,EF Core 会自动识别 Id 为主键(注意不可识别id,是区分大小写的)

2.映射失败的几大原因

  1. 配置问题:Dbcontext写错字段类型,例如NUMBER(8,2)写成了NUMBER(8),如果高级版本采用ConfigureByConvention配置,不存在这个问题。
  2. 入参问题:前端传入和后端接收字段类型不一致,例如Guid和string
  3. 映射关系问题:没有创建CreateMap,导致映射失败(Mapping failed)

3.任务中途取消

报错:A Task was canceled

Update操作:根据传递实体的主键,查不到数据(检查主键是否正确)

4.派生类型无法配置密钥

A key cannot be configured on 'xxxx' because it is a derived type

实体字段配置问题:数据库实体不能继承另一个实体

反例:

public class A 
{


}
public class B : A
{


}

正例:

//抽取公共字段出来,写在Common类中
public class A : Common
{


}
public class B : Common
{


}

5.实体继承 Entity<int>和继承AuditedAggregateRoot<id>的区别

讲直白点:

①仓储查表

        必须继承一个,才能使用下述方式(注入仓储)来通过EFcore查表

private readonly IRepository<Book, Guid> _bookRepository;

②少写字段

        继承Entity<int> ,实体可以少写一个 Id 字段
        继承AuditedAggregateRoot<int>,实体可以少写很多字段例如创建时间、修改时间、操作用户等

③查表拼接

        查表会拼接你这个实体所有的字段,包括继承的,这说明强制要求我们的表主键字段含有Id,否则就会出错。

        不过也可以强制修改,例如我有一个表,继承了Entity<int>:

        查表生成的sql是这样的:

SELECT Id,[pid],[pname] FROM [TestABP].[dbo].[Parent]

         我没有Id字段,就会导致查这张表失败,解决方案二选一如下:

        ①改名,pid改为Id(不区分大小写,改成id Id ID iD都可以)

        ②忽略Id字段,让EFcore拼接查询sql的时候忽略它,以下两个步骤都必不可少,没有主键标记EFcore也会报错:

        builder.Entity<Parent>(b =>
        {
            //b.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema);

            
            b.HasKey(x => x.pid);// 声明本表主键【步骤一】
            b.Ignore(x => x.Id); //忽略继承的主键【步骤二】
            
            b.ConfigureByConvention();// 自动配置基本属性
        });

        说了那么多,建议还是按它们那套标准来:建表必定含有Id字段作为主键,然后实体继承了Entity<int>后就不要重复写id字段了(重复写id字段可以查表,但是EFcore不支持这样映射,还是会报如下的错)。

 6.EFcore如何打印sql

在上下文类加上以下代码即可:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
   optionsBuilder
    // 输出到 VS 输出窗口
    .LogTo(msg => System.Diagnostics.Trace.WriteLine(msg), new[] { Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.CommandExecuting });

}

7.EFcore实体和数据库的对接错误

常见错误如下(以下表格情况是在实体设置好dbset情况下进行的):

结果原因解决方案
实体多一个字段报错查表会拼接无关字段,SQLsever报错

1.实体删除无关字段

2.上下文类忽略该字段(二选一)

实体少一个字段正常列表结果没有这个缺失的字段而已
实体字段名一样类型不同报错        查表正常,EFcore转化到实体时异常修改对应字段名的类型(例如把string改为int)
有实体无表报错     查表出错,SQLsever报错建表
有表无实体正常       没有实体=不会查表,不会查表=不会报错

通用解决方法:查表不用EFcore

(吐槽一下:EFcore又难用,拼出来的SQL又复杂,还不如用主流的ORM框架!)

ABPFrameWork开发指南 入门文档 目 录 1 ABP总体介绍 1 1.1 入门介绍 1 1.1.1 ABP采用了以下技术 2 1.1.2 ABP框架已实现了以下特性 2 1.1.3 ABP适用的场景 4 1.2 多层架构体系 4 1.2.1 前言 4 1.2.2 ABP的体系结构 5 1.2.3 领域层 5 1.2.4 应用层 6 1.2.5 基础设施层 6 1.2.6 WEB与展现层 6 1.2.7 其它 7 1.3 模块系统 7 1.3.1 ABP模块系统简介 7 1.3.2 生命期事件 8 1.3.3 模块依赖 9 1.3.4 自定义的模块方法 10 1.4 启动配置 11 1.4.1 配置ABP 11 1.4.2 配置模块 13 1.4.3 为一个模块创建配置 13 2 ABP公共结构 16 2.1 ABP依赖注入 16 2.1.1 传统方式的问题 16 2.1.2 解决方案 18 2.1.3 依赖注入框架 20 2.1.4 ABP依赖注入的基础结构 21 2.1.5 附件 25 2.2 ABP会话管理 26 2.2.1 简介 26 2.2.2 注入会话 27 2.2.3 使用会话属性 27 2.3 ABP日志管理 28 2.3.1 服务器端 28 2.3.2 客户端 32 2.4 ABP设置管理 32 2.4.1 介绍 32 2.4.2 定义设置 33 2.4.3 设置范围 34 2.4.4 获取设置值 35 2.4.5 更改设置 36 2.4.6 关于缓存 36 3 ABP领域层 37 3.1 ABP领域层—实体 37 3.1.1 实体类 37 3.1.2 接口约定 38 3.1.3 IEntity接口 41 3.2 ABP领域层—仓储 42 3.2.1 IRepository接口 42 3.2.2 仓储的实现 47 3.2.3 管理数据库连接 48 3.2.4 仓储的生命周期 48 3.2.5 仓储的最佳实践 48 3.3 ABP领域层—工作单元 49 3.3.1 通用连接事务管理方法 49 3.3.2 ABP的连接事务管理 50 3.3.3 工作单元 53 3.3.4 选项 56 3.3.5 方法 57 3.3.6 事件 57 3.4 ABP领域层—数据过滤器 58 3.4.1 介绍 58 3.4.2 预定义过滤器 58 3.4.3 禁用过滤器 60 3.4.4 启用过滤器 61 3.4.5 设定过滤器参数 62 3.4.6 自定义过滤器 62 3.4.7 其它对象关系映射工具 64 3.5 ABP领域层—领域事件 64 3.5.1 事件总线 64 3.5.2 定义事件 65 3.5.3 触发事件 65 3.5.4 事件处理 66 3.5.5 注册处理器 68 3.5.6 取消注册事件 69 4 ABP应用层 71 4.1 ABP应用层—应用服务 71 4.1.1 IApplicationService接口 71 4.1.2 应用服务类型 73 4.1.3 工作单元 74 4.1.4 应用服务的生命周期 76 4.2 ABP应用层—数据传输对象 76 4.2.1 数据传输对象的作用 76 4.2.2 DTO 约定 & 验证 78 4.2.3 DTO实体间的自动映射 80 4.2.4 辅助接口类型 82 4.3 ABP应用层—DTO有效性验证 82 4.3.1 使用数据注解 83 4.3.2 自定义检验 84 4.3.3 设置缺省值 85 4.4 ABP应用层—权限认证 86 4.4.1 定义权限 86 4.4.2 检查权限 87 4.5 ABP应用层—审计日志 90 4.5.1 配置 91 4.5.2 通过属性来启用禁用审计日志 92 4.5.3 说明 93 5 ABP表现层 94 5.1 ABP展现层—动态WebApi层 94 5.1.1 建立动态web api 控制器 94 5.1.2 使用动态js代理 95 5.2 ABP展现层—本地化 97 5.2.1 程序语言 97 5.2.2 本地化源文件 98 5.2.3 获得一个本地化配置文件 100 5.2.4 总结 103 5.3 ABP展现层—Javascript函数库 103 5.3.1 AJAX 103 5.3.2 通知 107 5.3.3 消息 107 5.3.4 用户界面的繁忙提示 109 5.3.5 Js日志接口 110 5.3.6 Javascript公共方法 111 5.4 ABP展现层—导航栏 112 5.4.1 创建菜单 112 5.4.2 显示菜单 114 5.5 ABP展现层—异常处理 114 5.5.1 开启错误处理 115 5.5.2 非Ajax请求 115 5.5.3 AJAX请求 117 5.5.4 异常事件 118 5.6 ABP展现层—嵌入资源文件 118 6 ABP基础设施层 119 6.1 ABP基础设施层—集成Entity Framework 119 6.1.1 Nuget包 119 6.1.2 创建DbContext 119 6.1.3 仓储 120 6.2 ABP基础设施层—集成NHibernate 124 6.2.1 Nuget包 125 6.2.2 配置 125 6.2.3 仓储实现 127 7 ABP实例一:ASP.NET Boilerplate 131 7.1 引子 131 7.2 什么是ASP.Net Boilerplate? 132 7.3 ABP不适用于那些场合? 133 7.4 开始 134 7.5 使用模板创建空的网站应用程序 134 7.6 领域层 136 7.6.1 实体-Entities 137 7.6.2 仓储-Repository 138 7.6.3 关于命名空间 139 7.7 基础设施层 139 7.7.1 数据库迁移 139 7.7.2 实体映射 143 7.7.3 仓储实现 143 7.8 应用层 145 7.8.1 应用服务及数据传输对象 145 7.8.2 DTO验证 149 7.8.3 动态Web API控制器 150 7.9 表现层 151 7.9.1 单页应用 152 7.9.2 视图视图模型 152 7.9.3 本地化 160 7.9.4 JavaScript API 162 7.10 更多 164 7.10.1 模块系统 164 7.10.2 依赖注入约定 165 7.11 结论 165 8 ABP实例二:单页面网站应用程序 167 8.1 简介 167 8.2 基于Abp创建应用程序 168 8.3 创建实体 169 8.4 创建DbContext 170 8.5 创建Database Migrations 171 8.6 定义仓储 173 8.7 实现仓储 174 8.8 构建应用程序服务 175 8.9 验证 179 8.10 异常处理 180 8.11 构建Web API 服务 181 8.12 开发单页面应用(SPA) 181 8.13 本地化 186
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值