Abp 创建一个模版demo并运行

news2025/2/25 18:04:34

Demo效果 :简单的单表crud后台服务。不包含UI

项目类型是模块ABP。生成的结构和 多应用/单应用 有差异。
结合文档以及git的源码分享一下demo的理解

abp文档:API/Auto API Controllers | Documentation Center | ABP.IO
前置准备:
Net8 环境:下载 .NET 8.0 (Linux、macOS 和 Windows)
Vs2022 17.8版本以上

1.根据指令生成模版

2.项目结构如下

还有个test文件夹我移除了


首先我们要知道各个模块主要干嘛的

领域层
domian :
基本的领域层,它包含前面介绍的所有构建块(实体、值对象、域服务、规范、存储库接口等)
domian.Shared:它包含一些属于领域层的类型,但与所有其他层共享。例如,它可能包含一些与领域对象相关的常量和枚举,但需要被其他层重用

应用层
Application: 
是实现Contracts项目中定义的接口的基本应用程序层 (可以直接将应用层生成接口)
Application.Contracts: 包含应用程序服务接口和这些接口使用的dto。这个项目可以被客户端应用程序共享(包括UI)

基础服务层 
EntityFrameworkCore:它是EF Core的基本集成包。你的应用程序的DbContext,数据库映射,存储库的实现和其他EF Core相关的东西都在这里

表示层 (本次demo没有ui 没这一层)
Web:
 是一个ASP.NET Core MVC / Razor Pages 应用程序, 这是唯一为应用程序和api服务的可执行应用程序

远程服务层
HttpApi:项目包含解决方案定义的HTTP接口。它通常包含MVC控制器和相关模型。因此,您可以在这个项目中编写HTTP接口。
HttpApi.Client:当您有一个需要使用HTTP接口的c#应用程序时,此项目是有用的。一旦客户端应用程序引用了这个项目,它就可以直接注入和使用应用程序服务。这得益于ABP框架的 动态c#客户端API代理系统


1.首先建立实体。 在 Domian 下面新建一个productConfig类 。
继承FullAuditedAggregateRoot 泛型根据你的id的类型。这是abp自带的实体类。但是底层都是必须继承 Entity。这封装包含了基本的修改人和逻辑删除的扩展字段不用单独封了。

2.因为我使用的是ef core 所以刚好在 EntityFrameworkCore 下的 DbContext.cs文件夹下 注入我的表。以及添加数据库的映射关系

3.先在host 下的 appsettings.json里配置好连接字符串 


用的是ef 所以需要数据迁移。首先要准备好确定 dotnet --ef 是否安装。
模版项目在host下执行下面语句(其余项目在EntityFrameworkCore 下执行)
会自动创建个Migrations文件夹

dotnet ef migrations add Added_ProductConfig

然后再执行一下语句生成Abp的基础表 (如果运行项目的时候报错 abpsetting 不存在就是表没创建成功) 。执行完检查下表有米有。

dotnet ef database update


4. 在 Application.Contracts 下创建
1个入参的dto和出参dto

以及1个应用层的接口。

5.在 Domian下新建一个接口 IProductConfigRepository 用于在实体赋值时的验重查询操作

public interface IProductConfigRepository : IRepository<ProductConfig, Guid>
{
    Task<ProductConfig> FindByNameAsync(string name);

    Task<List<ProductConfig>> GetListAsync(
        int skipCount,
        int maxResultCount,
        string sorting,
        string filter = null
    );
}

