46 Commits

Author SHA1 Message Date
aaea2c30a5 Excluding dbcontext models files from analysis 2025-02-21 18:20:59 +01:00
3ed9a15d09 Excluding database models files from analysis 2025-02-21 18:06:53 +01:00
5ef25d601f Excluding migrations files from analysis 2025-02-21 13:55:34 +01:00
810f23f814 Adding new tests 2025-02-21 13:21:06 +01:00
8d8a7a4c77 Updated README 2024-11-30 01:01:06 +01:00
b26c553d12 Fixed multiple db contexts 2024-11-30 00:26:05 +01:00
0ab8d7f7d1 Fixing connection strings - wip 2024-11-29 22:23:40 +01:00
998a4d5ab9 Adding database configuration - wip 2024-09-05 01:52:07 +02:00
b638c48104 Fixed launch.json 2024-09-04 17:17:16 +02:00
ada6967234 Minor fixes + changed debug launch.json file 2024-09-04 15:51:56 +02:00
286a9eff72 Updated gitignore 2024-05-14 21:56:09 +02:00
ebf8237be6 Fixing dotnet coverage + minor fixes 2024-05-14 19:47:25 +02:00
6c34f3e380 Fixing coverage - 11 2024-05-14 00:27:25 +02:00
fb578026eb Fixing coverage - 10 2024-05-13 21:43:49 +02:00
57391d3e51 Fixing coverage - 9 2024-05-13 01:13:09 +02:00
277d6b99fd Fixing coverage - 8 2024-05-13 00:58:40 +02:00
bbfac5eb07 Changed method name 2024-05-13 00:27:36 +02:00
4663e10f96 Revert "Fixing coverage - 7"
This reverts commit b393abe948.
2024-05-13 00:26:17 +02:00
b393abe948 Fixing coverage - 7 2024-05-13 00:21:21 +02:00
3e4372c1b7 Removed unuseful configuration from RootController 2024-05-12 23:53:15 +02:00
6b53139fd4 Fixing coverage - 6 2024-05-12 23:47:10 +02:00
e941649d75 Fixing coverage - 5 2024-05-12 23:19:54 +02:00
3808adb6db Fixing coverage - 4 2024-05-12 22:56:15 +02:00
8ee14255ce Fixing coverage - 3 2024-05-12 21:50:23 +02:00
7c324e9647 Fixing coverage - 2 2024-05-12 19:54:27 +02:00
5d4e351982 Fixing coverage 2024-05-12 19:36:23 +02:00
1739961bc2 Added basic README + minor fixes 2024-05-12 17:57:27 +02:00
567e5abf07 Fixing SonarCloud integration - 10 2024-05-05 15:49:25 +02:00
c27971609a Fixing SonarCloud integration - 9 2024-05-05 03:40:20 +02:00
41251bb110 Fixing SonarCloud integration - 8 2024-05-05 02:56:44 +02:00
f53e289f51 Fixing pipeline 2024-05-05 02:48:10 +02:00
23ca30f084 Fixing SonarCloud integration - 7 2024-05-05 02:44:46 +02:00
a1a1a667b3 Fixing SonarCloud integration - 6 2024-05-05 02:39:56 +02:00
12d56ebcc1 Fixing SonarCloud integration - 5 2024-05-05 02:37:59 +02:00
54be2c8732 Fixing SonarCloud integration - 4 2024-05-05 02:13:08 +02:00
af470ea869 Fixing SonarCloud integration - 3 2024-05-04 20:12:21 +02:00
1446803fb8 Fixing SonarCloud integration - 2 2024-05-04 20:05:53 +02:00
0929ca2d94 Fixing SonarCloud integration 2024-05-04 19:52:45 +02:00
39b7f4f3b3 Fixing SonarCloud integration + fixing CodeSmells - 2 2024-05-04 19:29:44 +02:00
79b66cb8b4 Fixing SonarCloud integration + fixing CodeSmells 2024-05-04 18:47:58 +02:00
708966fcf8 Various changes (Controller and test)
- Added RootController
- Added new methods in BaseController
- Added BaseResponse
- Changed Program structure
- Added first basic test for Program
2024-05-04 17:51:11 +02:00
d3ba42e57a Adding base controller 2024-04-07 20:04:41 +02:00
716d75a072 Added base NLog configuration 2024-04-07 17:50:11 +02:00
61f0dab8df Removed unnecessary check for null 2024-04-07 17:03:33 +02:00
e78e5b205f Merge branch 'test' 2024-04-07 16:55:28 +02:00
ddb5223572 Adding test configuration 2024-03-10 20:36:07 +01:00
53 changed files with 2082 additions and 158 deletions

View File

@@ -37,12 +37,21 @@ jobs:
run: |
New-Item -Path .\.sonar\scanner -ItemType Directory
dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner
- name: Install dotnet-coverage tool
run: dotnet tool install --global dotnet-coverage
- name: Build and analyze
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SQLSERVER_DB_SERVER: ${{ secrets.SQLSERVER_DB_SERVER }}
MONGO_DB_SERVER: ${{ secrets.MONGO_DB_SERVER }}
shell: powershell
run: |
.\.sonar\scanner\dotnet-sonarscanner begin /k:"csimonapastore_BasicDotnetTemplate" /o:"csimonapastore-github" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io"
dotnet build
.\.sonar\scanner\dotnet-sonarscanner begin /k:"csimonapastore_BasicDotnetTemplate" /o:"csimonapastore-github" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml /d:sonar.exclusions="**/Migrations/**.cs, **/Models/Database/**.cs, **/Core/Database/**.cs"
dotnet restore
dotnet build --no-incremental
dotnet-coverage collect "dotnet test" -f xml -o "coverage.xml"
dotnet test ./MainProject.Tests/MainProject.Tests.csproj --collect "Code Coverage" --results-directory "./TestResults"
Get-Content -Path "coverage.xml"
.\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"

4
.gitignore vendored
View File

@@ -1,3 +1,5 @@
**/obj
**/bin
appsettings.*.json
**/appsettings.*.json
**/coverage*.xml
.fake

31
.vscode/launch.json vendored
View File

@@ -2,18 +2,13 @@
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md
"name": ".NET Core Launch (console)",
"name": ".NET Rest Api - Watch",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/bin/Debug/net8.0/BasicDotnetTemplate.dll",
"program": "${workspaceFolder}/MainProject/bin/Debug/net8.0/MainProject.dll",
"args": [],
"cwd": "${workspaceFolder}",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole",
"stopAtEntry": false,
"env": {
@@ -22,17 +17,31 @@
"ASPNETCORE_DETAILEDERRORS": "1",
"ASPNETCORE_SHUTDOWNTIMEOUTSECONDS": "3"
},
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)",
"uriFormat": "%s/swagger"
},
},
"postDebugTask": "watch"
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}
],
"tasks": {
"label": "watch",
"type": "shell",
"command": "dotnet",
"args": [
"watch",
"run"
],
"problemMatcher": "$msCompile",
"group": {
"kind": "build",
"isDefault": true
}
}
}

6
.vscode/tasks.json vendored
View File

@@ -7,7 +7,7 @@
"type": "process",
"args": [
"build",
"${workspaceFolder}/BasicDotnetTemplate.csproj",
"${workspaceFolder}/BasicDotnetTemplate.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
@@ -19,7 +19,7 @@
"type": "process",
"args": [
"publish",
"${workspaceFolder}/BasicDotnetTemplate.csproj",
"${workspaceFolder}/BasicDotnetTemplate.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
@@ -33,7 +33,7 @@
"watch",
"run",
"--project",
"${workspaceFolder}/BasicDotnetTemplate.csproj"
"${workspaceFolder}/BasicDotnetTemplate.sln"
],
"problemMatcher": "$msCompile"
}

View File

@@ -1,24 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="NLog" Version="5.2.8" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters.Abstractions" Version="8.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
</ItemGroup>
</Project>

