框架搭建
2
聚合服务
这里我们将聚合服务命名为Domain.Core
和基础服务层一致,我们先通过命令创建单层模板项目Domain.Core,这里我们删除wwwroot、Data、Entities、Localization、ObjectMapping文件夹及其所有子文件,并删除package.json文件和Services文件夹下的Dtos文件夹
在同级目录添加类库Domain.Core.Constracts并删除其中的Class1.cs文件
这里我们在聚合服务中暂时不需要提供动态客户端代理,暂时不创建.Client项目,如果需要,创建方式和基础服务层相同。
修改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>
修改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 packagestypeof(AbpAspNetCoreMvcModule),typeof(AbpAutofacModule),typeof(AbpSwashbuckleModule),typeof(AbpAspNetCoreSerilogModule),typeof(CoreContractsModule),typeof(NotificationManagerClientModule)
)]
public class CoreModule : AbpModule
{#region 私有方法#region 配置动态WebApiprivate void ConfigureAutoApiControllers(){Configure<AbpAspNetCoreMvcOptions>(options =>{options.ConventionalControllers.Create(typeof(CoreModule).Assembly);});}#endregion#region 配置swaggerprivate 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#endregionpublic 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();}
}
修改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();
修改appsettings.json文件如下:
{"urls": "http://*:6003","RemoteServices": {"NitificationManager": {"BaseUrl": "http://localhost:5003/"}}
}
修改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>
在Demo.Core.Contracts项目中创建CoreContractsModule.cs文件内容如下:
using Volo.Abp.Application;
using Volo.Abp.Modularity;namespace Demo.Core.Contracts;[DependsOn(typeof(AbpDddApplicationContractsModule)
)]
public class CoreContractsModule : AbpModule
{}
启动Demo.Core项目可正常运行并显示swagger页面则聚合服务创建成功。
业务代码
1
基础服务
在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; }
}
在Demo.NotificationManager项目Data文件夹下的NotificationManagerDbContext类中添加如下属性并引入相应依赖:
public DbSet<Notification> Notifications { get; set; }
通过 dotnet-ef 的 migrations add 命令和 database update 命令创建数据库结构。
这里我们未用到领域服务,如果需要,可在Demo.NotificationManager项目中添加Managers文件夹并按领域存放对应代码。
在Demo.NotificationManager.Contracts项目中添加Services文件夹,DTO和应用服务接口的定义。这里添加子文件夹Notifications并在其中添加Dtos文件夹,用于存放DTO。
我们添加以下两个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; }
}
添加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 interfaceINotificationAppService : ICrudAppService<NotificationDto, Guid, PagedResultRequestDto, NotificationCreateDto>
{}
在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){}
}
此处,我引入了Mapster框架替代原来的AutoMapper框架,所以我们看到CustomCrudAppService类替代了默认增删改查类CrudAppService。
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);}
}
#endregionpublic 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 Mapsterprotected 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
}
此类我继承自原有的CrudAppService类,并重写了对象转换方法。
使用Mapster,比AutoMapper效率更高,而且不需要提前声明默认的对象映射关系,如果需要额外定义映射关系或操作,可通过添加MapConfig类实现。具体用法参考其官方文档:https://github.com/rivenfx/Mapster-docs
如果我们需要定义领域通用配置、枚举等原属于Domain.Share项目中的内容,可创建并存放在Demo.NotificationManager.Contracts项目下Share文件夹中。
完成以上步骤,并可成功运行Demo.NotificationManager项目,则基础服务代码运行成功。
未完待续
关注我获得
更多精彩