EntityFrameworkCore  下创建一个对应的实现

 /// <summary>
 /// 产品分类表检验类
 /// </summary>
 public class EfCoreProductConfigRepository : EfCoreRepository<FinancingInstitutionDbContext, ProductConfig, Guid>,
     IProductConfigRepository
 {
     public EfCoreProductConfigRepository(
     IDbContextProvider<FinancingInstitutionDbContext> dbContextProvider)
     : base(dbContextProvider)
     {
     }

     public async Task<ProductConfig> FindByNameAsync(string name)
     {
         var dbSet = await GetDbSetAsync();
         return await dbSet.FirstOrDefaultAsync(p => p.Name == name);
     }

     public async Task<List<ProductConfig>> GetListAsync(
         int skipCount,
         int maxResultCount,
         string sorting,
         string filter = null)
     {
         var dbSet = await GetDbSetAsync();
         return await dbSet
             .WhereIf(
                 !filter.IsNullOrWhiteSpace(),
                 author => author.Name.Contains(filter)
             )
             .OrderBy(sorting)
             .Skip(skipCount)
             .Take(maxResultCount)
             .ToListAsync();
     }
 }

6.在 Domian下面新建一个类  ProductConfigManage 。因为我只有crud。实际对于实体来说有修改操作的只有3个行为。增删改。从面向对象的角度理解会更容易理解。根据行为。做该领域内这个行为需要做的事情。前1个步骤就是为这个做铺垫

 /// <summary>
 /// 实体层聚合类
 /// </summary>
 public class ProductConfigManage : DomainService
 {
     private readonly IProductConfigRepository _productConfigRepository;

     public ProductConfigManage(IProductConfigRepository productConfigRepository)
     {
         _productConfigRepository = productConfigRepository;
     }

     /// <summary>
     /// 新增
     /// </summary>
     /// <returns></returns>
     public async Task<ProductConfig> CreateAsync(
         [Required] string name, bool status, int sort, Guid partentId)
     {
         Check.NotNullOrWhiteSpace(name, nameof(name));

         var checkExits = await _productConfigRepository.FindByNameAsync(name);
         if (checkExits != null)
             throw new ProductAlreadyExistsException(name);

         return new ProductConfig(GuidGenerator.Create(), name, status, sort, partentId);
     }

     /// <summary>
     /// 修改
     /// </summary>
     /// <returns></returns>
     public async Task<ProductConfig> UpdateAsync([Required] ProductConfig config, [Required] string name, bool status, int sort, Guid partentId)
     {
         Check.NotNullOrWhiteSpace(name, nameof(name));

         var checkExits = await _productConfigRepository.FindByNameAsync(name);
         if (checkExits != null && config.Id != checkExits.Id)
             throw new ProductAlreadyExistsException(name);

         return config.Upd(name, status, sort, partentId);
     }

     /// <summary>
     /// 删除
     /// </summary>
     /// <returns></returns>
     public async Task<ProductConfig> DeleteAsync([Required] ProductConfig config)
     {
         var checkExits = await _productConfigRepository.FindAsync(p => p.Id == config.Id);
         if (checkExits == null)
             throw new ProductAlreadyExistsException(config.Name);

         return config.Del();
     }
 }

同时实体里。增加对应的增删改 需要的实体赋值 

 /// <summary>
 /// 基础配置
 /// </summary>
 public class ProductConfig : FullAuditedAggregateRoot<Guid>
 {
     /// <summary>
     /// 分类名称
     /// </summary>
     public string Name { get; set; } = string.Empty;

     /// <summary>
     /// 状态 true 启用 
     /// </summary>
     public bool Status { get; set; } = false;

     /// <summary>
     /// 排序
     /// </summary>
     public int Sort { get; set; } = 0;

     /// <summary>
     /// 父级Id
     /// </summary>
     public Guid ParentId { get; set; }

     internal ProductConfig(Guid id, [NotNull] string name, bool status, int sort, Guid parentId)
     {
         Id = id;
         SetName(name);
         Status = status;
         Sort = sort;
         ParentId = parentId;
     }
     
     internal ProductConfig Upd([NotNull] string name, bool status, int sort, Guid parentId)
     {
         SetName(name);
         Status = status;
         Sort = sort;
         ParentId = parentId;
         return this;
     }

     internal ProductConfig Del()
     {
         IsDeleted = true;
         DeletionTime = DateTime.Now;
         return this;
     }

     //效验名称最大为100
     private void SetName([NotNull] string name)
     {
         Name = Check.NotNullOrWhiteSpace(
         name,
         nameof(name),
         maxLength: 100
     );
     }
 }

