ABP vNext微服务架构详细教程(补充篇)——单层模板(中)

框架搭建

2

聚合服务

a397f833b8020deaad5eb313f26f8e74.png

这里我们将聚合服务命名为Domain.Core

e2b64a3ca28f8cf127b73262d7a2605b.gif

和基础服务层一致,我们先通过命令创建单层模板项目Domain.Core,这里我们删除wwwroot、Data、Entities、Localization、ObjectMapping文件夹及其所有子文件,并删除package.json文件和Services文件夹下的Dtos文件夹

fe893f40298110d9e3fcf6e8ee8adcbc.gif

在同级目录添加类库Domain.Core.Constracts并删除其中的Class1.cs文件

c2eb67e59dfae65ebc058a183d0db0e1.png

这里我们在聚合服务中暂时不需要提供动态客户端代理,暂时不创建.Client项目,如果需要,创建方式和基础服务层相同。

71cc44124fc4c51822b912cc5c43a240.gif

修改Demo.Core.csproj文件如下:

<Project Sdk="Microsoft.NET.Sdk.Web">


  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
  </PropertyGroup>


  <ItemGroup>
    <PackageReference Include="Serilog.AspNetCore" Version="5.0.0" />
    <PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
  </ItemGroup>


  <ItemGroup>
    <PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="5.3.4" />
    <PackageReference Include="Volo.Abp.Autofac" Version="5.3.4" />
    <PackageReference Include="Volo.Abp.Swashbuckle" Version="5.3.4" />
    <PackageReference Include="Volo.Abp.AspNetCore.Serilog" Version="5.3.4" />
  </ItemGroup>
  
  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="6.0.5" />
  </ItemGroup>


  <ItemGroup>
    <Content Remove="Localization\Core\*.json" />
    <EmbeddedResource Include="Localization\Core\*.json" />
    <EmbeddedResource Remove="wwwroot\**" />
    <Content Remove="wwwroot\**" />
    <Content Remove="package.json" />
  </ItemGroup>


  <ItemGroup>
    <Compile Remove="Logs\**" />
    <Content Remove="Logs\**" />
    <EmbeddedResource Remove="Logs\**" />
    <None Remove="Logs\**" />
    <Compile Remove="wwwroot\**" />
    <None Remove="wwwroot\**" />
  </ItemGroup>
  
  <ItemGroup>
    <ProjectReference Include="..\..\..\service\notificationmanager\Demo.NotificationManager.Client\Demo.NotificationManager.Client.csproj" />
    <ProjectReference Include="..\Demo.Core.Contracts\Demo.Core.Contracts.csproj" />
  </ItemGroup>
</Project>

28c4648364d7fcd2f1e09a1cc48ad6ab.gif

修改CoreModule.cs类代码如下:

using Demo.Core.Contracts;
using Demo.NotificationManager.Client;
using Microsoft.OpenApi.Models;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;
using Volo.Abp.Swashbuckle;


namespace Demo.Core;


[DependsOn(
    // ABP Framework packages
    typeof(AbpAspNetCoreMvcModule),
    typeof(AbpAutofacModule),
    typeof(AbpSwashbuckleModule),
    typeof(AbpAspNetCoreSerilogModule),
    typeof(CoreContractsModule),
    typeof(NotificationManagerClientModule)
)]
public class CoreModule : AbpModule
{
    #region 私有方法
    
    #region 配置动态WebApi
    private void ConfigureAutoApiControllers()
    {
        Configure<AbpAspNetCoreMvcOptions>(options =>
        {
            options.ConventionalControllers.Create(typeof(CoreModule).Assembly);
        });
    }
    #endregion


    #region 配置swagger
    private void ConfigureSwagger(IServiceCollection services)
    {
        services.AddAbpSwaggerGen(
            options =>
            {
                options.SwaggerDoc("v1", new OpenApiInfo { Title = "Core API", Version = "v1" });
                options.DocInclusionPredicate((docName, description) => true);
                options.CustomSchemaIds(type => type.FullName);
            }
        );
    }
    #endregion
    
    #endregion
    
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        ConfigureSwagger(context.Services);
        ConfigureAutoApiControllers();
    }
    
    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        
        app.UseRouting();
        app.UseUnitOfWork();
        app.UseSwagger();
        app.UseSwaggerUI(options =>
        {
            options.SwaggerEndpoint("/swagger/v1/swagger.json", "Core API");
        });
        app.UseAbpSerilogEnrichers();
        app.UseConfiguredEndpoints();
    }
}

9b2097b55a6f30e0391f6012b85090ca.gif

修改Program.cs文件内容如下:

using Demo.Core;
using Serilog;
using Serilog.Events;


Log.Logger = new LoggerConfiguration()
#if DEBUG
    .MinimumLevel.Debug()
#else
                .MinimumLevel.Information()
#endif
    .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
    .Enrich.FromLogContext()
    .WriteTo.Async(c => c.File("Logs/logs.txt"))
#if DEBUG
    .WriteTo.Async(c => c.Console())
#endif
    .CreateLogger();


var builder = WebApplication.CreateBuilder(args);


