This is the part 11 of the series: .NET Microservice with ABP
Posts in the Series
Part 3. Administration Service
Part 8. Identity server and Angular App
Part 11. Add a New service (this post)
Table of Contents
- Posts in the Series
- Intro
- Create service script
- Run the script
- Create shared hosting for Microservice
- Prepare the host project
- Update the EF core project
- Create a project API
- Prepare for the migration
- Migration
- Create feature definition in contracts
- Create permission
- Create AppService Contract
- Create AppService in the Application
- Create a Controller
- Update the DbMigrator project
- Prepare the Administration service
Intro
In this post, we will add a new service to our microservice application.
Create service script
$serviceNameInput = $args[0]
$solution = Get-ChildItem *.sln | Select-Object -First 1 | Get-ItemProperty -Name Name
$name = (Get-Item $solution.PSPath).Basename
$pascalCase = $serviceNameInput -replace '(?:^|_)(\p{L})', { $_.Groups[1].Value.ToUpper() }
$service = $pascalCase + "Service"
$folder = $serviceNameInput.ToLower()
abp new "$name.$service" -t module --no-ui -o services\$folder
Remove-Item -Recurse -Force (Get-ChildItem -Path "services\$folder" -Recurse -Include *.IdentityServer)
Remove-Item -Recurse -Force (Get-ChildItem -Path "services\$folder" -Recurse -Include *.MongoDB.Tests)
Remove-Item -Recurse -Force (Get-ChildItem -Path "services\$folder" -Recurse -Include *.MongoDB)
Remove-Item -Recurse -Force (Get-ChildItem -Path "services\$folder" -Recurse -Include *.Host.Shared)
Remove-Item -Recurse -Force (Get-ChildItem -Path "services\$folder" -Recurse -Include *.Installer)
dotnet sln ".\$name.sln" add (Get-ChildItem -Path "services\$folder" -Recurse -Include *.csproj)
Run the script
Please make sure to run this command in the root of your project where the solution file is located.
.\newservice.ps1 project
This script will create a new service and do some cleanup and add the project to solution.
Create shared hosting for Microservice
dotnet new classlib -n Tasky.Microservice.Shared -o shared\Tasky.Microservice.Shared
We are creating this project as a shared project for all the new microservice.
Add the reference to AdministrationService
and SaaSService
<ItemGroup>
<ProjectReference Include="..\..\services\administration\src\Tasky.AdministrationService.EntityFrameworkCore\Tasky.AdministrationService.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\services\saas\src\Tasky.SaaSService.EntityFrameworkCore\Tasky.SaaSService.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\Tasky.Shared.Hosting\Tasky.Shared.Hosting.csproj" />
</ItemGroup>
Create the TaskyMicroserviceHosting
module
[DependsOn(
typeof(TaskyHostingModule),
typeof(AdministrationServiceEntityFrameworkCoreModule),
typeof(SaaSServiceEntityFrameworkCoreModule)
)]
public class TaskyMicroserviceHosting : AbpModule
{
}
Now we have the shared project we can use for all the new microservice
Prepare the host project
Add the reference the of the Shared microservice project
<ProjectReference Include="..\..\..\..\shared\Tasky.Shared.Microservice.Hosting\Tasky.Shared.Microservice.Hosting.csproj" />
Update the DependsOn
in the host project
[DependsOn(
typeof(TaskyMicroserviceHosting),
typeof(ProjectServiceApplicationModule),
typeof(ProjectServiceEntityFrameworkCoreModule),
typeof(ProjectServiceHttpApiModule)
)]
Update the host port to 7004
Update the appsettings.json
{
"App": {
"CorsOrigins": "http://localhost:4200"
},
"ConnectionStrings": {
"ProjectService": "User ID=postgres;Password=postgres;Host=localhost;Port=5432;Database=TaskyProjectService;Pooling=false;",
"SaaSService": "User ID=postgres;Password=postgres;Host=localhost;Port=5432;Database=TaskySaaSService;Pooling=false;",
"AdministrationService": "User ID=postgres;Password=postgres;Host=localhost;Port=5432;Database=TaskyAdministrationService;Pooling=false;"
},
"Redis": {
"Configuration": "127.0.0.1"
},
"AuthServer": {
"Authority": "https://localhost:7000/",
"RequireHttpsMetadata": "false",
"SwaggerClientId": "ProjectService_Swagger",
"SwaggerClientSecret": "1q2w3e*"
},
"RabbitMQ": {
"Connections": {
"Default": {
"HostName": "localhost"
}
},
"EventBus": {
"ClientName": "Tasky_ProjectService",
"ExchangeName": "Tasky"
}
}
}
Update the EF core project
create a ProjectServiceDbContextFactory
in the EF core project
public class ProjectServiceDbContextFactory : IDesignTimeDbContextFactory<ProjectServiceDbContext>
{
public ProjectServiceDbContext CreateDbContext(string[] args)
{
var configuration = BuildConfiguration();
var builder = new DbContextOptionsBuilder<ProjectServiceDbContext>()
.UseNpgsql(GetConnectionStringFromConfiguration());
return new ProjectServiceDbContext(builder.Options);
}
private static string GetConnectionStringFromConfiguration()
{
return BuildConfiguration()
.GetConnectionString(ProjectServiceDbProperties.ConnectionStringName);
}
private static IConfigurationRoot BuildConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(
Path.Combine(
Directory.GetParent(Directory.GetCurrentDirectory())?.Parent!.FullName!,
$"host{Path.DirectorySeparatorChar}Tasky.ProjectService.HttpApi.Host"
)
)
.AddJsonFile("appsettings.json", false);
return builder.Build();
}
}
Create a project API
Create the project entity
public class Project : AggregateRoot<Guid>, IMultiTenant
{
public string Name { get; set; }
public Guid? TenantId { get; set; }
public Project(string name)
{
Name = name;
}
}
Add Projects
to the DbContext
public DbSet<Project> Projects { get; set; }
Update the ProjectServiceDbContextModelCreatingExtensions
in the ConfigureProjectService
method
builder.Entity<Project>(b =>
{
//Configure table & schema name
b.ToTable(ProjectServiceDbProperties.DbTablePrefix + "Projects", ProjectServiceDbProperties.DbSchema);
b.ConfigureByConvention();
});
Update the ProjectServiceEntityFrameworkCoreModule
in the AddAbpDbContext
context.Services.AddAbpDbContext<ProjectServiceDbContext>(options =>
{
options.AddDefaultRepositories(true);
});
Prepare for the migration
Add the ef core design nuget for the migrations.
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
Once this is created delete EntityFrameworkCore
folder can be created from the host project.
Migration
To create migrations
dotnet ef migrations add Init
To update database
dotnet ef database update
Create feature definition in contracts
Add the Feature package
<PackageReference Include="Volo.Abp.Features" Version="5.2.0" />
Create ProjectServiceFeatures
for the constants
public class ProjectServiceFeatures
{
public const string GroupName = "ProjectService";
public static class Project
{
public const string Default = GroupName + ".Project";
}
}
Create ProjectServiceFeaturesDefinitionProvider
for creating permissions
public class ProjectServiceFeaturesDefinitionProvider : FeatureDefinitionProvider
{
public override void Define(IFeatureDefinitionContext context)
{
var myGroup = context.AddGroup(ProjectServiceFeatures.GroupName);
myGroup.AddFeature(
ProjectServiceFeatures.Project.Default,
defaultValue: "false",
displayName: L("Project"),
valueType: new ToggleStringValueType());
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<ProjectServiceResource>(name);
}
}
Create permission
Create ProjectServicePermissions
file
public const string GroupName = "ProjectService";
public static class Project
{
public const string Default = GroupName + ".Project";
public const string Create = Default + ".Create";
}
Create ProjectServicePermissionDefinitionProvider
file
public class ProjectServicePermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var projectGroup = context.AddGroup(ProjectServicePermissions.GroupName, L("Permission:ProjectService"));
var projectPermission = projectGroup.AddPermission(ProjectServicePermissions.Project.Default, L("Permission:ProjectService:Default"));
projectPermission.AddChild(ProjectServicePermissions.Project.Create);
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<ProjectServiceResource>(name);
}
}
Create AppService Contract
Create ProjectDto
file
public class ProjectDto : EntityDto<Guid>
{
public string Name { get; set; }
}
Create IProjectAppService
file
public interface IProjectAppService : IApplicationService
{
Task<List<ProjectDto>> GetAllAsync();
Task<ProjectDto> Create(ProjectDto projectDto);
}
Create AppService in the Application
Create ProjectAppService
file
[RequiresFeature(ProjectServiceFeatures.Project.Default)]
[Authorize(ProjectServicePermissions.Project.Default)]
public class ProjectAppService : ProjectServiceAppService, IProjectAppService
{
private readonly IRepository<Project, Guid> repository;
public ProjectAppService(IRepository<Project, Guid> repository)
{
this.repository = repository;
}
[Authorize(ProjectServicePermissions.Project.Default)]
public async Task<List<ProjectDto>> GetAllAsync()
{
var projects = await repository.GetListAsync();
return ObjectMapper.Map<List<Project>,List<ProjectDto>>(projects);
}
[Authorize(ProjectServicePermissions.Project.Create)]
public async Task<ProjectDto> Create(ProjectDto projectDto)
{
var project = await repository.InsertAsync(new Project(projectDto.Name));
return new ProjectDto
{
Name = project.Name
};
}
}
Create a Controller
[Area(ProjectServiceRemoteServiceConsts.ModuleName)]
[RemoteService(Name = ProjectServiceRemoteServiceConsts.RemoteServiceName)]
[Route("api/project")]
public class ProjectController : ProjectServiceController, IProjectAppService
{
private readonly IProjectAppService _projectService;
public ProjectController(IProjectAppService sampleAppService)
{
_projectService = sampleAppService;
}
[HttpGet]
public async Task<List<ProjectDto>> GetAllAsync()
{
return await _projectService.GetAllAsync();
}
[HttpPost]
public async Task<ProjectDto> Create(ProjectDto projectDto)
{
return await _projectService.Create(projectDto);
}
}
Update the DbMigrator project
Add the project reference for the new service
<ProjectReference Include="..\..\services\project\src\Tasky.ProjectService.Application.Contracts\Tasky.ProjectService.Application.Contracts.csproj" />
<ProjectReference Include="..\..\services\project\src\Tasky.ProjectService.EntityFrameworkCore\Tasky.ProjectService.EntityFrameworkCore.csproj" />
Update the dependency
typeof(ProjectServiceEntityFrameworkCoreModule),
typeof(ProjectServiceApplicationContractsModule)
Update the MigrateAllDatabasesAsync
function in the TaskyDbMigrationService
file
await MigrateDatabaseAsync<ProjectServiceDbContext>(cancellationToken);
Update the appsettings.json
{
"ConnectionStrings": {
"SaaSService": "User ID=postgres;Password=postgres;Host=localhost;Port=5432;Database=TaskySaaSService;Pooling=false;",
"IdentityService": "User ID=postgres;Password=postgres;Host=localhost;Port=5432;Database=TaskyIdentityService;Pooling=false;",
"AdministrationService": "User ID=postgres;Password=postgres;Host=localhost;Port=5432;Database=TaskyAdministrationService;Pooling=false;",
"ProjectService": "User ID=postgres;Password=postgres;Host=localhost;Port=5432;Database=TaskyProjectService;Pooling=false;"
},
"ApiScope": [
"AuthServer",
"SaaSService",
"IdentityService",
"AdministrationService",
"ProjectService"
],
"ApiResource": [
"AuthServer",
"SaaSService",
"IdentityService",
"AdministrationService",
"ProjectService"
],
"Clients": [
{
"ClientId": "Tasky_Web",
"ClientSecret": "1q2w3e*",
"RootUrls": ["https://localhost:5000"],
"Scopes": ["SaaSService", "IdentityService", "AdministrationService", "ProjectService"],
"GrantTypes": ["hybrid"],
"RedirectUris": ["https://localhost:5000/signin-oidc"],
"PostLogoutRedirectUris": ["https://localhost:5000/signout-callback-oidc"],
"AllowedCorsOrigins": ["https://localhost:5000"]
},
{
"ClientId": "Tasky_App",
"ClientSecret": "1q2w3e*",
"RootUrls": ["http://localhost:4200"],
"Scopes": [
"AuthServer",
"SaaSService",
"IdentityService",
"AdministrationService",
"ProjectService"
],
"GrantTypes": ["authorization_code"],
"RedirectUris": ["http://localhost:4200"],
"PostLogoutRedirectUris": ["http://localhost:4200"],
"AllowedCorsOrigins": ["http://localhost:4200"]
},
{
"ClientId": "AdministrationService_Swagger",
"ClientSecret": "1q2w3e*",
"RootUrls": ["https://localhost:7001"],
"Scopes": ["SaaSService", "IdentityService", "AdministrationService", "ProjectService"],
"GrantTypes": ["authorization_code"],
"RedirectUris": ["https://localhost:7001/swagger/oauth2-redirect.html"],
"PostLogoutRedirectUris": ["https://localhost:7001/signout-callback-oidc"],
"AllowedCorsOrigins": ["https://localhost:7001"]
},
{
"ClientId": "IdentityService_Swagger",
"ClientSecret": "1q2w3e*",
"RootUrls": ["https://localhost:7002"],
"Scopes": ["SaaSService", "IdentityService", "AdministrationService", "ProjectService"],
"GrantTypes": ["authorization_code"],
"RedirectUris": ["https://localhost:7002/swagger/oauth2-redirect.html"],
"PostLogoutRedirectUris": ["https://localhost:7002"],
"AllowedCorsOrigins": ["https://localhost:7002"]
},
{
"ClientId": "SaaSService_Swagger",
"ClientSecret": "1q2w3e*",
"RootUrls": ["https://localhost:7003"],
"Scopes": ["SaaSService", "IdentityService", "AdministrationService", "ProjectService"],
"GrantTypes": ["authorization_code"],
"RedirectUris": ["https://localhost:7003/swagger/oauth2-redirect.html"],
"PostLogoutRedirectUris": ["https://localhost:7003"],
"AllowedCorsOrigins": ["https://localhost:7003"]
},
{
"ClientId": "ProjectService_Swagger",
"ClientSecret": "1q2w3e*",
"RootUrls": ["https://localhost:7004"],
"Scopes": ["SaaSService", "IdentityService", "AdministrationService", "ProjectService"],
"GrantTypes": ["authorization_code"],
"RedirectUris": ["https://localhost:7004/swagger/oauth2-redirect.html"],
"PostLogoutRedirectUris": ["https://localhost:7004"],
"AllowedCorsOrigins": ["https://localhost:7004"]
}
]
}
Prepare the Administration service
Add reference to the ProjectService.Application.Contracts
project.
<ProjectReference Include="..\..\..\project\src\Tasky.ProjectService.Application.Contracts\Tasky.ProjectService.Application.Contracts.csproj" />
Update the dependency in the Admin Host
typeof(ProjectServiceApplicationContractsModule)
Repo: https://github.com/antosubash/Tasky
Related Posts
Migrating Tye to Aspire
In this post we will see how to migrate the Tye to Aspire
.Net Microservice template with ABP
In this post I will show you how to create ABP microservice using a dotnet new template.
Migrating Identity Service to OpenIddict Module
In this post we will see how to replace Identity server with OpenIddict in our microservice