7.完成应用层。在 Application 下面新建 ProductConfigAppService类
继承 ApplicationService。 底层最终都是继承这个。也封装了很多例如 crudappservice 但是必须实现对应的接口。这个就看个人喜好了。这样的写法应用层非常的直观。新增 =>直接在domian层获取新增的实体然后去数据层直接保存。对象化操作的写接口。

 /// <summary>
 /// 产品分类服务
 /// </summary>
 public class ProductConfigAppService : ApplicationService, IProductConfigAppService
 {
     private readonly IRepository<ProductConfig, Guid> _Repository;
     private readonly ProductConfigManage _configManager;

    public ProductConfigAppService(
     IRepository<ProductConfig, Guid> repository)
 {
     _Repository = repository;
 }

     /// <summary>
     /// 获取全部配置
     /// </summary>
     /// <returns></returns>
     public async Task<PagedResultDto<ProductConfigDto>> GetAll()
     {
         var source = await _Repository.GetListAsync(p => true);
         if (source == null || source.Count <= 0)
             return new PagedResultDto<ProductConfigDto>();

         var returnDto = source.Select(x =>
         {
             var dto = ConverMap(x);
             return dto;
         }).ToList();

         var count = await _Repository.GetCountAsync();

         return new PagedResultDto<ProductConfigDto>(count, returnDto);
     }

     /// <summary>
     /// 获取指定配置
     /// </summary>
     /// <returns></returns>
     public async Task<ProductConfigDto> GetModel(Guid id)
     {
         var source = await _Repository.FindAsync(p => p.Id.Equals(id));
         if (source == null)
             return new ProductConfigDto();

         return ConverMap(source);
     }

     /// <summary>
     /// 新增配置
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
     public async Task<ProductConfigDto> CreateAsync(AddAndUpdProductConfigDto input)
     {
         var model = await _configManager.CreateAsync(input.Name, input.Status, input.Sort, input.ParentId);

         await _Repository.InsertAsync(model);

         return ConverMap(model);
     }

     /// <summary>
     /// 修改配置
     /// </summary>
     /// <param name="id"></param>
     /// <param name="input"></param>
     /// <returns></returns>
     /// <exception cref="Exception"></exception>
     public async Task UpdateAsync(AddAndUpdProductConfigDto input)
     {
         Check.NotDefaultOrNull<Guid>(input.Id, nameof(input.Id));

         var check = await _Repository.FindAsync(p => p.Id == input.Id);
         if (check == null)
             throw new Exception("数据不存在");

         var model = await _configManager.UpdateAsync(check, input.Name, input.Status, input.Sort, input.ParentId);

         await _Repository.UpdateAsync(model);
     }

     /// <summary>
     /// 删除配置
     /// </summary>
     /// <param name="id"></param>
     /// <param name="input"></param>
     /// <returns></returns>
     /// <exception cref="Exception"></exception>
     public async Task DeleteAsync([Required] Guid id)
     {
         Check.NotDefaultOrNull<Guid>(id, nameof(id));

         var check = await _Repository.FindAsync(p => p.Id == id);
         if (check == null)
             throw new Exception("数据不存在");

         var model = await _configManager.DeleteAsync(check);

         await _Repository.UpdateAsync(model);
     }


     /// <summary>
     /// 转换实体
     /// </summary>
     /// <param name="source"></param>
     /// <returns></returns>
     private ProductConfigDto ConverMap(ProductConfig source)
     {
         return ObjectMapper.Map<ProductConfig, ProductConfigDto>(source);
     }
 }

8.应用层写完了。动态生成api启动项目
host下的 FinancingInstitutionHttpApiHostModule 里动态注入我们的模块 生成

9.启动项目

