Make Solution Template Ready - NeoSOFT-Technologies/rest-dot-net-core GitHub Wiki
Description
When you create a new .NET Core project with dotnet new command it is possible to select from a list of predefined templates. This is a very handy feature, but out-of-the-box templates are not really convenient, additional changes are required afterwards to make the project fit for its purpose. In a situation where many projects with similar structures are created, such as many micro-services, a custom template is very helpful. Users can define a custom template and easily create new projects out of it. In order to convert our solution to template, we need to make some changes to our existing solution.
Steps to make solution template-ready
- Change solution, project and folder names to simpler ones like NeoCA
- Hide the .vs folder if not hidden already
- Remove user secrets and drive connection strings from appsettings
- Change names of context classes and connection strings in all the appsettings files
"ConnectionStrings": {
"ApplicationConnectionString": "",
"IdentityConnectionString": "",
"HealthCheckConnectionString": ""
}
- Remove dbProvider from appsettings
- Remove dbProvider switch case from HealthCheckExtensions, IdentityExtensions, PersistenceExtensions and DbFixture
- Add if else in .csproj for database related packages if not present
- NeoCA.Api.csproj
<!--#if (Database == "MSSQL")-->
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="3.1.1" />
<PackageReference Include="AspNetCore.HealthChecks.UI.SqlServer.Storage" Version="3.1.2" />
<!--#endif-->
<!--#if (Database == "PGSQL")-->
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="3.1.1" />
<PackageReference Include="AspNetCore.HealthChecks.UI.PostgreSQL.Storage" Version="3.1.2" />
<!--#endif-->
<!--#if (Database == "MySQL")-->
<PackageReference Include="AspNetCore.HealthChecks.MySql" Version="3.1.1" />
<PackageReference Include="AspNetCore.HealthChecks.UI.MySql.Storage" Version="3.1.2" />
<!--#endif-->
<!--#if (Database == "SQLite")-->
<PackageReference Include="AspNetCore.HealthChecks.Sqlite" Version="3.1.1" />
<PackageReference Include="AspNetCore.HealthChecks.UI.SQLite.Storage" Version="3.1.1" />
<!--#endif-->
- NeoCA.Identity.csproj
<!--#if (Database == "MSSQL")-->
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.10" />
<!--#endif-->
<!--#if (Database == "PGSQL")-->
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.1" />
<!--#endif-->
<!--#if (Database == "MySQL")-->
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.1.1" />
<!--#endif-->
<!--#if (Database == "SQLite")-->
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.2" />
<!--#endif-->
- NeoCA.Persistence.csproj
<!--#if (Database == "MSSQL")-->
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.10" />
<!--#endif-->
<!--#if (Database == "PGSQL")-->
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.1" />
<!--#endif-->
<!--#if (Database == "MySQL")-->
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.1.1" />
<!--#endif-->
<!--#if (Database == "SQLite")-->
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.2" />
<!--#endif-->
- Add if else in HealthCheckExtensions, IdentityExtensions, PersistenceServiceRegistration and DbFixture for database
- HealthCheckExtensions.cs
services.AddHealthChecks()
//#if (Database == "MSSQL")
.AddSqlServer(configuration["ConnectionStrings:IdentityConnectionString"], tags: new[] {
"db",
"all"})
//#endif
//#if (Database == "PGSQL")
.AddNpgSql(configuration["ConnectionStrings:IdentityConnectionString"], tags: new[] {
"db",
"all"})
//#endif
//#if (Database == "MySQL")
.AddMySql(configuration["ConnectionStrings:IdentityConnectionString"], tags: new[] {
"db",
"all"})
//#endif
//#if (Database == "SQLite")
.AddSqlite(configuration["ConnectionStrings:IdentityConnectionString"], tags: new[] {
"db",
"all"})
//#endif
.AddUrlGroup(new Uri(configuration["API:WeatertherInfo"]), tags: new[] {
"testdemoUrl",
"all"
});
//#if (Database == "MSSQL")
services.AddHealthChecksUI(opt =>
{
opt.SetEvaluationTimeInSeconds(15); //time in seconds between check
opt.MaximumHistoryEntriesPerEndpoint(60); //maximum history of checks
opt.SetApiMaxActiveRequests(1); //api requests concurrency
opt.AddHealthCheckEndpoint("API", "/healthz"); //map health check api
}).AddSqlServerStorage(configuration["ConnectionStrings:HealthCheckConnectionString"]);
//#endif
//#if (Database == "PGSQL")
services.AddHealthChecksUI(opt =>
{
opt.SetEvaluationTimeInSeconds(15); //time in seconds between check
opt.MaximumHistoryEntriesPerEndpoint(60); //maximum history of checks
opt.SetApiMaxActiveRequests(1); //api requests concurrency
opt.AddHealthCheckEndpoint("API", "/healthz"); //map health check api
}).AddPostgreSqlStorage(configuration["ConnectionStrings:HealthCheckConnectionString"]);
//#endif
//#if (Database == "MySQL")
services.AddHealthChecksUI(opt =>
{
opt.SetEvaluationTimeInSeconds(15); //time in seconds between check
opt.MaximumHistoryEntriesPerEndpoint(60); //maximum history of checks
opt.SetApiMaxActiveRequests(1); //api requests concurrency
opt.AddHealthCheckEndpoint("API", "/healthz"); //map health check api
}).AddMySqlStorage(configuration["ConnectionStrings:HealthCheckConnectionString"]);
//#endif
//#if (Database == "SQLite")
services.AddHealthChecksUI(opt =>
{
opt.SetEvaluationTimeInSeconds(15); //time in seconds between check
opt.MaximumHistoryEntriesPerEndpoint(60); //maximum history of checks
opt.SetApiMaxActiveRequests(1); //api requests concurrency
opt.AddHealthCheckEndpoint("API", "/healthz"); //map health check api
}).AddSqliteStorage(configuration["ConnectionStrings:HealthCheckConnectionString"]);
//#endif
return services;
- IdentityExtensions.cs
//#if (Database == "MSSQL")
services.AddDbContext<IdentityDbContext>(
options => options.UseSqlServer(configuration.GetConnectionString("IdentityConnectionString"),
b => b.MigrationsAssembly(typeof(IdentityDbContext).Assembly.FullName)));
//#endif
//#if (Database == "PGSQL")
services.AddDbContext<IdentityDbContext>(
options => options.UseNpgsql(configuration.GetConnectionString("IdentityConnectionString"),
b => b.MigrationsAssembly(typeof(IdentityDbContext).Assembly.FullName)));
//#endif
//#if (Database == "MySQL")
services.AddDbContext<IdentityDbContext>(
options => options.UseMySql(configuration.GetConnectionString("IdentityConnectionString"),
b => b.MigrationsAssembly(typeof(IdentityDbContext).Assembly.FullName)));
//#endif
//#if (Database == "SQLite")
services.AddDbContext<IdentityDbContext>(
options => options.UseSqlite(configuration.GetConnectionString("IdentityConnectionString"),
b => b.MigrationsAssembly(typeof(IdentityDbContext).Assembly.FullName)));
//#endif
- PersistenceServiceRegistration.cs
//#if (Database == "MSSQL")
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("ApplicationConnectionString")));
//#endif
//#if (Database == "PGSQL")
services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(configuration.GetConnectionString("ApplicationConnectionString")));
//#endif
//#if (Database == "MySQL")
services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(configuration.GetConnectionString("ApplicationConnectionString")));
//#endif
//#if (Database == "SQLite")
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(configuration.GetConnectionString("ApplicationConnectionString")));
//#endif
- DbFixture.cs
//#if (Database == "MSSQL")
ApplicationConnString = $"Server=localhost,1433;Database={ApplicationDbName};User=sa;Password=2@LaiNw)PDvs^t>L!Ybt]6H^%h3U>M";
IdentityConnString = $"Server=localhost,1433;Database={IdentityDbName};User=sa;Password=2@LaiNw)PDvs^t>L!Ybt]6H^%h3U>M";
HealthCheckConnString = $"Server=localhost,1433;Database={HealthCheckDbName};User=sa;Password=2@LaiNw)PDvs^t>L!Ybt]6H^%h3U>M";
applicationBuilder.UseSqlServer(ApplicationConnString);
identityBuilder.UseSqlServer(IdentityConnString);
//#endif
//#if (Database == "PGSQL")
ApplicationConnString = $"Server=localhost;Port=5432;Database={ApplicationDbName};User Id=root;Password=root;";
IdentityConnString = $"Server=localhost;Port=5432;Database={IdentityDbName};User Id=root;Password=root;";
HealthCheckConnString = $"Server=localhost;Port=5432;Database={HealthCheckDbName};User Id=root;Password=root;";
applicationBuilder.UseNpgsql(ApplicationConnString);
identityBuilder.UseNpgsql(IdentityConnString);
//#endif
//#if (Database == "MySQL")
ApplicationConnString = $"Server=localhost;Port=3306;Database={ApplicationDbName};Userid=root;Password=root;";
IdentityConnString = $"Server=localhost;Port=3306;Database={IdentityDbName};Userid=root;Password=root;";
HealthCheckConnString = $"Server=localhost;Port=3306;Database={HealthCheckDbName};Userid=root;Password=root;";
applicationBuilder.UseMySql(ApplicationConnString);
identityBuilder.UseMySql(IdentityConnString);
//#endif
//#if (Database == "SQLite")
ApplicationConnString = $"Data Source=..//..//..//db//{ApplicationDbName}";
IdentityConnString = $"Data Source=..//..//..//db//{IdentityDbName}";
HealthCheckConnString = $"Data Source=..//..//..//db//{HealthCheckDbName}";
applicationBuilder.UseSqlite(ApplicationConnString);
identityBuilder.UseSqlite(IdentityConnString);
//#endif
- Delete UI Project if present
- Delete bin, obj folders from all the projects
- Add if else in docker-compose if not present
##if (Database == "MSSQL")
mssql:
image: "mcr.microsoft.com/mssql/server"
ports:
- "1433:1433"
environment:
SA_PASSWORD: "2@LaiNw)PDvs^t>L!Ybt]6H^%h3U>M"
ACCEPT_EULA: "Y"
networks:
- clean-network
##endif
##if (Database == "PGSQL")
postgres:
image: postgres
environment:
POSTGRES_USER: root
POSTGRES_PASSWORD: root
ports:
- "5432:5432"
networks:
- clean-network
##endif
##if (Database == "MySQL")
mysql:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: root
ports:
- '3306:3306'
networks:
- clean-network
##endif
##if (Database == "SQLite")
sqlite:
image: nouchka/sqlite3:latest
stdin_open: true
tty: true
networks:
- clean-network
volumes:
- ./test/NeoCA.API.IntegrationTests/./db:/root/db/
##endif
- Add if else in sln to add/remove projects if needed. Keep GUIDs from your sln file as they are. Also, add if else wherever you find GUIDs(right hand side ones) for the following projects.
//#if (Communication == "REST")
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "API", "API", "{153F204B-20E6-46A6-BB73-BAD48B580CC4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NeoCA.Api", "src\API\NeoCA.Api\NeoCA.Api.csproj", "{17B7B793-4B9F-4040-8722-6BF5E8ED6FFB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NeoCA.API.IntegrationTests", "test\NeoCA.API.IntegrationTests\NeoCA.API.IntegrationTests.csproj", "{93AE446B-39C0-49BE-BA11-85293B15C929}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NeoCA.API.UnitTests", "test\NeoCA.API.UnitTests\NeoCA.API.UnitTests.csproj", "{6559162B-B5AE-499B-9850-9D556E7B54C8}"
EndProject
//#endif
//#if (Communication == "gRPC")
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gRPC", "gRPC", "{DF730924-E57B-4551-8F91-C8AA5A4274A7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NeoCA.gRPC", "src\gRPC\NeoCA.gRPC\NeoCA.gRPC.csproj", "{C6B70E88-F7FB-47C1-8A78-E5064DEED58F}"
//#endif
- Change the last step in Integration Testing.yml workflow to following
- name: Test
run: dotnet test --filter FullyQualifiedName~IntegrationTests --no-build --verbosity normal
Reference project
https://github.com/NeoSOFT-Technologies/netcore3-1-template/tree/main/Content