Skip to content

Make Solution Template Ready

Apoorv Rane edited this page Jul 1, 2022 · 6 revisions

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 NeoBoilerplate

Solution, Projects and Folders name

  • Hide the .vs folder if not hidden already

.vs hidden

  • Remove user secrets and drive connection strings from appsettings
  • Change names of context classes and connection strings in all the appsettings files

Context classes

"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
  1. NeoCA.Api.csproj
          <!--#if (Database == "SQLite")-->
	  <PackageReference Include="AspNetCore.HealthChecks.Sqlite" Version="6.0.1" />
	  <PackageReference Include="AspNetCore.HealthChecks.UI.SQLite.Storage" Version="6.0.2" />
	  <!--#endif -->	  
	  <!--#if (Database == "MSSQL")-->
          <PackageReference Include="AspNetCore.HealthChecks.UI.SqlServer.Storage" Version="6.0.1-rc2.4" />
          <PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="6.0.1-rc2.7" />
	  <!--#endif -->
	  <!--#if (Database == "MySQL") -->
          <PackageReference Include="AspNetCore.HealthChecks.MySql" Version="6.0.1-rc1.1" />
          <PackageReference Include="AspNetCore.HealthChecks.UI.MySql.Storage" Version="6.0.1-rc2.4" />
	  <!--#endif -->
	  <!--#if (Database == "PGSQL") -->
          <PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="6.0.1-rc2.3" />
          <PackageReference Include="AspNetCore.HealthChecks.UI.PostgreSQL.Storage" Version="6.0.1-rc2.4" />
	  <!--#endif -->
  1. NeoCA.Identity.csproj
          <!--#if (Database == "SQLite")-->
	  <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.1" />
	  <!--#endif-->    
	  <!--#if (Database == "MSSQL")-->
          <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.1" />
	  <!--#endif-->
	  <!--#if (Database == "MySQL")-->
          <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.0" />
	  <!--#endif-->
	  <!--#if (Database == "PGSQL")-->
          <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.2" />
	  <!--#endif-->
  1. NeoCA.Persistence.csproj
          <!--#if (Database == "SQLite")-->
          <Folder Include="Migrations\SQLite\" />
	  <!--#endif-->    
	  <!--#if (Database == "MSSQL")-->
          <Folder Include="Migrations\MSSQL\" />
	  <!--#endif-->
	  <!--#if (Database == "MySQL")-->
          <Folder Include="Migrations\MySQL\" />
	  <!--#endif-->
	  <!--#if (Database == "PGSQL")-->
          <Folder Include="Migrations\PGSQL\" />
	  <!--#endif-->
  • Add if else in HealthCheckExtensions, IdentityExtensions, PersistenceServiceRegistration and DbFixture for database
  1. HealthCheckExtensions.cs
            //#if (Database == "MSSQL")
            services.AddHealthChecks()
                        .AddSqlServer(configuration["ConnectionStrings:IdentityConnectionString"], tags: new[] {
                            "db",
                            "all"})
                        .AddUrlGroup(new Uri(configuration["API:WeatertherInfo"]), tags: new[] {
                            "testdemoUrl",
                            "all"
                        });
                    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.AddHealthChecks()
                        .AddNpgSql(configuration["ConnectionStrings:IdentityConnectionString"], tags: new[] {
                            "db",
                            "all"})
                        .AddUrlGroup(new Uri(configuration["API:WeatertherInfo"]), tags: new[] {
                            "testdemoUrl",
                            "all"
                        });
                    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.AddHealthChecks()
                        .AddMySql(configuration["ConnectionStrings:IdentityConnectionString"], tags: new[] {
                            "db",
                            "all"})
                        .AddUrlGroup(new Uri(configuration["API:WeatertherInfo"]), tags: new[] {
                            "testdemoUrl",
                            "all"
                        });
                    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.AddHealthChecks()
                        .AddSqlite(configuration["ConnectionStrings:IdentityConnectionString"], tags: new[] {
                            "db",
                            "all"})
                        .AddUrlGroup(new Uri(configuration["API:WeatertherInfo"]), tags: new[] {
                            "testdemoUrl",
                            "all"
                        });
            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;
  1. 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"),
            new MySqlServerVersion(new Version(8, 0,11)),
                         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
  1. 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"),
            new MySqlServerVersion(new Version(8, 0, 11))
            ));
            //#endif
            //#if (Database == "SQLite")
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlite(configuration.GetConnectionString("ApplicationConnectionString")));
            //#endif
  1. DbFixture.cs
            //#if (Database == "MSSQL")
            ApplicationConnString = $"Server=localhost,1433;Database={ApplicationDbName};User=sa;Password=2@LaiNw)PDvs^t>L!Ybt]6H^%h3U>M;TrustServerCertificate=True;";
                    IdentityConnString = $"Server=localhost,1433;Database={IdentityDbName};User=sa;Password=2@LaiNw)PDvs^t>L!Ybt]6H^%h3U>M;TrustServerCertificate=True;";
                    HealthCheckConnString = $"Server=localhost,1433;Database={HealthCheckDbName};User=sa;Password=2@LaiNw)PDvs^t>L!Ybt]6H^%h3U>M;TrustServerCertificate=True;";
                    applicationBuilder.UseSqlServer(ApplicationConnString);
                    //#if (Authentication == "Identity")
                    identityBuilder.UseSqlServer(IdentityConnString);
                    //#endif
            //#endif
            //#if (Database == "PGSQL")
            ApplicationConnString = $"Server=localhost;Port=5430;Database={ApplicationDbName};User Id=root;Password=root;CommandTimeout = 300;";
                    IdentityConnString = $"Server=localhost;Port=5430;Database={IdentityDbName};User Id=root;Password=root;CommandTimeout = 300;";

                    HealthCheckConnString = $"Server=localhost;Port=5430;Database={HealthCheckDbName};User Id=root;Password=root;CommandTimeout = 300;";
                    applicationBuilder.UseNpgsql(ApplicationConnString);
                    //#if (Authentication == "Identity")
                    identityBuilder.UseNpgsql(IdentityConnString);
                    //#endif
            //#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, new MySqlServerVersion(new Version(8, 0, 11)));
                    //#if (Authentication == "Identity")
                    identityBuilder.UseMySql(IdentityConnString, new MySqlServerVersion(new Version(8, 0, 11)));
                    //#endif
            //#endif
            //#if (Database == "SQLite")
            ApplicationConnString = $"Data Source=..//..//..//db//{ApplicationDbName}";
            IdentityConnString = $"Data Source=..//..//..//db//{IdentityDbName}";
            HealthCheckConnString = $"Data Source=..//..//..//db//{HealthCheckDbName}";
            applicationBuilder.UseSqlite(ApplicationConnString);
            //#if (Authentication == "Identity")
            identityBuilder.UseSqlite(IdentityConnString);
            //#endif
            //#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:
      - "5430:5432"
    networks:
      - clean-network