OK 大概流程就是如此
abp是带验签的 。不过只是demo 我没启动authserve项目了。需要使用的话就一样配置authserver服务的连接字符串以及ef的2个指令。按照下面官方文档的运行步骤启动即可

这只是个demo 大概代码写哪里。怎么跑起来。
哈哈哈 万一接口不通啥的很正常因为我还没测。有问题会及时更正文档。
一起学习 一起进步。 欢迎指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1373360.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】Linux系统编程——Linux目录结构

Linux的文件系统呈现为一种树状结构&#xff0c;以根目录/为最顶层&#xff0c;其下分布着各种不同的子目录&#xff0c;每个目录都有其特定的用途和功能。下面是Linux目录结构的详细介绍&#xff1a; 1. 根目录 / 根目录是整个文件系统的基础。所有的目录和文件都从这里开始…

智能制造与MES:推动制造业转型升级的关键

随着科技的迅猛发展&#xff0c;智能制造已经成为推动制造业转型升级的重要手段。而制造执行系统&#xff08;MES&#xff09;作为智能制造的核心管理系统&#xff0c;在提高生产效率、优化生产流程、实现数字化转型等方面发挥着重要作用。 一、智能制造的概念与特点 智能制造…

极狐 GitLab 冷知识:使用 Email 也可以创建 Issue?

前言 在使用 GitLab 时&#xff0c;创建 Issue 和 Merge Request 的方法&#xff0c;除了常规的使用 GitLab Web UI 进行操作和通过 API 调用操作&#xff0c;还有一些比较好玩的&#xff0c;比如使用 Email 来创建。 Incoming email 如果是 Self-Manager 的 GitLab 用户&am…

少儿编程 2023年12月中国电子学会图形化编程等级考试Scratch编程三级真题解析(判断题)

2023年12月scratch编程等级考试三级真题 判断题 19、下列两段程序的运行效果相同 答案:对 考点分析:考查积木综合使用,重点考查循环积木的使用;左边属于有条件的循环,由变量的值控制,当变量值大于50时,循环停止,而变量始终为零,不满足条件,所以一直循环,和右边的…

市场复盘总结 20240110

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 昨日主题投资 连板进级率 33% 二进三&#xff1a; 进级率低 50% 最常用的二种方法&#xff1a; 方法一&…

观成科技-加密C2框架EvilOSX流量分析

工具简介 EvilOSX是一款开源的&#xff0c;由python编写专门为macOS系统设计的C2工具&#xff0c;该工具可以利用自身释放的木马来实现一系列集成功能&#xff0c;如键盘记录、文件捕获、浏览器历史记录爬取、截屏等。EvilOSX主要使用HTTP协议进行通信&#xff0c;通信内容为特…

Android可换行的RadioGroup

Android可换行的RadioGroup,有时候需要换行显示的单选列表&#xff0c;当然可以有多种实现方式&#xff0c;比如recycleview或者listview实现&#xff0c;本文采用的是RadioGrouprediobutton方式实现。 一、首先自定义view public class WrapRadioGroup extends RadioGroup {pr…

领域专家精心讲解AI视频生成

大家好&#xff0c;我是herosunly。985院校硕士毕业&#xff0c;现担任算法研究员一职&#xff0c;热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名&#xff0c;CCF比赛第二名&#xff0c;科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的…

初识 Elasticsearch 应用知识,一文读懂 Elasticsearch 知识文集(2)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

PPT插件-大珩助手-快速构建自己的图形

绘图板-快速构建自己的图形 通过手绘的方式&#xff0c;快速构建自己的想法和创意&#xff0c;通过在PPT中插入绘图&#xff0c;植入背景透明的绘图&#xff0c;点击画笔可切换橡皮擦&#xff0c;可以清空画板重新绘制。 素材库-存储图形 通过素材库存储自己的图形 图形调整…

【Emgu.CV教程】4.4、无缝融合应用之TextureFlattening()纹理扁平化