28
BasicDotnetTemplate.sln Normal file
View File

@@ -0,0 +1,28 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MainProject", "MainProject\MainProject.csproj", "{5BC4F94C-35BC-4436-A107-9D85E53A9E84}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MainProject.Tests", "MainProject.Tests\MainProject.Tests.csproj", "{A672CB00-DC99-4847-A384-B857C5E69301}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5BC4F94C-35BC-4436-A107-9D85E53A9E84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5BC4F94C-35BC-4436-A107-9D85E53A9E84}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5BC4F94C-35BC-4436-A107-9D85E53A9E84}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5BC4F94C-35BC-4436-A107-9D85E53A9E84}.Release|Any CPU.Build.0 = Release|Any CPU
{A672CB00-DC99-4847-A384-B857C5E69301}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A672CB00-DC99-4847-A384-B857C5E69301}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A672CB00-DC99-4847-A384-B857C5E69301}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A672CB00-DC99-4847-A384-B857C5E69301}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,45 @@
using System;
using System.Reflection;
using System.Net;
using System.Net.Http;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using BasicDotnetTemplate.MainProject;
using BasicDotnetTemplate.MainProject.Controllers;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using BasicDotnetTemplate.MainProject.Models.Api.Response;
using BasicDotnetTemplate.MainProject.Models.Settings;
namespace BasicDotnetTemplate.MainProject.Tests;
[TestClass]
public class RootController_Test
{
[TestMethod]
public void GetRoot_Valid()
{
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
try
{
RootController rootController = new RootController();
var result = rootController.GetRoot();
if (result != null)
{
var data = (OkResult)result;
Assert.IsTrue(data.StatusCode == 200);
}
else
{
Assert.Fail($"Data is null");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
}

View File

@@ -0,0 +1,104 @@
using System;
using System.Reflection;
using System.Net;
using System.Net.Http;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using BasicDotnetTemplate.MainProject;
using BasicDotnetTemplate.MainProject.Controllers;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using BasicDotnetTemplate.MainProject.Models.Api.Response;
using BasicDotnetTemplate.MainProject.Models.Settings;
namespace BasicDotnetTemplate.MainProject.Tests;
[TestClass]
public class BaseController_Tests
{
[TestMethod]
public void BaseController_NullConfiguration()
{
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
var exception = true;
try
{
_ = new VersionController(null);
exception = false;
Assert.Fail($"This test should not pass as configuration is null");
}
catch (Exception)
{
Assert.IsTrue(exception);
}
}
[TestMethod]
public void VersionController_GetVersion_Valid()
{
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
try
{
IConfiguration configuration = TestUtils.CreateConfiguration();
VersionController versionController = new VersionController(configuration);
var result = versionController.GetVersion();
var objectResult = ((ObjectResult)result).Value;
if (objectResult != null)
{
var data = (BaseResponse)objectResult;
if (data.Data != null)
{
AppSettings appSettings = new AppSettings();
configuration.GetSection("AppSettings").Bind(appSettings);
string version = data.Data != null ? (string)data.Data : "";
Assert.IsTrue((((IStatusCodeActionResult)result).StatusCode == 200) && (version == appSettings.Settings?.Version));
}
else
{
Assert.Fail($"Unable to convert response value to BaseResponse because Data is null");
}
}
else
{
Assert.Fail($"Data is null");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
[TestMethod]
public void VersionController_GetVersion_NoVersion()
{
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
try
{
Console.WriteLine(System.AppDomain.CurrentDomain.BaseDirectory);
var configuration = TestUtils.CreateEmptyConfiguration(System.AppDomain.CurrentDomain.BaseDirectory + "/JsonData", "emptyAppsettings.json");
VersionController versionController = new VersionController(configuration);
var result = versionController.GetVersion();
var objectResult = ((ObjectResult)result).Value;
if (objectResult != null)
{
var data = (BaseResponse)objectResult;
Assert.IsTrue((((IStatusCodeActionResult)result).StatusCode == 200) && String.IsNullOrEmpty(data.Data));
}
else
{
Assert.Fail($"Response value is null");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
}

View File

@@ -0,0 +1,27 @@
{
"AppSettings" :
{
"Settings": {
"Name": "MainProject",
"Version": "v1.0",
"Description": "This template contains basic configuration for a .Net 8 backend"
},
"DatabaseSettings": {
"SqlServerConnectionString": "SQLSERVER_DB_SERVER",
"MongoDbConnectionString": "MONGO_DB_SERVER",
"PostgreSQLConnectionString": "POSTGRESQL_DB_SERVER"
},
"OpenApiSettings": {
"TermsOfServiceUrl": "",
"OpenApiContact": {
"Name": "",
"Url": ""
},
"OpenApiLicense": {
"Name": "MIT License",
"Url": "https://github.com/csimonapastore/BasicDotnetTemplate/blob/main/LICENSE.md"
}
}
}
}

View File

@@ -0,0 +1,27 @@
{
"AppSettings" :
{
"Settings": {
"Name": "MainProject",
"Version": "v1.0",
"Description": "This template contains basic configuration for a .Net 8 backend"
},
"DatabaseSettings": {
"SqlServerConnectionString": "SQLSERVER_DB_SERVER",
"MongoDbConnectionString": "MONGO_DB_SERVER",
"PostgreSQLConnectionString": "POSTGRESQL_DB_SERVER"
},
"OpenApiSettings": {
"TermsOfServiceUrl": "https://github.com/csimonapastore/BasicDotnetTemplate/blob/main/LICENSE.md",
"OpenApiContact": {
"Name": "README",
"Url": "https://github.com/csimonapastore/BasicDotnetTemplate/blob/main/README.md"
},
"OpenApiLicense": {
"Name": "MIT License",
"Url": "https://github.com/csimonapastore/BasicDotnetTemplate/blob/main/LICENSE.md"
}
}
}
}

View File

@@ -0,0 +1,7 @@
{
"AppSettings" :
{
}
}

View File

@@ -2,7 +2,7 @@
"AppSettings" :
{
"Settings": {
"Name": "BasicDotnetTemplate",
"Name": "MainProject",
"Version": "v1.0",
"Description": "This template contains basic configuration for a .Net 8 backend"
},

View File

@@ -0,0 +1,16 @@
{
"AppSettings" :
{
"Settings": {
"Name": "MainProject",
"Version": "v1.0",
"Description": "This template contains basic configuration for a .Net 8 backend"
},
"DatabaseSettings": {
"SqlServerConnectionString": "",
"MongoDbConnectionString": "",
"PostgreSQLConnectionString": ""
}
}
}

View File

@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
</ItemGroup>
<ItemGroup>
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MainProject\MainProject.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="JsonData/**" CopyToOutputDirectory="Always" CopyToPublishDirectory="Always" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,80 @@
using System;
using System.Reflection;
using System.Net;
using System.Net.Http;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using BasicDotnetTemplate.MainProject;
using BasicDotnetTemplate.MainProject.Models.Api.Response;
using Microsoft.Extensions.DependencyModel.Resolution;
namespace BasicDotnetTemplate.MainProject.Tests;
[TestClass]
public class ApiResponse_Tests
{
[TestMethod]
public void IstantiateBaseResponse_OnlyStatus_Valid()
{
try
{
var baseResponse = new BaseResponse(200, null, null);
Assert.IsTrue(baseResponse.Status == 200 && String.IsNullOrEmpty(baseResponse.Message) && baseResponse.Data == null);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
[TestMethod]
public void IstantiateBaseResponse_OnlyStatus_IsInvalid()
{
try
{
var baseResponse = new BaseResponse(201, null, null);
Assert.IsFalse(baseResponse.Status == 200);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
[TestMethod]
public void IstantiateBaseResponse_StatusAndMessage_Valid()
{
try
{
var baseResponse = new BaseResponse(200, "This is a test message", null);
Assert.IsTrue(baseResponse.Status == 200 && baseResponse.Message == "This is a test message" && baseResponse.Data == null);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
[TestMethod]
public void IstantiateBaseResponse_AllFields_Valid()
{
try
{
string[] data = { "Volvo", "BMW", "Ford", "Mazda" };
var baseResponse = new BaseResponse(200, "This is a test message", data);
Assert.IsTrue(baseResponse.Status == 200 && baseResponse.Message == "This is a test message" && baseResponse.Data == data);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
}

View File

@@ -0,0 +1,98 @@
using System;
using System.Reflection;
using System.Net;
using System.Net.Http;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using BasicDotnetTemplate.MainProject;
using BasicDotnetTemplate.MainProject.Models.Api.Response;
using Microsoft.Extensions.DependencyModel.Resolution;
using BasicDotnetTemplate.MainProject.Models.Settings;
namespace BasicDotnetTemplate.MainProject.Tests;
[TestClass]
public class Settings_Tests
{
[TestMethod]
public void IstantiatePrivateSettings_Valid()
{
try
{
var sqlServer = "Data Source=localhost; Initial Catalog=YourDatabase; User Id=YourUsername; Password=YourPassword;";
var mongodb = "mongodb://localhost:27017";
var postgres = "Server=localhost; Port=5432; Database=YourDatabase; User Id=YourUsername; Password=YourPassword;";
var privateSettings = new PrivateSettings()
{
DatabaseConnection = new DatabaseConnection() {
SqlServer = sqlServer,
Mongodb = mongodb,
Postgres = postgres,
}
};
Assert.IsTrue(
privateSettings.DatabaseConnection != null &&
privateSettings.DatabaseConnection.SqlServer == sqlServer &&
privateSettings.DatabaseConnection.Mongodb == mongodb &&
privateSettings.DatabaseConnection.Postgres == postgres
);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
[TestMethod]
public void IstantiateBaseResponse_OnlyStatus_IsInvalid()
{
try
{
var baseResponse = new BaseResponse(201, null, null);
Assert.IsFalse(baseResponse.Status == 200);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
[TestMethod]
public void IstantiateBaseResponse_StatusAndMessage_Valid()
{
try
{
var baseResponse = new BaseResponse(200, "This is a test message", null);
Assert.IsTrue(baseResponse.Status == 200 && baseResponse.Message == "This is a test message" && baseResponse.Data == null);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
[TestMethod]
public void IstantiateBaseResponse_AllFields_Valid()
{
try
{
string[] data = { "Volvo", "BMW", "Ford", "Mazda" };
var baseResponse = new BaseResponse(200, "This is a test message", data);
Assert.IsTrue(baseResponse.Status == 200 && baseResponse.Message == "This is a test message" && baseResponse.Data == data);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
}

View File

@@ -0,0 +1,77 @@
using System;
using System.Reflection;
using System.Net;
using System.Net.Http;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using BasicDotnetTemplate.MainProject;
namespace BasicDotnetTemplate.MainProject.Tests;
[TestClass]
public class Program_Tests
{
[TestMethod]
public void Program_Configuration_IsValid()
{
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
try
{
var reflectionType = typeof(ReflectionProgram);
if (reflectionType != null)
{
MethodInfo[] methods = reflectionType.GetMethods(); //Using BindingFlags.NonPublic does not show any results
MethodInfo? execute = null;
foreach (MethodInfo m in methods)
{
if (m.Name == "LaunchConfiguration")
{
execute = m;
}
}
if (execute != null)
{
object? initializeObj = execute != null ? execute.Invoke(null, Array.Empty<object>()) : throw new ArgumentNullException("LaunchConfiguration not found");
MethodInfo? initialize = initializeObj != null ? (MethodInfo)initializeObj : throw new ArgumentNullException("Unable to convert object because execute.Invoke is null");
if (initialize != null)
{
var success = false;
try
{
initialize.Invoke(null, new object[] { Array.Empty<string>() });
success = true;
}
catch (Exception innerException)
{
Assert.Fail($"An exception was thrown during initialize.Invoke: {innerException.Message}");
}
Assert.IsTrue(success);
}
else
{
Assert.Fail("Initialize is null.");
}
}
else
{
Assert.Fail("Initialize method not found in Program class.");
}
}
else
{
Assert.Fail("Program class not found in the assembly.");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
}

View File

@@ -0,0 +1,45 @@
using System;
using System.Reflection;
using System.Net;
using System.Net.Http;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using BasicDotnetTemplate.MainProject;
using BasicDotnetTemplate.MainProject.Models.Api.Response;
using Microsoft.Extensions.DependencyModel.Resolution;
using BasicDotnetTemplate.MainProject.Models.Settings;
using Microsoft.AspNetCore.Builder;
using BasicDotnetTemplate.MainProject.Utils;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Interfaces;
namespace BasicDotnetTemplate.MainProject.Tests;
public static class TestUtils
{
public static IConfiguration CreateConfiguration()
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(Array.Empty<string>());
AppSettings appSettings = ProgramUtils.AddConfiguration(ref builder, System.AppDomain.CurrentDomain.BaseDirectory + "/JsonData");
ProgramUtils.AddOpenApi(ref builder, appSettings);
AppSettings _appSettings = new AppSettings();
builder.Configuration.GetSection("AppSettings").Bind(_appSettings);
return builder.Configuration;
}
public static IConfiguration CreateEmptyConfiguration(string? path = "", string? filename = "")
{
string appSettingsPath = String.IsNullOrEmpty(path) ? System.AppDomain.CurrentDomain.BaseDirectory : path;
return new ConfigurationBuilder()
.SetBasePath(appSettingsPath)
.AddJsonFile(String.IsNullOrEmpty(filename) ? "appsettings.json" : filename, optional: false, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
}
}

View File

@@ -0,0 +1,325 @@
using System;
using System.Reflection;
using System.Net;
using System.Net.Http;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using BasicDotnetTemplate.MainProject;
using BasicDotnetTemplate.MainProject.Models.Api.Response;
using Microsoft.Extensions.DependencyModel.Resolution;
using BasicDotnetTemplate.MainProject.Models.Settings;
using Microsoft.AspNetCore.Builder;
using BasicDotnetTemplate.MainProject.Utils;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Interfaces;
namespace BasicDotnetTemplate.MainProject.Tests;
[TestClass]
public class ProgramUtils_Tests
{
[TestMethod]
public void NoOpenApiConfig_Valid()
{
try
{
OpenApiInfo expectedOpenApiInfo = new OpenApiInfo()
{
Title = "MainProject",
Description = "This template contains basic configuration for a .Net 8 backend",
Version = "v1.0",
Contact = null,
TermsOfService = null,
License = null,
Extensions = new Dictionary<string, IOpenApiExtension>()
};
WebApplicationBuilder builder = WebApplication.CreateBuilder(Array.Empty<string>());
AppSettings realAppSettings = ProgramUtils.AddConfiguration(ref builder, System.AppDomain.CurrentDomain.BaseDirectory + "/JsonData", "noApiConfigurationAppSettings.json");
OpenApiInfo realOpenApiInfo = ProgramUtils.CreateOpenApiInfo(realAppSettings);
var areEquals = expectedOpenApiInfo.Title == realOpenApiInfo.Title &&
expectedOpenApiInfo.Description == realOpenApiInfo.Description &&
expectedOpenApiInfo.Version == realOpenApiInfo.Version &&
expectedOpenApiInfo.Contact == realOpenApiInfo.Contact &&
expectedOpenApiInfo.TermsOfService == realOpenApiInfo.TermsOfService &&
expectedOpenApiInfo.License == realOpenApiInfo.License;
Assert.IsTrue(areEquals);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
[TestMethod]
public void CreateOpenApiInfo_EmptyAppSettings()
{
try
{
OpenApiInfo expectedOpenApiInfo = new OpenApiInfo()
{
Title = null,
Description = null,
Version = null,
Contact = null,
TermsOfService = null,
License = null,
Extensions = new Dictionary<string, IOpenApiExtension>()
};
WebApplicationBuilder builder = WebApplication.CreateBuilder(Array.Empty<string>());
AppSettings realAppSettings = ProgramUtils.AddConfiguration(ref builder, System.AppDomain.CurrentDomain.BaseDirectory + "/JsonData", "emptyAppsettings.json");
OpenApiInfo realOpenApiInfo = ProgramUtils.CreateOpenApiInfo(realAppSettings);
var areEquals = expectedOpenApiInfo.Title == realOpenApiInfo.Title &&
expectedOpenApiInfo.Description == realOpenApiInfo.Description &&
expectedOpenApiInfo.Version == realOpenApiInfo.Version &&
expectedOpenApiInfo.Contact == realOpenApiInfo.Contact &&
expectedOpenApiInfo.TermsOfService == realOpenApiInfo.TermsOfService &&
expectedOpenApiInfo.License == realOpenApiInfo.License;
Assert.IsTrue(areEquals);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
[TestMethod]
public void CreateOpenApiInfo_NullSettings()
{
try
{
AppSettings appSettings = new AppSettings();
OpenApiInfo realOpenApiInfo = ProgramUtils.CreateOpenApiInfo(appSettings);
Assert.IsTrue(realOpenApiInfo != null);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
[TestMethod]
public void CreateOpenApiInfo_NullOpenApiSettings()
{
try
{
AppSettings appSettings = new AppSettings()
{
Settings = new Settings
{
Name = "Test",
Description = "This is a test description",
Version = "v1"
},
OpenApiSettings = null
};
OpenApiInfo realOpenApiInfo = ProgramUtils.CreateOpenApiInfo(appSettings);
Assert.IsTrue(realOpenApiInfo != null);
Assert.IsTrue(realOpenApiInfo.Title == appSettings.Settings.Name);
Assert.IsTrue(realOpenApiInfo.Description == appSettings.Settings.Description);
Assert.IsTrue(realOpenApiInfo.Version == appSettings.Settings.Version);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
[TestMethod]
public void CreateOpenApiInfo_NullTermsOfServiceUrl()
{
try
{
AppSettings appSettings = new AppSettings()
{
Settings = new Settings
{
Name = "Test",
Description = "This is a test description",
Version = "v1"
},
OpenApiSettings = new OpenApiSettings
{
TermsOfServiceUrl = null,
OpenApiContact = null,
OpenApiLicense = null
}
};
OpenApiInfo realOpenApiInfo = ProgramUtils.CreateOpenApiInfo(appSettings);
Assert.IsTrue(realOpenApiInfo != null);
Assert.IsTrue(realOpenApiInfo.Title == appSettings.Settings.Name);
Assert.IsTrue(realOpenApiInfo.Description == appSettings.Settings.Description);
Assert.IsTrue(realOpenApiInfo.Version == appSettings.Settings.Version);
Assert.IsTrue(realOpenApiInfo.TermsOfService == null);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
[TestMethod]
public void OpenApiConfig_NotNull()
{
try
{
OpenApiInfo expectedOpenApiInfo = new OpenApiInfo()
{
Title = "MainProject",
Description = "This template contains basic configuration for a .Net 8 backend",
Version = "v1.0",
Contact = null,
TermsOfService = null,
License = null,
Extensions = new Dictionary<string, IOpenApiExtension>()
};
WebApplicationBuilder builder = WebApplication.CreateBuilder(Array.Empty<string>());
AppSettings appSettings = ProgramUtils.AddConfiguration(ref builder, System.AppDomain.CurrentDomain.BaseDirectory + "/JsonData", "completeAppSettings.json");
OpenApiInfo realOpenApiInfo = ProgramUtils.CreateOpenApiInfo(appSettings);
var areEquals = expectedOpenApiInfo.Title == realOpenApiInfo.Title &&
expectedOpenApiInfo.Description == realOpenApiInfo.Description &&
expectedOpenApiInfo.Version == realOpenApiInfo.Version &&
expectedOpenApiInfo.Contact == realOpenApiInfo.Contact &&
expectedOpenApiInfo.TermsOfService == realOpenApiInfo.TermsOfService &&
expectedOpenApiInfo.License == realOpenApiInfo.License;
Assert.IsFalse(areEquals);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
[TestMethod]
public void AddOpenApi_Valid()
{
try
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(Array.Empty<string>());
AppSettings appSettings = ProgramUtils.AddConfiguration(ref builder, System.AppDomain.CurrentDomain.BaseDirectory + "/JsonData");
ProgramUtils.AddOpenApi(ref builder, appSettings);
AppSettings _appSettings = new AppSettings();
builder.Configuration.GetSection("AppSettings").Bind(_appSettings);
var areEquals = appSettings.OpenApiSettings?.OpenApiContact?.Name == _appSettings.OpenApiSettings?.OpenApiContact?.Name &&
appSettings.OpenApiSettings?.OpenApiContact?.Url == _appSettings.OpenApiSettings?.OpenApiContact?.Url &&
appSettings.OpenApiSettings?.OpenApiLicense?.Name == _appSettings.OpenApiSettings?.OpenApiLicense?.Name &&
appSettings.OpenApiSettings?.OpenApiLicense?.Url == _appSettings.OpenApiSettings?.OpenApiLicense?.Url &&
appSettings.OpenApiSettings?.TermsOfServiceUrl == _appSettings.OpenApiSettings?.TermsOfServiceUrl;
Assert.IsTrue(areEquals);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
[TestMethod]
public void AddSqlServerContext_Valid()
{
try
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(Array.Empty<string>());
AppSettings appSettings = ProgramUtils.AddConfiguration(ref builder, System.AppDomain.CurrentDomain.BaseDirectory + "/JsonData");
ProgramUtils.AddDbContext(ref builder, appSettings);
AppSettings _appSettings = new AppSettings();
builder.Configuration.GetSection("AppSettings").Bind(_appSettings);
var areEquals = appSettings.DatabaseSettings?.SqlServerConnectionString == _appSettings.DatabaseSettings?.SqlServerConnectionString;
Assert.IsTrue(areEquals);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
[TestMethod]
public void AddMongoDbContext_Valid()
{
try
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(Array.Empty<string>());
AppSettings appSettings = ProgramUtils.AddConfiguration(ref builder, System.AppDomain.CurrentDomain.BaseDirectory + "/JsonData");
ProgramUtils.AddDbContext(ref builder, appSettings);
AppSettings _appSettings = new AppSettings();
builder.Configuration.GetSection("AppSettings").Bind(_appSettings);
var areEquals = appSettings.DatabaseSettings?.MongoDbConnectionString == _appSettings.DatabaseSettings?.MongoDbConnectionString;
Assert.IsTrue(areEquals);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
[TestMethod]
public void AddPostgreSqlDbContext_Valid()
{
try
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(Array.Empty<string>());
AppSettings appSettings = ProgramUtils.AddConfiguration(ref builder, System.AppDomain.CurrentDomain.BaseDirectory + "/JsonData");
ProgramUtils.AddDbContext(ref builder, appSettings);
AppSettings _appSettings = new AppSettings();
builder.Configuration.GetSection("AppSettings").Bind(_appSettings);
var areEquals = appSettings.DatabaseSettings?.PostgreSQLConnectionString == _appSettings.DatabaseSettings?.PostgreSQLConnectionString;
Assert.IsTrue(areEquals);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
[TestMethod]
public void NoSqlServerContext_Empty()
{
try
{
DatabaseSettings expectedDbSettings = new DatabaseSettings()
{
SqlServerConnectionString = ""
};
WebApplicationBuilder builder = WebApplication.CreateBuilder(Array.Empty<string>());
AppSettings realAppSettings = ProgramUtils.AddConfiguration(ref builder, System.AppDomain.CurrentDomain.BaseDirectory + "/JsonData", "noApiConfigurationAppSettings.json");
ProgramUtils.AddDbContext(ref builder, realAppSettings);
var areEquals = expectedDbSettings.SqlServerConnectionString == realAppSettings.DatabaseSettings?.SqlServerConnectionString;
Console.WriteLine(realAppSettings.DatabaseSettings?.SqlServerConnectionString);
Assert.IsTrue(areEquals);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Assert.Fail($"An exception was thrown: {ex.Message}");
}
}
}

View File

@@ -0,0 +1,60 @@
using System.Net;
using Microsoft.AspNetCore.Mvc;
using BasicDotnetTemplate.MainProject.Models.Api.Response;
using BasicDotnetTemplate.MainProject.Models.Settings;
namespace BasicDotnetTemplate.MainProject.Controllers
{
public abstract class BaseController : ControllerBase
{
protected readonly IConfiguration _configuration;
protected readonly AppSettings _appSettings;
protected BaseController(
IConfiguration configuration
)
{
_configuration = configuration;
_appSettings = new AppSettings();
_configuration.GetSection("AppSettings").Bind(_appSettings);
}
#nullable enable
private static BaseResponse CreateResponse(HttpStatusCode status, string message, object? data = null)
{
return new BaseResponse((int)status, message, data);
}
protected new IActionResult Created(string message, object? data = null)
{
message = String.IsNullOrEmpty(message) ? "Created" : message;
return StatusCode((int)HttpStatusCode.Created, CreateResponse(HttpStatusCode.Created, message, data));
}
protected IActionResult Success(string message, object? data = null)
{
message = String.IsNullOrEmpty(message) ? "Success" : message;
return StatusCode((int)HttpStatusCode.OK, CreateResponse(HttpStatusCode.OK, message, data));
}
protected IActionResult NotFound(string message, object? data = null)
{
message = String.IsNullOrEmpty(message) ? "Not found" : message;
return StatusCode((int)HttpStatusCode.NotFound, CreateResponse(HttpStatusCode.NotFound, message, data));
}
protected IActionResult BadRequest(string message, object? data = null)
{
message = String.IsNullOrEmpty(message) ? "Bad request" : message;
return StatusCode((int)HttpStatusCode.BadRequest, CreateResponse(HttpStatusCode.BadRequest, message, data));
}
protected IActionResult InternalServerError(string message)
{
message = String.IsNullOrEmpty(message) ? "Internal server error" : message;
return StatusCode((int)HttpStatusCode.InternalServerError, CreateResponse(HttpStatusCode.InternalServerError, message));
}
#nullable disable
}
}

View File

@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Mvc;
using BasicDotnetTemplate.MainProject.Models.Settings;
using Microsoft.AspNetCore.Http.HttpResults;
namespace BasicDotnetTemplate.MainProject.Controllers
{
[Route("")]
public class RootController : ControllerBase
{
public RootController() { }
[HttpGet("")]
public IActionResult GetRoot()
{
return Ok();
}
}
}

View File

@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Mvc;
using BasicDotnetTemplate.MainProject.Models.Settings;
namespace BasicDotnetTemplate.MainProject.Controllers
{
[Route("[controller]")]
public class VersionController : BaseController
{
public VersionController(
IConfiguration configuration
) : base(configuration) { }
[HttpGet("get")]
public IActionResult GetVersion()
{
return Success(String.Empty, _appSettings?.Settings?.Version);
}
}
}

View File

@@ -0,0 +1,26 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.EntityFrameworkCore;
using MongoDB.EntityFrameworkCore.Extensions;
using BasicDotnetTemplate.MainProject.Models.Database.Mongo;
namespace BasicDotnetTemplate.MainProject.Core.Database
{
public class MongoDbContext : DbContext
{
public MongoDbContext(DbContextOptions<MongoDbContext> options) : base(options) { }
public DbSet<Log> Logs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Log>().ToCollection("Logs");
}
}
}

View File

@@ -0,0 +1,18 @@
using Microsoft.EntityFrameworkCore;
using BasicDotnetTemplate.MainProject.Models.Database.SqlServer;
namespace BasicDotnetTemplate.MainProject.Core.Database
{
public class PostgreSqlDbContext : DbContext
{
public PostgreSqlDbContext(DbContextOptions<PostgreSqlDbContext> options)
: base(options)
{
}
}
}

View File

@@ -0,0 +1,20 @@
using Microsoft.EntityFrameworkCore;
using BasicDotnetTemplate.MainProject.Models.Database.SqlServer;
namespace BasicDotnetTemplate.MainProject.Core.Database
{
public class SqlServerContext : DbContext
{
public SqlServerContext(DbContextOptions<SqlServerContext> options)
: base(options)
{
}
public DbSet<User> Users { get; set; }
}
}

View File

@@ -0,0 +1,44 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.msbuild" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="9.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="MongoDB.Driver" Version="2.28.0" />
<PackageReference Include="MongoDB.EntityFrameworkCore" Version="8.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.2.8" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.3.8" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters.Abstractions" Version="8.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,132 @@
// <auto-generated />
using System;
using BasicDotnetTemplate.MainProject.Core.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace MainProject.Migrations
{
[DbContext(typeof(SqlServerContext))]
[Migration("20240904211920_InitialCreate")]
partial class InitialCreate
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.8")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BasicDotnetTemplate.MainProject.Models.Database.SqlServer.Role", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2");
b.Property<int>("CreationUserId")
.HasColumnType("int");
b.Property<DateTime>("DeletionTime")
.HasColumnType("datetime2");
b.Property<int>("DeletionUserId")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("UpdateTime")
.HasColumnType("datetime2");
b.Property<int>("UpdateUserId")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("Role");
});
modelBuilder.Entity("BasicDotnetTemplate.MainProject.Models.Database.SqlServer.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2");
b.Property<int>("CreationUserId")
.HasColumnType("int");
b.Property<DateTime>("DeletionTime")
.HasColumnType("datetime2");
b.Property<int>("DeletionUserId")
.HasColumnType("int");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("RoleId")
.HasColumnType("int");
b.Property<DateTime>("UpdateTime")
.HasColumnType("datetime2");
b.Property<int>("UpdateUserId")
.HasColumnType("int");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("Users");
});
modelBuilder.Entity("BasicDotnetTemplate.MainProject.Models.Database.SqlServer.User", b =>
{
b.HasOne("BasicDotnetTemplate.MainProject.Models.Database.SqlServer.Role", "Role")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Role");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,79 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MainProject.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Role",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(type: "nvarchar(max)", nullable: false),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreationUserId = table.Column<int>(type: "int", nullable: false),
UpdateTime = table.Column<DateTime>(type: "datetime2", nullable: false),
UpdateUserId = table.Column<int>(type: "int", nullable: false),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: false),
DeletionUserId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Role", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Users",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Username = table.Column<string>(type: "nvarchar(max)", nullable: false),
FirstName = table.Column<string>(type: "nvarchar(max)", nullable: false),
LastName = table.Column<string>(type: "nvarchar(max)", nullable: false),
Email = table.Column<string>(type: "nvarchar(max)", nullable: false),
RoleId = table.Column<int>(type: "int", nullable: false),
PasswordHash = table.Column<string>(type: "nvarchar(max)", nullable: false),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreationUserId = table.Column<int>(type: "int", nullable: false),
UpdateTime = table.Column<DateTime>(type: "datetime2", nullable: false),
UpdateUserId = table.Column<int>(type: "int", nullable: false),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: false),
DeletionUserId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
table.ForeignKey(
name: "FK_Users_Role_RoleId",
column: x => x.RoleId,
principalTable: "Role",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Users_RoleId",
table: "Users",
column: "RoleId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Users");
migrationBuilder.DropTable(
name: "Role");
}
}
}

View File

@@ -0,0 +1,132 @@
// <auto-generated />
using System;
using BasicDotnetTemplate.MainProject.Core.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace MainProject.Migrations
{
[DbContext(typeof(SqlServerContext))]
[Migration("20241129231345_Try")]
partial class Try
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.8")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BasicDotnetTemplate.MainProject.Models.Database.SqlServer.Role", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2");
b.Property<int>("CreationUserId")
.HasColumnType("int");
b.Property<DateTime>("DeletionTime")
.HasColumnType("datetime2");
b.Property<int>("DeletionUserId")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("UpdateTime")
.HasColumnType("datetime2");
b.Property<int>("UpdateUserId")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("Role");
});
modelBuilder.Entity("BasicDotnetTemplate.MainProject.Models.Database.SqlServer.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2");
b.Property<int>("CreationUserId")
.HasColumnType("int");
b.Property<DateTime>("DeletionTime")
.HasColumnType("datetime2");
b.Property<int>("DeletionUserId")
.HasColumnType("int");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("RoleId")
.HasColumnType("int");
b.Property<DateTime>("UpdateTime")
.HasColumnType("datetime2");
b.Property<int>("UpdateUserId")
.HasColumnType("int");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("Users");
});
modelBuilder.Entity("BasicDotnetTemplate.MainProject.Models.Database.SqlServer.User", b =>
{
b.HasOne("BasicDotnetTemplate.MainProject.Models.Database.SqlServer.Role", "Role")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Role");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MainProject.Migrations
{
/// <inheritdoc />
public partial class Try : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

View File

@@ -0,0 +1,129 @@
// <auto-generated />
using System;
using BasicDotnetTemplate.MainProject.Core.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace MainProject.Migrations
{
[DbContext(typeof(SqlServerContext))]
partial class SqlServerContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.8")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BasicDotnetTemplate.MainProject.Models.Database.SqlServer.Role", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2");
b.Property<int>("CreationUserId")
.HasColumnType("int");
b.Property<DateTime>("DeletionTime")
.HasColumnType("datetime2");
b.Property<int>("DeletionUserId")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("UpdateTime")
.HasColumnType("datetime2");
b.Property<int>("UpdateUserId")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("Role");
});
modelBuilder.Entity("BasicDotnetTemplate.MainProject.Models.Database.SqlServer.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2");
b.Property<int>("CreationUserId")
.HasColumnType("int");
b.Property<DateTime>("DeletionTime")
.HasColumnType("datetime2");
b.Property<int>("DeletionUserId")
.HasColumnType("int");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("RoleId")
.HasColumnType("int");
b.Property<DateTime>("UpdateTime")
.HasColumnType("datetime2");
b.Property<int>("UpdateUserId")
.HasColumnType("int");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("Users");
});
modelBuilder.Entity("BasicDotnetTemplate.MainProject.Models.Database.SqlServer.User", b =>
{
b.HasOne("BasicDotnetTemplate.MainProject.Models.Database.SqlServer.Role", "Role")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Role");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,19 @@
namespace BasicDotnetTemplate.MainProject.Models.Api.Response;
public class BaseResponse
{
#nullable enable
public BaseResponse(int status, string? message, dynamic? data)
{
this.Status = status;
this.Message = message;
this.Data = data;
}
#nullable disable
public int Status { get; set; }
#nullable enable
public string? Message { get; set; }
public virtual dynamic? Data { get; set; }
#nullable disable
}

View File

@@ -0,0 +1,11 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace BasicDotnetTemplate.MainProject.Models.Database.Mongo
{
public class Log
{
[BsonId]
public ObjectId Id { get; set; }
}
}

View File

@@ -0,0 +1,16 @@
namespace BasicDotnetTemplate.MainProject.Models.Database.SqlServer
{
public class Base
{
public int Id { get; set; }
public DateTime CreationTime { get; set; }
public int CreationUserId { get; set; }
public DateTime UpdateTime { get; set; }
public int UpdateUserId { get; set; }
public DateTime DeletionTime { get; set; }
public int DeletionUserId { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace BasicDotnetTemplate.MainProject.Models.Database.SqlServer
{
public class Role : Base
{
public required string Name { get; set; }
}
}

View File

@@ -0,0 +1,19 @@
using System.Text.Json.Serialization;
namespace BasicDotnetTemplate.MainProject.Models.Database.SqlServer
{
public class User : Base
{
public required string Username { get; set; }
public required string FirstName { get; set; }
public required string LastName { get; set; }
public required string Email { get; set; }
public required Role Role { get; set; }
[JsonIgnore]
public required string PasswordHash { get; set; }
}
}

View File

@@ -1,4 +1,4 @@
namespace BasicDotnetTemplate.Models.Settings;
namespace BasicDotnetTemplate.MainProject.Models.Settings;
public class AppSettings
{
@@ -6,6 +6,7 @@ public class AppSettings
public Settings? Settings { get; set; }
public PrivateSettings? PrivateSettings { get; set; }
public OpenApiSettings? OpenApiSettings { get; set; }
public DatabaseSettings? DatabaseSettings { get; set; }
#nullable disable
}

View File

@@ -1,4 +1,4 @@
namespace BasicDotnetTemplate.Models.Settings;
namespace BasicDotnetTemplate.MainProject.Models.Settings;
public class DatabaseConnection
{

View File

@@ -0,0 +1,10 @@
namespace BasicDotnetTemplate.MainProject.Models.Settings;
public class DatabaseSettings
{
#nullable enable
public string? SqlServerConnectionString { get; set; }
public string? MongoDbConnectionString { get; set; }
public string? PostgreSQLConnectionString { get; set; }
#nullable disable
}

View File

@@ -1,4 +1,4 @@
namespace BasicDotnetTemplate.Models.Settings;
namespace BasicDotnetTemplate.MainProject.Models.Settings;
public class OpenApiSettings
{

View File

@@ -1,4 +1,4 @@
namespace BasicDotnetTemplate.Models.Settings;
namespace BasicDotnetTemplate.MainProject.Models.Settings;
public class OpenApiSettingsDetails
{

View File

@@ -1,4 +1,4 @@
namespace BasicDotnetTemplate.Models.Settings;
namespace BasicDotnetTemplate.MainProject.Models.Settings;
public class PrivateSettings
{

View File

@@ -1,4 +1,4 @@
namespace BasicDotnetTemplate.Models.Settings;
namespace BasicDotnetTemplate.MainProject.Models.Settings;
public class Settings
{

View File

@@ -3,7 +3,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="logfile" xsi:type="File" fileName="file.txt" />
<target name="logfile" xsi:type="File" fileName="Logs/application.txt" />
<target name="logconsole" xsi:type="Console" />
</targets>

61
MainProject/Program.cs Normal file
View File

@@ -0,0 +1,61 @@
using NLog;
using BasicDotnetTemplate.MainProject.Models.Settings;
using System.Reflection;
using BasicDotnetTemplate.MainProject.Utils;
namespace BasicDotnetTemplate.MainProject;
public static class ReflectionProgram
{
public static MethodInfo LaunchConfiguration()
{
var a = typeof(Program);
MethodInfo[] methods = a.GetMethods(); //Using BindingFlags.NonPublic does not show any results
MethodInfo? initialize = null;
foreach (MethodInfo m in methods)
{
if (m.Name == "Initialize")
{
initialize = m;
}
}
return initialize!;
}
}
internal static class Program
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
public static WebApplication Initialize(string[] args)
{
Logger.Info("[Program][Initialize] Start building");
var builder = WebApplication.CreateBuilder(args);
AppSettings appSettings = ProgramUtils.AddConfiguration(ref builder);
ProgramUtils.AddServices(ref builder);
ProgramUtils.AddScopes(ref builder);
ProgramUtils.AddOpenApi(ref builder, appSettings);
ProgramUtils.AddDbContext(ref builder, appSettings);
WebApplication app = builder.Build();
ProgramUtils.AddMiddlewares(ref app);
Logger.Info("[Program][Initialize] End building");
return app;
}
public static void Main(string[] args)
{
WebApplication app = Initialize(args);
Logger.Info("[Program][Main] Launching app");
app.Run();
NLog.LogManager.Shutdown(); // Flush and close down internal threads and timers
}
}

View File

@@ -0,0 +1,178 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.OpenApi.Models;
using MongoDB.Driver;
using NLog;
using BasicDotnetTemplate.MainProject.Core.Database;
using BasicDotnetTemplate.MainProject.Models.Settings;
namespace BasicDotnetTemplate.MainProject.Utils;
public static class ProgramUtils
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
public static AppSettings AddConfiguration(ref WebApplicationBuilder builder, string? path = "", string? filename = "")
{
Logger.Info("[ProgramUtils][AddConfiguration] Adding configuration");
string appSettingsPath = String.IsNullOrEmpty(path) ? System.AppDomain.CurrentDomain.BaseDirectory : path;
var _configuration = new ConfigurationBuilder()
.SetBasePath(appSettingsPath)
.AddJsonFile(String.IsNullOrEmpty(filename) ? "appsettings.json" : filename, optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables()
.Build();
builder.Services.AddSingleton<IConfiguration>(_configuration);
PrivateSettings privateSettings = new PrivateSettings();
_configuration.GetSection("PrivateSettings").Bind(privateSettings);
AppSettings appSettings = new AppSettings();
appSettings.PrivateSettings = privateSettings;
_configuration.GetSection("AppSettings").Bind(appSettings);
builder.Services.Configure<AppSettings>(_configuration.GetSection("AppSettings"));
builder.Services.Configure<PrivateSettings>(_configuration.GetSection("PrivateSettings"));
Logger.Info("[ProgramUtils][AddConfiguration] Ended configuration");
return appSettings;
}
public static OpenApiInfo CreateOpenApiInfo(AppSettings appSettings)
{
OpenApiInfo openApiInfo = new OpenApiInfo
{
Version = appSettings.Settings?.Version,
Title = appSettings.Settings?.Name,
Description = appSettings.Settings?.Description
};
if (!String.IsNullOrEmpty(appSettings.OpenApiSettings?.TermsOfServiceUrl))
{
openApiInfo.TermsOfService = new Uri(appSettings.OpenApiSettings.TermsOfServiceUrl);
}
if (!String.IsNullOrEmpty(appSettings.OpenApiSettings?.OpenApiContact?.Name) && !String.IsNullOrEmpty(appSettings.OpenApiSettings?.OpenApiContact?.Url))
{
openApiInfo.Contact = new OpenApiContact
{
Name = appSettings.OpenApiSettings.OpenApiContact.Name,
Url = new Uri(appSettings.OpenApiSettings.OpenApiContact.Url)
};
}
if (!String.IsNullOrEmpty(appSettings.OpenApiSettings?.OpenApiLicense?.Name) && !String.IsNullOrEmpty(appSettings.OpenApiSettings?.OpenApiLicense?.Url))
{
openApiInfo.License = new OpenApiLicense
{
Name = appSettings.OpenApiSettings.OpenApiLicense.Name,
Url = new Uri(appSettings.OpenApiSettings.OpenApiLicense.Url)
};
}
return openApiInfo;
}
public static void AddOpenApi(ref WebApplicationBuilder builder, AppSettings appSettings)
{
Logger.Info("[ProgramUtils][AddOpenApi] Adding swagger doc");
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", CreateOpenApiInfo(appSettings));
});
Logger.Info("[ProgramUtils][AddOpenApi] Ended swagger doc");
}
public static void AddServices(ref WebApplicationBuilder builder)
{
Logger.Info("[ProgramUtils][AddServices] Adding services");
builder.Services.AddAuthentication();
builder.Services.AddAuthorization();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
Logger.Info("[ProgramUtils][AddServices] Done services");
}
public static void AddMiddlewares(ref WebApplication app)
{
Logger.Info("[ProgramUtils][AddMiddlewares] Adding middlewares");
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.MapControllers(); // This maps all controllers
if (app.Environment.IsDevelopment())
{
app.UseStaticFiles();
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.InjectStylesheet("/swagger-ui/custom.css");
options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
});
}
Logger.Info("[ProgramUtils][AddMiddlewares] Done middlewares");
}
public static void AddDbContext(ref WebApplicationBuilder builder, AppSettings appSettings)
{
Logger.Info("[ProgramUtils][AddDbContext] Adding DbContext");
var messages = String.Empty;
if (!String.IsNullOrEmpty(appSettings.DatabaseSettings?.SqlServerConnectionString))
{
var connectionString = appSettings.DatabaseSettings?.SqlServerConnectionString ?? String.Empty;
connectionString = connectionString.Replace("SQLSERVER_DB_SERVER", Environment.GetEnvironmentVariable("SQLSERVER_DB_SERVER"));
builder.Services.AddDbContext<SqlServerContext>(options =>
options.UseSqlServer(connectionString)
);
messages = "SqlServerContext";
}
if (!String.IsNullOrEmpty(appSettings.DatabaseSettings?.MongoDbConnectionString))
{
var connectionString = appSettings.DatabaseSettings?.MongoDbConnectionString ?? String.Empty;
connectionString = connectionString.Replace("MONGO_DB_SERVER", Environment.GetEnvironmentVariable("MONGODB_DB_SERVER"));
var databaseName = connectionString.Split("/").LastOrDefault();
if (!String.IsNullOrEmpty(databaseName))
{
var client = new MongoClient(connectionString);
builder.Services.AddDbContext<MongoDbContext>(options =>
options.UseMongoDB(client, databaseName)
);
messages = messages + (String.IsNullOrEmpty(messages) ? "" : ", ") + "MongoDbContext";
}
}
if (!String.IsNullOrEmpty(appSettings.DatabaseSettings?.PostgreSQLConnectionString))
{
var connectionString = appSettings.DatabaseSettings?.PostgreSQLConnectionString ?? String.Empty;
connectionString = connectionString.Replace("POSTGRESQL_DB_SERVER", Environment.GetEnvironmentVariable("POSTGRESQL_DB_SERVER"));
builder.Services.AddDbContext<PostgreSqlDbContext>(options =>
options.UseNpgsql(connectionString)
);
messages = messages + (String.IsNullOrEmpty(messages) ? "" : ", ") + "PostgreSqlDbContext";
}
messages = String.IsNullOrEmpty(messages) ? "No context" : messages;
Logger.Info($"[ProgramUtils][AddDbContext] {messages} added");
}
public static void AddScopes(ref WebApplicationBuilder builder)
{
Logger.Info("[ProgramUtils][AddScopes] Adding scopes");
Logger.Info("[ProgramUtils][AddScopes] Done scopes");
}
}

View File

@@ -0,0 +1,32 @@
{
"AppSettings" :
{
"Settings": {
"Name": "MainProject",
"Version": "v1.0",
"Description": "This template contains basic configuration for a .Net 8 backend"
},
"DatabaseSettings": {
"SqlServerConnectionString": "SQLSERVER_DB_SERVER",
"MongoDbConnectionString": "MONGO_DB_SERVER",
"PostgreSQLConnectionString": "POSTGRESQL_DB_SERVER"
},
"OpenApiSettings": {
"TermsOfServiceUrl": "",
"OpenApiContact": {
"Name": "",
"Url": ""
},
"OpenApiLicense": {
"Name": "MIT License",
"Url": "https://github.com/csimonapastore/BasicDotnetTemplate/blob/main/LICENSE.md"
}
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
}

View File

@@ -1,10 +1,8 @@
.topbar-wrapper img[alt="Swagger UI"], .topbar-wrapper span {
/* .topbar-wrapper img[alt="Swagger UI"], .topbar-wrapper span {
src
}
} */
.topbar-wrapper .link:after {
/* content: 'Unoffical McLaren F1 API'; */
/* color: #fff; */
visibility: visible;
display: block;
position: absolute;

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,104 +0,0 @@
using Microsoft.OpenApi.Models;
using BasicDotnetTemplate.Models.Settings;
namespace BasicDotnetTemplate;
internal static class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var _configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables()
.Build();
// REGISTER SERVICES HERE
builder.Services.AddSingleton<IConfiguration>(_configuration);
PrivateSettings privateSettings = new PrivateSettings();
_configuration.GetSection("PrivateSettings").Bind(privateSettings);
builder.Services.Configure<AppSettings>(_configuration.GetSection("AppSettings"));
builder.Services.Configure<PrivateSettings>(_configuration.GetSection("PrivateSettings"));
builder.Services.AddAuthentication();
builder.Services.AddAuthorization();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
AppSettings appSettings = new AppSettings();
appSettings.PrivateSettings = privateSettings;
_configuration.GetSection("AppSettings").Bind(appSettings);
OpenApiInfo openApiInfo = new()
{
Version = appSettings?.Settings?.Version,
Title = appSettings?.Settings?.Name,
Description = appSettings?.Settings?.Description
};
if (!String.IsNullOrEmpty(appSettings?.OpenApiSettings?.TermsOfServiceUrl))
{
openApiInfo.TermsOfService = new Uri(appSettings.OpenApiSettings.TermsOfServiceUrl);
}
if (!String.IsNullOrEmpty(appSettings?.OpenApiSettings?.OpenApiContact?.Name) && !String.IsNullOrEmpty(appSettings?.OpenApiSettings?.OpenApiContact?.Url))
{
openApiInfo.Contact = new OpenApiContact
{
Name = appSettings.OpenApiSettings.OpenApiContact.Name,
Url = new Uri(appSettings.OpenApiSettings.OpenApiContact.Url)
};
}
if (!String.IsNullOrEmpty(appSettings?.OpenApiSettings?.OpenApiLicense?.Name) && !String.IsNullOrEmpty(appSettings?.OpenApiSettings?.OpenApiLicense?.Url))
{
openApiInfo.License = new OpenApiLicense
{
Name = appSettings.OpenApiSettings.OpenApiLicense.Name,
Url = new Uri(appSettings.OpenApiSettings.OpenApiLicense.Url)
};
}
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", openApiInfo);
});
var app = builder.Build();
// REGISTER MIDDLEWARE HERE
app.UseAuthentication();
app.UseAuthorization();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}"
);
if (app.Environment.IsDevelopment())
{
app.UseStaticFiles();
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.InjectStylesheet("/swagger-ui/custom.css");
options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
});
}
app.Run();
}
}

View File

@@ -1,3 +1,69 @@
# BasicDotnetTemplate
# MainProject
## Table of contents
- [MainProject](#mainproject)
- [Table of contents](#table-of-contents)
- [What is BasicDotnetTemplate?](#what-is-basicdotnettemplate)
- [Technologies](#technologies)
- [Dotnet](#dotnet)
- [Commands](#commands)
- [Debug](#debug)
- [Entity Framework](#entity-framework)
- [Sonarcloud](#sonarcloud)
- [Quality gate](#quality-gate)
- [Code smells](#code-smells)
## What is BasicDotnetTemplate?
BasicDotnetTemplate is a basic project written in .NET 8. It contains MainProject, a WebApi project, and MainProject.Tests written in .NET 8 that contains tests for MainProject.
## Technologies
### Dotnet
[![dotnet](https://img.shields.io/badge/.NET%20-8.0.201-blue)](https://dotnet.microsoft.com/en-us)
Every component is developed using **dotnet-core 8.0.201** and was generated with [dotnet](https://dotnet.microsoft.com/).
> .NET is the free, open-source, cross-platform framework for building modern apps and powerful cloud services. Supported on Windows, Linux, and macOS.
#### Commands
##### Debug
You can use the following comands in the MainProject folder.
```bash
dotnet watch run
```
Restarts or hot reloads the specified application, or runs a specified dotnet command, when changes in source code are detected.
### Entity Framework
This project uses Entity Framework Core, you can find the official documentation [here](https://www.entityframeworktutorial.net/efcore/entity-framework-core.aspx).
> Entity Framework is an Object/Relational Mapping (O/RM) framework. It is an enhancement to ADO.NET that gives developers an automated mechanism for accessing & storing the data in the database.
> EF Core supports two development approaches 1) Code-First 2) Database-First.
> In the code-first approach, EF Core API creates the database and tables using migration based on the conventions and configuration provided in your domain classes. This approach is useful in Domain Driven Design (DDD).
### Sonarcloud
[![SonarCloud](https://sonarcloud.io/images/project_badges/sonarcloud-white.svg)](https://sonarcloud.io/summary/new_code?id=csimonapastore_BasicDotnetTemplate)
This project is scanned on [SonarCloud](https://www.sonarsource.com/lp/products/sonarcloud/), a powerful cloud-based code analysis service designed to detect coding issues. It works with GitHub, Bitbucket Cloud, Azure DevOps and GitLab.
You can find the integration in [build.yml](.github/workflows/build.yml).
#### Quality gate
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=csimonapastore_BasicDotnetTemplate&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=csimonapastore_BasicDotnetTemplate)
A Quality Gate is a set of measure-based, Boolean conditions. It helps you know immediately whether your projects are production-ready.
This project uses **Sonar way** quality gate:
| Condition | Metric and rating | Real rating |
| ----------- | ----------- | ----------- |
| No new bugs are introduced | Reliability rating is A | [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=csimonapastore_BasicDotnetTemplate&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=csimonapastore_BasicDotnetTemplate) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=csimonapastore_BasicDotnetTemplate&metric=bugs)](https://sonarcloud.io/summary/new_code?id=csimonapastore_BasicDotnetTemplate) |
| No new vulnerabilities are introduced | Security rating is A | [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=csimonapastore_BasicDotnetTemplate&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=csimonapastore_BasicDotnetTemplate) [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=csimonapastore_BasicDotnetTemplate&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=csimonapastore_BasicDotnetTemplate) |
| New code has limited technical debt | Maintainability rating is A | [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=csimonapastore_BasicDotnetTemplate&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=csimonapastore_BasicDotnetTemplate) [![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=csimonapastore_BasicDotnetTemplate&metric=sqale_index)](https://sonarcloud.io/summary/new_code?id=csimonapastore_BasicDotnetTemplate) |
| All new security hotspots are reviewed | | |
| New code is sufficiently covered by test | Coverage is greater than or equal to 80.0% | [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=csimonapastore_BasicDotnetTemplate&metric=coverage)](https://sonarcloud.io/summary/new_code?id=csimonapastore_BasicDotnetTemplate) |
| New code has limited duplication | Duplicated Lines (%) is less than or equal to 3.0% | [![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=csimonapastore_BasicDotnetTemplate&metric=duplicated_lines_density)](https://sonarcloud.io/summary/new_code?id=csimonapastore_BasicDotnetTemplate) |
##### Code smells
[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=csimonapastore_BasicDotnetTemplate&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=csimonapastore_BasicDotnetTemplate)
> One way to look at smells is with respect to principles and quality: "Smells are certain structures in the code that indicate violation of fundamental design principles and negatively impact design quality".[Suryanarayana, Girish (November 2014). Refactoring for Software Design Smells. Morgan Kaufmann.]
> Code smells are usually not bugs; they are not technically incorrect and do not prevent the program from functioning. Instead, they indicate weaknesses in design that may slow down development or increase the risk of bugs or failures in the future. Bad code smells can be an indicator of factors that contribute to technical debt.[Tufano, Michele; Palomba, Fabio; Bavota, Gabriele; Oliveto, Rocco; Di Penta, Massimiliano; De Lucia, Andrea; Poshyvanyk, Denys (2015).] Robert C. Martin calls a list of code smells a "value system" for software craftsmanship.[Martin, Robert C. (2009). "17: Smells and Heuristics". Clean Code: A Handbook of Agile Software Craftsmanship. Prentice Hall.]
>