##endif

##if (Database == "MySQL")
  mysql:
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
    ports:
      - '3307:3307'
    networks:
      - clean-network
  ##endif
  
##if (Database == "SQLite")
  sqlite:
    image: nouchka/sqlite3:latest
    stdin_open: true
    tty: true
    networks:
      - clean-network
    volumes:
##if (Communication == "REST")
      - ./test/NeoBoilerplate.API.IntegrationTests/./db:/root/db/REST
##endif
##if (Communication == "GRPC")
      - ./test/NeoBoilerplate.gRPC.IntegrationTests/./db:/root/db/gRPC
##endif
##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 (Authentication == "Identity")
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NeoBoilerplate.Persistence.IntegrationTests", "test\NeoBoilerplate.Persistence.IntegrationTests\NeoBoilerplate.Persistence.IntegrationTests.csproj", "{152B1EBF-3CA4-46C9-AE05-8783A64BEA41}"
	ProjectSection(ProjectDependencies) = postProject
		{8747A220-8B36-4E4C-81CB-EA69F7D64D4F} = {8747A220-8B36-4E4C-81CB-EA69F7D64D4F}
		{85390A65-02D1-410C-846B-197DF5E01168} = {85390A65-02D1-410C-846B-197DF5E01168}
		{FF97E1A5-944C-4FEE-AE96-B6EA9F1E92DF} = {FF97E1A5-944C-4FEE-AE96-B6EA9F1E92DF}
		{41A0BEF1-34B6-468A-83D7-4F8C6A8FB654} = {41A0BEF1-34B6-468A-83D7-4F8C6A8FB654}
	EndProjectSection
EndProject
//#endif
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NeoBoilerplate.Application.UnitTests", "test\NeoBoilerplate.Application.UnitTests\NeoBoilerplate.Application.UnitTests.csproj", "{FD2A03DF-CA97-4295-B196-627E9C488FE1}"
EndProject
//#if (Authentication == "Identity")
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NeoBoilerplate.Identity.UnitTests", "test\NeoBoilerplate.Identity.UnitTests\NeoBoilerplate.Identity.UnitTests.csproj", "{C8EC46C7-1BA2-4B3D-AD75-3989231A6E9F}"
EndProject
//#endif
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NeoBoilerplate.Infrastructure.UnitTests", "test\NeoBoilerplate.Infrastructure.UnitTests\NeoBoilerplate.Infrastructure.UnitTests.csproj", "{5710746C-B4DA-4A76-9270-6AD9E13C4737}"
EndProject
//#if (Communication == "REST")
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "API", "API", "{9EC4D129-E267-4D13-BC11-7C5EF6CF196B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NeoBoilerplate.Api", "src\API\NeoBoilerplate.Api\NeoBoilerplate.Api.csproj", "{A0630B4A-4528-4EB9-84F4-F21AF04032DE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NeoBoilerplate.API.IntegrationTests", "test\NeoBoilerplate.API.IntegrationTests\NeoBoilerplate.API.IntegrationTests.csproj", "{0E6F31B4-22B7-4F68-9AB7-CBC1AB22CBEC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NeoBoilerplate.API.UnitTests", "test\NeoBoilerplate.API.UnitTests\NeoBoilerplate.API.UnitTests.csproj", "{67ED70F8-67A4-4120-A30D-BA4BC597047C}"
EndProject
//#endif
//#if (Communication == "GRPC")
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gRPC", "gRPC", "{5A3A2012-EECC-456F-B1A8-4ADF765E90B0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NeoBoilerplate.gRPC", "src\gRPC\NeoBoilerplate.gRPC\NeoBoilerplate.gRPC.csproj", "{31B16719-E1AF-477F-9D05-6312F7C0646E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NeoBoilerplate.gRPC.IntegrationTests", "test\NeoBoilerplate.gRPC.IntegrationTests\NeoBoilerplate.gRPC.IntegrationTests.csproj", "{F48D57BD-2E8C-43A8-9628-DE69B3844318}"
EndProject
//#endif
//#if (Authentication == "External")
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NeoBoilerplate.Auth", "src\Infrastructure\NeoBoilerplate.Auth\NeoBoilerplate.Auth.csproj", "{B52C0D38-9904-4CAB-91DD-6EBB55526902}"
EndProject
//#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/netcore6-0-template/tree/main/Content

Clone this wiki locally