这是无缝融合应用的最后一篇&#xff0c;TextureFlattening()函数&#xff0c;专门用于对图像指定部位进行纹理扁平化的。这个功能现在讲起来有点太早了&#xff0c;应该放到《图像的空间滤波--平滑》这一章节中才合适。因为它就是用Sobel算子进行平滑&#xff0c;也就是在保留…

uniapp使用wxml-to-canvas开发小程序保存canvas图片

微信小程序官方解决方案&#xff1a;wxml-to-canvas 使用wxml-to-canvas要知道一些前提条件 1、只能画view&#xff0c;text&#xff0c;image 2、每个元素必须要设置宽高 3、默认是flex布局&#xff0c;可以通过flexDirection: "column"来改变排列方式 4、文字 必…

复试 || 就业day14(2024.01.10)算法篇

文章目录 前言字符串中第二大的数字字符串中不同整数的数目判断句子是否为全字母句长度为三且各字符不同的子字符串检查是否区域内所有整数都被覆盖*重新分配字符使所有字符串都相等可以输入的最大单词数检查是否所有字符出现次数相同差的绝对值为 K 的数对数目至少在两个数组中…

PHP开发日志 ━━ php8.3安装与使用组件Xdebug

今天开头写点历史&#xff1a; 二十年前流行asp&#xff0c;当时用vb整合常用函数库写了一个dll给asp调用&#xff0c;并在此基础上开发一套仿windows界面的后台管理系统&#xff1b;后来asp逐渐没落&#xff0c;于是在十多年前转投php&#xff0c;不久后用php写了一套mvc框架&…

压缩编码之变换的选择之离散余弦变换(DCT)和离散傅立叶变换(DFT)——数字图像处理

原理 变换的选择是一个关键的考量因素&#xff0c;它决定了数据是如何被压缩的。选择变换时考虑以下几个重要原则&#xff1a; 数据去关联性&#xff1a;变换的目的之一是减少数据中的相关性。例如&#xff0c;在图像压缩中&#xff0c;像素间往往高度相关。通过适当的变换&a…

Redis-Cluster 与 Redis 集群的技术大比拼

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 Redis-Cluster 与 Redis 集群的技术大比拼 前言概念与原理对比Redis-Cluster&#xff1a;基于哈希槽的分布式解决方案传统 Redis 集群&#xff1a;主从架构下的数据分片方式 搭建与配置的异同Redis-Cl…

跟着小德学C++之进程信息记录

嗨&#xff0c;大家好&#xff0c;我是出生在达纳苏斯的一名德鲁伊&#xff0c;我是要立志成为海贼王&#xff0c;啊不&#xff0c;是立志成为科学家的德鲁伊。最近&#xff0c;我发现我们所处的世界是一个虚拟的世界&#xff0c;并由此开始&#xff0c;我展开了对我们这个世界…

JS逆向之加密参数定位

文章目录 前言加密参数的处理步骤加密参数的定位方法搜索断点XHR断点DOM断点EVENT断点 hook 前言 当我们对网络请求进行抓包分析之后&#xff0c;需要用开发者工具对加密参数进行全局搜索。当搜索不到加密参数的时候&#xff0c;应该采取什么解决方法去定位。 还有一个应用场…

Tomcat-快速使用

关于Tomcat的概念及来由在Tomcat基本知识中进行了介绍&#xff0c;下面我们直接上手快速使用起来。 一、快速使用 &#xff08;1&#xff09;tomcat下载 &#xff08;2&#xff09;解压缩 &#xff08;3&#xff09;启动程序 &#xff08;4&#xff09;访问tomcat&#xff1a…

Windows和Linux中检查端口是否被占用

一、windows 1、查询端口占用情况 cmd > netstat -ano 2、查询8080端口是否被占用 cmd > netstat -ano|findstr 8080 3、查询哪个程序占用了端口 cmd > tasklist|findstr PID 4、终止该占用进程 cmd > ntsd -c q -p PID 二、Linux 1、查询8080端口是否被占用…