Abp 创建一个模块项目demo

本文详细介绍了如何在ABP框架中创建一个简单的单表CRUD后台服务,涉及模块划分、数据访问、EntityFrameworkCore集成、数据库迁移、API接口生成及验证规则。
摘要由CSDN通过智能技术生成

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
FullAuditedAggregateRoot 包含了修改人和逻辑删除的扩展字段不用单独封了。

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

非模版项目:


模版项目:
   属性还是一样的添加。添加映射则换到 DbContextModelCreatingExtensions 类 下写映射关系。
   不然host无法读取到表的存在。


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


用的是ef 所以需要数据迁移。首先要准备好确定 dotnet --ef 是否安装。是否可以使用ef core CLI。
模版项目在host下执行下面语句(其余项目在EntityFrameworkCore 下执行)
(不管是host 还是 core 都需要把项目设置为启动项。再在程序包控制台执行语句)
会自动创建个Migrations文件夹。在生成的迁移文件里。会看到表的生成语句的存在

dotnet ef migrations add Added_ProductConfig

然后再执行一下语句会生成我创建的实体表。执行完检查下表有米有。
(如果是模版项目记得把Authservice 也执行一下2个语句不然最终起不来。缺表)

dotnet ef database update

Autherserver 和Host的表

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

以及1个应用层的接口。

以及修改一个domian 下的  FinancingInstitutionApplicationModule 类。改为false 
(这是里dto的规则效验。不false 映射报错)

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个行为。增删改。从面向对象的角度理解会更容易理解(根据行为操作在domian层获取对应的实体。再去应用层根据操作执行)。前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 但是必须实现对应的CRUD接口。这个就看个人喜好我了。

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

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

     /// <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> AddAsync(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 UpdAsync(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 DelAsync([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)
     {
         try
         {
             return ObjectMapper.Map<ProductConfig, ProductConfigDto>(source);
         }
         catch (Exception e)
         {
             return new ProductConfigDto();
         }
     }
 }

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

9.启动项目 接口的生成规则是这样的。我写的不规范所以是这样的。


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


Demo写完了 跑一下 : 插入一条数据 OK


(如果运行项目的时候报错 abpsetting 不存在。就是表没创建成功。重新跑下ef那2个语句确保表都有了) 。

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
“DRY——避免重复代码”是一个优秀的开发者在开发软件时所具备的最重要的思想之一。我们在开发企业WEB应用程序时都有一些类似的需求,例如:都需要登录页面、用户/角色管理、权限验证、数据有效性验证、多语言/本地化等等。一个高品质的大型软件都会运用一些最佳实践,例如分层体系结构、领域驱动设计、依赖注入等。我们也可能会采用ORM、数据库迁移(Database Migrations)、日志记录(Logging)等工具。 从零开始创建一个企业应用程序是一件繁琐的事,因为需要重复做很多常见的基础工作。许多公司都在开发自己的应用程序框架来重用于不同的项目,然后在框架的基础上开发一些新的功能。但并不是每个公司都有这样的实力。假如我们可以分享的更多,也许可以避免每个公司或每个项目的重复编写类似的代码。作者之所以把项目命名为“ASP.NET Boilerplate”,就是希望它能成为开发一般企业WEB应用的新起点,直接把ABP作为项目模板。 ABP的全称是Asp.net boilerplate project(asp.Net样板工程)。是github上非常活跃的一个开源项目。它并没有使用任何新的技术,只是由两名架构师将asp.net开发中常用的一些工具整合到了一起,并且部分实现了DDD的概念。是一个开箱即用的框架,可以作为asp.net分布式应用的一个良好起点。 它的功能包括: 服务器端: 基于最新的.NET技术 (目前是ASP.NET MVC 5、Web API 2、C# 5.0,在ASP.NET 5正式发布后会升级) 实现领域驱动设计(实体、仓储、领域服务、领域事件、应用服务、数据传输对象,工作单元等等) 实现分层体系结构(领域层,应用层,展现层和基础设施层) 提供了一个基础架构来开发可重用可配置的模块 集成一些最流行的开源框架/库,也许有些是你正在使用的。 提供了一个基础架构让我们很方便地使用依赖注入(使用Castle Windsor作为依赖注入的容器) 提供Repository仓储模式支持不同的ORM(已实现Entity Framework 、NHibernate、MangoDb和内存数据库) 支持并实现数据库迁移(EF 的 Code first) 模块化开发(每个模块有独立的EF DbContext,可单独指定数据库) 包括一个简单的和灵活的多语言/本地化系统 包括一个 EventBus来实现服务器端全局的领域事件 统一的异常处理(应用层几乎不需要处理自己写异常处理代码) 数据有效性验证(Asp.NET MVC只能做到Action方法的参数验证,ABP实现了Application层方法的参数有效性验证) 通过Application Services自动创建Web Api层(不需要写ApiController层了) 提供基类和帮助类让我们方便地实现一些常见的任务 使用“约定优于配置原则” 客户端: Bootstrap、Less、AngularJs、jQuery、Modernizr和其他JS库: jQuery.validate、jQuery.form、jQuery.blockUI、json2等 为单页面应用程序(AngularJs、Durandaljs)和多页面应用程序(Bootstrap+Jquery)提供了项目模板。 自动创建Javascript 的代理层来更方便使用Web Api 封装一些Javascript 函数,更方便地使用ajax、消息框、通知组件、忙状态的遮罩层等等 除ABP框架项目以外,还开发了名叫“Zero”的模块,实现了以下功能: 身份验证与授权管理(通过ASP.NET Identity实现的) 用户&角色管理 系统设置存取管理(系统级、租户级、用户级,作用范围自动管理) 审计日志(自动记录每一次接口的调用者和参数) 我共享的资料包含ABP(2.02版本)的一个Demo文件以及调试时候需要填的坑的处理方法(填了蛮久才填满。。。),还包括一本ABP中文教程。
创建ABP框架的空解决方案,可以分为两种方式:使用ABP CLI命令行工具和使用Visual Studio模板。 方式一:使用ABP CLI命令行工具创建空解决方案 1. 安装ABP CLI命令行工具 ABP CLI是ABP框架的命令行工具,用于创建和管理ABP项目。你需要在本地安装ABP CLI命令行工具,可以在命令行中使用以下命令进行安装: ``` dotnet tool install -g Volo.Abp.Cli ``` 2. 创建空解决方案 在命令行中使用以下命令来创建一个空解决方案: ``` abp new MySolutionName ``` 其中,MySolutionName是你要创建的解决方案名称。 3. 选择模板 在创建空解决方案时,ABP CLI会提示你选择模板。你可以选择ASP.NET Core MVC、Blazor等模板。如果只需要一个空解决方案,可以选择Empty模板。 4. 运行应用程序 使用Visual Studio打开解决方案,编译运行应用程序。 方式二:使用Visual Studio模板创建空解决方案 1. 下载ABP框架的Visual Studio模板 你可以从ABP框架官网下载Visual Studio模板,下载完成后安装到Visual Studio中。 2. 创建空解决方案 在Visual Studio中选择File -> New -> Project,在弹出的窗口中选择ABP Solution模板,输入解决方案名称和路径,选择空解决方案模板,点击Create按钮。 3. 运行应用程序 编译运行应用程序,测试解决方案是否创建成功。 以上就是使用ABP CLI命令行工具和Visual Studio模板创建空解决方案的步骤,希望能对你有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值