builder.Host.UseAutofac().UseSerilog();
builder.Services.ReplaceConfiguration(builder.Configuration);
builder.Services.AddApplication<CoreModule>();


var app = builder.Build();
app.InitializeApplication();
app.Run();

a8dcacd6cd33398d897e461888ef26ed.gif

修改appsettings.json文件如下:

{
  "urls": "http://*:6003",
  "RemoteServices": {
    "NitificationManager": {
      "BaseUrl": "http://localhost:5003/"
    }
  }
}

0dba3eb74b9ed7774f3c5aa2765bcfbb.gif

修改Demo.Core.Contracts.csproj文件:

<Project Sdk="Microsoft.NET.Sdk">


    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
    </PropertyGroup>
    
    <ItemGroup>
        <PackageReference Include="Volo.Abp.Ddd.Application.Contracts" Version="5.3.4" />
    </ItemGroup>
</Project>

514fc89985a44ebfbe4f32ae3f6f5b5f.gif

在Demo.Core.Contracts项目中创建CoreContractsModule.cs文件内容如下:

using Volo.Abp.Application;
using Volo.Abp.Modularity;


namespace Demo.Core.Contracts;


[DependsOn(
    typeof(AbpDddApplicationContractsModule)
)]
public class CoreContractsModule : AbpModule
{


}

153d455b9d47f9cb9d23b2051d03ee82.png

启动Demo.Core项目可正常运行并显示swagger页面则聚合服务创建成功。

业务代码

1

基础服务

a887f1cb12fa154cbe22f37efe9e0323.gif

在Demo.NotificationManager项目Entities文件夹中按领域划分子文件夹,这里创建Notifications领域文件夹,并添加实体类Notification如下:

using System.ComponentModel.DataAnnotations;
using Volo.Abp.Domain.Entities.Auditing;


namespace Demo.NotificationManager.Entities.Notifications;


/// <summary>
/// 通知
/// </summary>
public class Notification : CreationAuditedEntity<Guid>
{
    /// <summary>
    /// 接受着用户ID
    /// </summary>
    public Guid ReceiverId { get; set; }


    /// <summary>
    /// 标题
    /// </summary>
    [StringLength(128)]
    public string Title { get; set; }
    
    /// <summary>
    /// 内容
    /// </summary>
    public string Content { get; set; }


    /// <summary>
    /// 是否已读
    /// </summary>
    public bool IsRead { get; set; }
}

f6c9688eaffd5e6aad93142b721fead8.gif

在Demo.NotificationManager项目Data文件夹下的NotificationManagerDbContext类中添加如下属性并引入相应依赖:

public DbSet<Notification> Notifications { get; set; }

a822c3d888def1eec2da0385984e98d5.gif

通过 dotnet-ef 的 migrations add 命令和 database update 命令创建数据库结构。

44427566c4a9d095cf4e1ee9691f49f6.png

这里我们未用到领域服务,如果需要,可在Demo.NotificationManager项目中添加Managers文件夹并按领域存放对应代码。

66e42c2e2b094c360a2c19a3820341a0.gif

在Demo.NotificationManager.Contracts项目中添加Services文件夹,DTO和应用服务接口的定义。这里添加子文件夹Notifications并在其中添加Dtos文件夹,用于存放DTO。

d62a2be487f4830c8a1991366413ef81.gif

我们添加以下两个DTO分别用于添加和查询通知:

using System.ComponentModel.DataAnnotations;
using Volo.Abp.Application.Dtos;


namespace Demo.NotificationManager.Contracts.Services.Notifications.Dtos;


public class NotificationCreateDto:EntityDto<Guid>
{
    /// <summary>
    /// 接受着用户ID
    /// </summary>
    public Guid ReceiverId { get; set; }


    /// <summary>
    /// 标题
    /// </summary>
    [StringLength(128)]
    public string Title { get; set; }
    
    /// <summary>
    /// 内容
    /// </summary>
    public string Content { get; set; }


    /// <summary>
    /// 是否已读
    /// </summary>
    public bool IsRead { get; set; }
}
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Auditing;


namespace Demo.NotificationManager.Contracts.Services.Notifications.Dtos;


public class NotificationDto:NotificationCreateDto,ICreationAuditedObject
{
    /// <summary>
    /// 创建时间
    /// </summary>
    public DateTime CreationTime { get; set; }
    
    /// <summary>
    /// 创建者
    /// </summary>
    public Guid? CreatorId { get; set; }
}

1d1140cf896ea59dd9b68864495d98a4.gif

添加INotificationAppService应用服务接口,内容如下:

using Demo.NotificationManager.Contracts.Services.Notifications.Dtos;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;


namespace Demo.NotificationManager.Contracts.Services.Notifications;


public interface
    INotificationAppService : ICrudAppService<NotificationDto, Guid, PagedResultRequestDto, NotificationCreateDto>
{


}

49f0ce48db65eb06ca4916094df5efca.gif

在Demo.NotificationManger中Services文件夹中添加应用服务实现类,这里添加Notifications子文件夹并创建NotificationAppService类如下:

using Demo.Abp.Extension;
using Demo.NotificationManager.Contracts.Services.Notifications;
using Demo.NotificationManager.Contracts.Services.Notifications.Dtos;
using Demo.NotificationManager.Entities.Notifications;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Repositories;


namespace Demo.NotificationManager.Services.Notifications;


public class NotificationAppService : CustomCrudAppService<Notification, NotificationDto, Guid,PagedResultRequestDto,NotificationCreateDto>, INotificationAppService
{
    public NotificationAppService(IRepository<Notification, Guid> repository) : base(repository)
    {
    }
}

c7ed3ec6ee29053be72fd3a3969a829e.png

此处,我引入了Mapster框架替代原来的AutoMapper框架,所以我们看到CustomCrudAppService类替代了默认增删改查类CrudAppService。

620f6317d2f0030a68a5fe427adaa76a.gif

CrudAppService类被存放在Demo.Abp.Extension类库中作为通用基类使用,代码如下:

using Mapster;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;


namespace Demo.Abp.Extension;


#region 重载
public abstract class CustomCrudAppService<TEntity, TEntityDto, TKey>
    : CustomCrudAppService<TEntity, TEntityDto, TKey, PagedAndSortedResultRequestDto>
    where TEntity : class, IEntity<TKey>
    where TEntityDto : IEntityDto<TKey>
{
    protected CustomCrudAppService(IRepository<TEntity, TKey> repository)
        : base(repository)
    {


    }
}


public abstract class CustomCrudAppService<TEntity, TEntityDto, TKey, TGetListInput>
    : CustomCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TEntityDto>
    where TEntity : class, IEntity<TKey>
    where TEntityDto : IEntityDto<TKey>
{
    protected CustomCrudAppService(IRepository<TEntity, TKey> repository)
        : base(repository)
    {


    }
}


public abstract class CustomCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput>
    : CustomCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TCreateInput>
    where TEntity : class, IEntity<TKey>
    where TEntityDto : IEntityDto<TKey>
{
    protected CustomCrudAppService(IRepository<TEntity, TKey> repository)
        : base(repository)
    {


    }
}


public abstract class CustomCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
    : CustomCrudAppService<TEntity, TEntityDto, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
    where TEntity : class, IEntity<TKey>
    where TEntityDto : IEntityDto<TKey>
{
    protected CustomCrudAppService(IRepository<TEntity, TKey> repository)
        : base(repository)
    {


    }


    protected override Task<TEntityDto> MapToGetListOutputDtoAsync(TEntity entity)
    {
        return MapToGetOutputDtoAsync(entity);
    }


    protected override TEntityDto MapToGetListOutputDto(TEntity entity)
    {
        return MapToGetOutputDto(entity);
    }
}
#endregion




public class CustomCrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput,
        TUpdateInput>
    : CrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
    where TEntity : class, IEntity<TKey>
    where TGetOutputDto : IEntityDto<TKey>
    where TGetListOutputDto : IEntityDto<TKey>
{
    public CustomCrudAppService(IRepository<TEntity, TKey> repository) : base(repository)
    {
    }
    
    #region Mapster
    protected override TEntity MapToEntity(TCreateInput createInput)
    {
        var entity = createInput.Adapt<TEntity>();
        SetIdForGuids(entity);
        return entity;
    }


    protected override void MapToEntity(TUpdateInput updateInput, TEntity entity)
    {
        if (updateInput is IEntityDto<TKey> entityDto)
        {
            entityDto.Id = entity.Id;
        }
        entity = updateInput.Adapt(entity);
    }


    protected override TGetOutputDto MapToGetOutputDto(TEntity entity)
    {
        return entity.Adapt<TGetOutputDto>(); //ObjectMapper.Map<TEntity, TGetOutputDto>(entity);
    }
    protected override TGetListOutputDto MapToGetListOutputDto(TEntity entity)
    {
        return entity.Adapt<TGetListOutputDto>(); //ObjectMapper.Map<TEntity, TGetListOutputDto>(entity);
    }
    #endregion
}

0b760975cc56ab9a5e2736b30524c9ec.png

此类我继承自原有的CrudAppService类,并重写了对象转换方法。

31d92ef53bfcbdc94c6819813cbecc7b.png

使用Mapster,比AutoMapper效率更高,而且不需要提前声明默认的对象映射关系,如果需要额外定义映射关系或操作,可通过添加MapConfig类实现。具体用法参考其官方文档:https://github.com/rivenfx/Mapster-docs

bc4c51300601d072ca8019ca51cddcc5.png

如果我们需要定义领域通用配置、枚举等原属于Domain.Share项目中的内容,可创建并存放在Demo.NotificationManager.Contracts项目下Share文件夹中。

86c30f763a8b05646422fef42f2c675d.png

完成以上步骤,并可成功运行Demo.NotificationManager项目,则基础服务代码运行成功。

e9246de82a733964a451446af7168f16.png

未完待续

3d465c4ef30fbb5e8470b6749e9403a2.jpeg

03618cecdcc73e8c939e838d022114b2.png

关注我获得

更多精彩

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值