diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml new file mode 100644 index 0000000..2444b59 --- /dev/null +++ b/.github/workflows/dotnet-core.yml @@ -0,0 +1,92 @@ +name: .NET Core CI/CD + +on: + push: + branches: [ master, main, 'devin/**' ] + paths: + - '**.Core/**' + - 'SampleMvcWebApp.Core.sln' + - '.github/workflows/dotnet-core.yml' + pull_request: + branches: [ master, main ] + paths: + - '**.Core/**' + - 'SampleMvcWebApp.Core.sln' + - '.github/workflows/dotnet-core.yml' + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + dotnet-version: ['8.0.x'] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ matrix.dotnet-version }} + + - name: Display .NET version + run: dotnet --version + + - name: Restore dependencies + run: dotnet restore SampleMvcWebApp.Core.sln + + - name: Build + run: dotnet build SampleMvcWebApp.Core.sln --no-restore --configuration Release + + - name: Test + run: dotnet test SampleMvcWebApp.Core.sln --no-build --configuration Release --verbosity normal --logger "trx;LogFileName=test-results.trx" + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-results + path: '**/TestResults/*.trx' + + code-quality: + runs-on: ubuntu-latest + needs: build + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Restore dependencies + run: dotnet restore SampleMvcWebApp.Core.sln + + - name: Build with analyzers + run: dotnet build SampleMvcWebApp.Core.sln --configuration Release /p:TreatWarningsAsErrors=false + + docker-build: + runs-on: ubuntu-latest + needs: build + if: github.event_name == 'push' + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + push: false + tags: samplemvcwebapp-core:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/DataLayer.Core/Class1.cs b/DataLayer.Core/Class1.cs new file mode 100644 index 0000000..e14d7d8 --- /dev/null +++ b/DataLayer.Core/Class1.cs @@ -0,0 +1,9 @@ +using System; + +namespace DataLayer.Core +{ + public class Class1 + { + + } +} diff --git a/DataLayer.Core/DataLayer.Core.csproj b/DataLayer.Core/DataLayer.Core.csproj new file mode 100644 index 0000000..dbdcea4 --- /dev/null +++ b/DataLayer.Core/DataLayer.Core.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..122a9a0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,55 @@ +# Dockerfile for SampleMvcWebApp.Core +# Multi-stage build for optimized production image + +# Build stage +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +WORKDIR /src + +# Copy solution and project files first for better layer caching +COPY ["SampleMvcWebApp.Core.sln", "./"] +COPY ["SampleWebApp.Core/SampleWebApp.Core.csproj", "SampleWebApp.Core/"] +COPY ["ServiceLayer.Core/ServiceLayer.Core.csproj", "ServiceLayer.Core/"] +COPY ["DataLayer.Core/DataLayer.Core.csproj", "DataLayer.Core/"] +COPY ["Tests.Core/Tests.Core.csproj", "Tests.Core/"] + +# Restore dependencies +RUN dotnet restore "SampleMvcWebApp.Core.sln" + +# Copy all source code +COPY ["SampleWebApp.Core/", "SampleWebApp.Core/"] +COPY ["ServiceLayer.Core/", "ServiceLayer.Core/"] +COPY ["DataLayer.Core/", "DataLayer.Core/"] +COPY ["Tests.Core/", "Tests.Core/"] + +# Build the application +WORKDIR "/src/SampleWebApp.Core" +RUN dotnet build "SampleWebApp.Core.csproj" -c Release -o /app/build + +# Publish stage +FROM build AS publish +RUN dotnet publish "SampleWebApp.Core.csproj" -c Release -o /app/publish /p:UseAppHost=false + +# Runtime stage +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final +WORKDIR /app + +# Create non-root user for security +RUN adduser --disabled-password --gecos '' appuser && chown -R appuser /app +USER appuser + +# Copy published application +COPY --from=publish /app/publish . + +# Expose port 80 (standard HTTP port for containers) +EXPOSE 80 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:80/health || exit 1 + +# Set environment variables +ENV ASPNETCORE_URLS=http://+:80 +ENV ASPNETCORE_ENVIRONMENT=Production + +# Entry point +ENTRYPOINT ["dotnet", "SampleWebApp.Core.dll"] diff --git a/SampleMvcWebApp.Core.sln b/SampleMvcWebApp.Core.sln new file mode 100644 index 0000000..621960c --- /dev/null +++ b/SampleMvcWebApp.Core.sln @@ -0,0 +1,76 @@ + +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}") = "SampleWebApp.Core", "SampleWebApp.Core\SampleWebApp.Core.csproj", "{B494479D-4B57-4548-86DF-53F81BA64A8C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceLayer.Core", "ServiceLayer.Core\ServiceLayer.Core.csproj", "{0FB9C08C-5C93-47A8-A4A7-F6478540EC17}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataLayer.Core", "DataLayer.Core\DataLayer.Core.csproj", "{7A8C860C-C6F6-431C-9ED2-51775A9F0B00}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Core", "Tests.Core\Tests.Core.csproj", "{815C19E1-E09B-4BC1-9AF9-DDB8ABD5F9F3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B494479D-4B57-4548-86DF-53F81BA64A8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B494479D-4B57-4548-86DF-53F81BA64A8C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B494479D-4B57-4548-86DF-53F81BA64A8C}.Debug|x64.ActiveCfg = Debug|Any CPU + {B494479D-4B57-4548-86DF-53F81BA64A8C}.Debug|x64.Build.0 = Debug|Any CPU + {B494479D-4B57-4548-86DF-53F81BA64A8C}.Debug|x86.ActiveCfg = Debug|Any CPU + {B494479D-4B57-4548-86DF-53F81BA64A8C}.Debug|x86.Build.0 = Debug|Any CPU + {B494479D-4B57-4548-86DF-53F81BA64A8C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B494479D-4B57-4548-86DF-53F81BA64A8C}.Release|Any CPU.Build.0 = Release|Any CPU + {B494479D-4B57-4548-86DF-53F81BA64A8C}.Release|x64.ActiveCfg = Release|Any CPU + {B494479D-4B57-4548-86DF-53F81BA64A8C}.Release|x64.Build.0 = Release|Any CPU + {B494479D-4B57-4548-86DF-53F81BA64A8C}.Release|x86.ActiveCfg = Release|Any CPU + {B494479D-4B57-4548-86DF-53F81BA64A8C}.Release|x86.Build.0 = Release|Any CPU + {0FB9C08C-5C93-47A8-A4A7-F6478540EC17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0FB9C08C-5C93-47A8-A4A7-F6478540EC17}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0FB9C08C-5C93-47A8-A4A7-F6478540EC17}.Debug|x64.ActiveCfg = Debug|Any CPU + {0FB9C08C-5C93-47A8-A4A7-F6478540EC17}.Debug|x64.Build.0 = Debug|Any CPU + {0FB9C08C-5C93-47A8-A4A7-F6478540EC17}.Debug|x86.ActiveCfg = Debug|Any CPU + {0FB9C08C-5C93-47A8-A4A7-F6478540EC17}.Debug|x86.Build.0 = Debug|Any CPU + {0FB9C08C-5C93-47A8-A4A7-F6478540EC17}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0FB9C08C-5C93-47A8-A4A7-F6478540EC17}.Release|Any CPU.Build.0 = Release|Any CPU + {0FB9C08C-5C93-47A8-A4A7-F6478540EC17}.Release|x64.ActiveCfg = Release|Any CPU + {0FB9C08C-5C93-47A8-A4A7-F6478540EC17}.Release|x64.Build.0 = Release|Any CPU + {0FB9C08C-5C93-47A8-A4A7-F6478540EC17}.Release|x86.ActiveCfg = Release|Any CPU + {0FB9C08C-5C93-47A8-A4A7-F6478540EC17}.Release|x86.Build.0 = Release|Any CPU + {7A8C860C-C6F6-431C-9ED2-51775A9F0B00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A8C860C-C6F6-431C-9ED2-51775A9F0B00}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A8C860C-C6F6-431C-9ED2-51775A9F0B00}.Debug|x64.ActiveCfg = Debug|Any CPU + {7A8C860C-C6F6-431C-9ED2-51775A9F0B00}.Debug|x64.Build.0 = Debug|Any CPU + {7A8C860C-C6F6-431C-9ED2-51775A9F0B00}.Debug|x86.ActiveCfg = Debug|Any CPU + {7A8C860C-C6F6-431C-9ED2-51775A9F0B00}.Debug|x86.Build.0 = Debug|Any CPU + {7A8C860C-C6F6-431C-9ED2-51775A9F0B00}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A8C860C-C6F6-431C-9ED2-51775A9F0B00}.Release|Any CPU.Build.0 = Release|Any CPU + {7A8C860C-C6F6-431C-9ED2-51775A9F0B00}.Release|x64.ActiveCfg = Release|Any CPU + {7A8C860C-C6F6-431C-9ED2-51775A9F0B00}.Release|x64.Build.0 = Release|Any CPU + {7A8C860C-C6F6-431C-9ED2-51775A9F0B00}.Release|x86.ActiveCfg = Release|Any CPU + {7A8C860C-C6F6-431C-9ED2-51775A9F0B00}.Release|x86.Build.0 = Release|Any CPU + {815C19E1-E09B-4BC1-9AF9-DDB8ABD5F9F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {815C19E1-E09B-4BC1-9AF9-DDB8ABD5F9F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {815C19E1-E09B-4BC1-9AF9-DDB8ABD5F9F3}.Debug|x64.ActiveCfg = Debug|Any CPU + {815C19E1-E09B-4BC1-9AF9-DDB8ABD5F9F3}.Debug|x64.Build.0 = Debug|Any CPU + {815C19E1-E09B-4BC1-9AF9-DDB8ABD5F9F3}.Debug|x86.ActiveCfg = Debug|Any CPU + {815C19E1-E09B-4BC1-9AF9-DDB8ABD5F9F3}.Debug|x86.Build.0 = Debug|Any CPU + {815C19E1-E09B-4BC1-9AF9-DDB8ABD5F9F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {815C19E1-E09B-4BC1-9AF9-DDB8ABD5F9F3}.Release|Any CPU.Build.0 = Release|Any CPU + {815C19E1-E09B-4BC1-9AF9-DDB8ABD5F9F3}.Release|x64.ActiveCfg = Release|Any CPU + {815C19E1-E09B-4BC1-9AF9-DDB8ABD5F9F3}.Release|x64.Build.0 = Release|Any CPU + {815C19E1-E09B-4BC1-9AF9-DDB8ABD5F9F3}.Release|x86.ActiveCfg = Release|Any CPU + {815C19E1-E09B-4BC1-9AF9-DDB8ABD5F9F3}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/SampleWebApp.Core/Program.cs b/SampleWebApp.Core/Program.cs new file mode 100644 index 0000000..00ff539 --- /dev/null +++ b/SampleWebApp.Core/Program.cs @@ -0,0 +1,44 @@ +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +var summaries = new[] +{ + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" +}; + +app.MapGet("/weatherforecast", () => +{ + var forecast = Enumerable.Range(1, 5).Select(index => + new WeatherForecast + ( + DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + Random.Shared.Next(-20, 55), + summaries[Random.Shared.Next(summaries.Length)] + )) + .ToArray(); + return forecast; +}) +.WithName("GetWeatherForecast") +.WithOpenApi(); + +app.Run(); + +record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) +{ + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); +} diff --git a/SampleWebApp.Core/Properties/launchSettings.json b/SampleWebApp.Core/Properties/launchSettings.json new file mode 100644 index 0000000..e51c43d --- /dev/null +++ b/SampleWebApp.Core/Properties/launchSettings.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/SampleWebApp.Core/SampleWebApp.Core.csproj b/SampleWebApp.Core/SampleWebApp.Core.csproj new file mode 100644 index 0000000..2e500b1 --- /dev/null +++ b/SampleWebApp.Core/SampleWebApp.Core.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + diff --git a/SampleWebApp.Core/SampleWebApp.Core.http b/SampleWebApp.Core/SampleWebApp.Core.http new file mode 100644 index 0000000..0e4ee57 --- /dev/null +++ b/SampleWebApp.Core/SampleWebApp.Core.http @@ -0,0 +1,6 @@ +@SampleWebApp.Core_HostAddress = http://localhost:5174 + +GET {{SampleWebApp.Core_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/SampleWebApp.Core/appsettings.Development.json b/SampleWebApp.Core/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/SampleWebApp.Core/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/SampleWebApp.Core/appsettings.json b/SampleWebApp.Core/appsettings.json new file mode 100644 index 0000000..e296231 --- /dev/null +++ b/SampleWebApp.Core/appsettings.json @@ -0,0 +1,12 @@ +{ + "ConnectionStrings": { + "SampleWebAppDb": "Data Source=(localdb)\\mssqllocaldb;Initial Catalog=SampleWebAppDb;MultipleActiveResultSets=True;Integrated Security=SSPI;Trusted_Connection=True" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/ServiceLayer.Core/Class1.cs b/ServiceLayer.Core/Class1.cs new file mode 100644 index 0000000..935e0e3 --- /dev/null +++ b/ServiceLayer.Core/Class1.cs @@ -0,0 +1,9 @@ +using System; + +namespace ServiceLayer.Core +{ + public class Class1 + { + + } +} diff --git a/ServiceLayer.Core/ServiceLayer.Core.csproj b/ServiceLayer.Core/ServiceLayer.Core.csproj new file mode 100644 index 0000000..bb6bfbd --- /dev/null +++ b/ServiceLayer.Core/ServiceLayer.Core.csproj @@ -0,0 +1,11 @@ + + + + + + + + netstandard2.0 + + + diff --git a/Tests.Core/Tests.Core.csproj b/Tests.Core/Tests.Core.csproj new file mode 100644 index 0000000..c76157b --- /dev/null +++ b/Tests.Core/Tests.Core.csproj @@ -0,0 +1,29 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + + diff --git a/Tests.Core/UnitTest1.cs b/Tests.Core/UnitTest1.cs new file mode 100644 index 0000000..8d8af74 --- /dev/null +++ b/Tests.Core/UnitTest1.cs @@ -0,0 +1,10 @@ +namespace Tests.Core; + +public class UnitTest1 +{ + [Fact] + public void Test1() + { + + } +} \ No newline at end of file diff --git a/docs/ConfigurationMigration.md b/docs/ConfigurationMigration.md new file mode 100644 index 0000000..43ae8de --- /dev/null +++ b/docs/ConfigurationMigration.md @@ -0,0 +1,264 @@ +# Configuration Migration Plan + +This document outlines the migration strategy from `Web.config` to `appsettings.json` for the .NET Core application. + +## Overview + +The .NET Framework application uses `Web.config` for configuration, while .NET Core uses `appsettings.json` and environment-based configuration. This document maps the current configuration to the new format. + +## Current Configuration (Web.config) + +### Connection Strings + +**Source: `SampleWebApp/Web.config`** +```xml + + + +``` + +### App Settings + +**Source: `SampleWebApp/Web.config`** +```xml + + + + + + + +``` + +### Application Settings + +**Source: `SampleWebApp/Web.config`** +```xml + + + + LocalHost + + + jonsmith_ + + + +``` + +### Entity Framework Configuration + +**Source: `SampleWebApp/Web.config`** +```xml + + + + + + + + + + +``` + +## Target Configuration (appsettings.json) + +### Base Configuration + +**Target: `SampleWebApp.Core/appsettings.json`** +```json +{ + "ConnectionStrings": { + "SampleWebAppDb": "Data Source=(localdb)\\mssqllocaldb;Initial Catalog=SampleWebAppDb;MultipleActiveResultSets=True;Integrated Security=SSPI;Trusted_Connection=True" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Microsoft.EntityFrameworkCore": "Warning" + } + }, + "AllowedHosts": "*", + "Application": { + "HostType": "LocalHost", + "DatabaseLoginPrefix": "jonsmith_" + } +} +``` + +### Development Configuration + +**Target: `SampleWebApp.Core/appsettings.Development.json`** +```json +{ + "ConnectionStrings": { + "SampleWebAppDb": "Data Source=(localdb)\\mssqllocaldb;Initial Catalog=SampleWebAppDb-Dev;MultipleActiveResultSets=True;Integrated Security=SSPI;Trusted_Connection=True" + }, + "Logging": { + "LogLevel": { + "Default": "Debug", + "Microsoft.AspNetCore": "Information", + "Microsoft.EntityFrameworkCore": "Information" + } + }, + "Application": { + "HostType": "LocalHost" + } +} +``` + +### Production Configuration + +**Target: `SampleWebApp.Core/appsettings.Production.json`** +```json +{ + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning", + "Microsoft.EntityFrameworkCore": "Warning" + } + }, + "Application": { + "HostType": "Production" + } +} +``` + +### Azure Configuration + +**Target: `SampleWebApp.Core/appsettings.Azure.json`** +```json +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + }, + "ApplicationInsights": { + "LogLevel": { + "Default": "Information" + } + } + }, + "Application": { + "HostType": "Azure" + } +} +``` + +## Configuration Mapping Table + +| Web.config Section | Web.config Key | appsettings.json Path | Notes | +|--------------------|----------------|----------------------|-------| +| connectionStrings | SampleWebAppDb | ConnectionStrings:SampleWebAppDb | Direct mapping | +| appSettings | webpages:Version | N/A | Not needed in .NET Core | +| appSettings | webpages:Enabled | N/A | Not needed in .NET Core | +| appSettings | ClientValidationEnabled | N/A | Handled differently | +| appSettings | UnobtrusiveJavaScriptEnabled | N/A | Handled differently | +| appSettings | owin:AutomaticAppStartup | N/A | OWIN not used | +| applicationSettings | HostTypeString | Application:HostType | Custom setting | +| applicationSettings | DatabaseLoginPrefix | Application:DatabaseLoginPrefix | Custom setting | +| entityFramework | defaultConnectionFactory | N/A | Configured in code | +| system.web | compilation debug | ASPNETCORE_ENVIRONMENT | Environment-based | +| system.web | targetFramework | N/A | Project file | + +## Environment Variables + +For production deployments, sensitive configuration should use environment variables: + +```bash +# Connection string (override appsettings.json) +export ConnectionStrings__SampleWebAppDb="Server=prod-server;Database=SampleWebAppDb;User Id=app;Password=secret;" + +# Application settings +export Application__HostType="Production" + +# ASP.NET Core environment +export ASPNETCORE_ENVIRONMENT="Production" +``` + +## Configuration Classes + +### ApplicationSettings.cs + +```csharp +namespace SampleWebApp.Core.Configuration +{ + public class ApplicationSettings + { + public string HostType { get; set; } = "LocalHost"; + public string DatabaseLoginPrefix { get; set; } = string.Empty; + } +} +``` + +### Program.cs Configuration + +```csharp +var builder = WebApplication.CreateBuilder(args); + +// Bind configuration section to strongly-typed class +builder.Services.Configure( + builder.Configuration.GetSection("Application")); + +// Add DbContext with connection string +builder.Services.AddDbContext(options => + options.UseSqlServer( + builder.Configuration.GetConnectionString("SampleWebAppDb"))); +``` + +## Secrets Management + +### Development (User Secrets) + +```bash +# Initialize user secrets +dotnet user-secrets init --project SampleWebApp.Core + +# Set connection string secret +dotnet user-secrets set "ConnectionStrings:SampleWebAppDb" "Data Source=..." --project SampleWebApp.Core +``` + +### Production (Azure Key Vault) + +```csharp +// Program.cs +builder.Configuration.AddAzureKeyVault( + new Uri($"https://{keyVaultName}.vault.azure.net/"), + new DefaultAzureCredential()); +``` + +## Transform Files Migration + +### Web.Debug.config -> appsettings.Development.json + +The debug transforms are replaced by environment-specific JSON files. + +### Web.Release.config -> appsettings.Production.json + +Release transforms become production configuration. + +### Web.AzureRelease.config -> appsettings.Azure.json + +Azure-specific configuration in dedicated file. + +## Validation Checklist + +- [ ] Connection strings migrated correctly +- [ ] Custom application settings preserved +- [ ] Environment-specific overrides working +- [ ] Secrets not committed to source control +- [ ] Configuration binding tested +- [ ] Environment variables override JSON settings +- [ ] Logging configuration appropriate per environment + +## References + +- [Configuration in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/) +- [Options pattern in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options) +- [Safe storage of app secrets](https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets) diff --git a/docs/MigrationMatrix.md b/docs/MigrationMatrix.md new file mode 100644 index 0000000..eb5fcf5 --- /dev/null +++ b/docs/MigrationMatrix.md @@ -0,0 +1,158 @@ +# Migration Matrix Document + +This document provides a comprehensive mapping of the migration strategy from .NET Framework 4.5.1 to .NET Core 8.0 using the Strangler Fig Pattern. + +## Project Migration Overview + +| Current Project | .NET Core Target | Framework | Status | Priority | +|-----------------|------------------|-----------|--------|----------| +| SampleWebApp | SampleWebApp.Core | net8.0 | Infrastructure Ready | High | +| DataLayer | DataLayer.Core | netstandard2.0 | Infrastructure Ready | High | +| ServiceLayer | ServiceLayer.Core | netstandard2.0 | Infrastructure Ready | High | +| Tests | Tests.Core | net8.0 | Infrastructure Ready | Medium | + +## Detailed File Migration Matrix + +### SampleWebApp -> SampleWebApp.Core + +| Source Path | Target Path | Migration Complexity | Notes | +|-------------|-------------|---------------------|-------| +| Controllers/ | Controllers/ | Medium | Update base classes, action results | +| Controllers/PostsController.cs | Controllers/PostsController.cs | Medium | Update DI, async patterns | +| Controllers/PostsAsyncController.cs | Controllers/PostsController.cs | Low | Merge with sync controller | +| Controllers/TagsController.cs | Controllers/TagsController.cs | Medium | Update DI patterns | +| Controllers/TagsAsyncController.cs | Controllers/TagsController.cs | Low | Merge with sync controller | +| Controllers/BlogsController.cs | Controllers/BlogsController.cs | Medium | Update DI patterns | +| Controllers/HomeController.cs | Controllers/HomeController.cs | Low | Minimal changes | +| Views/ | Views/ | Medium | Update Razor syntax, tag helpers | +| Views/_ViewImports.cshtml | Views/_ViewImports.cshtml | Medium | Add tag helper imports | +| Views/Shared/_Layout.cshtml | Views/Shared/_Layout.cshtml | Medium | Update Bootstrap, scripts | +| Infrastructure/ | Infrastructure/ | High | Major DI changes | +| Infrastructure/AutofacDi.cs | Program.cs / Extensions/ | High | Convert to ASP.NET Core DI | +| Infrastructure/DiModelBinder.cs | Not needed | N/A | Use built-in DI | +| Infrastructure/WebUiInitialise.cs | Program.cs | High | Startup configuration | +| App_Start/ | Program.cs | High | Consolidate startup | +| App_Start/BundleConfig.cs | wwwroot/ or bundler | Medium | Use LibMan or npm | +| App_Start/FilterConfig.cs | Program.cs | Low | Register filters | +| App_Start/RouteConfig.cs | Program.cs | Low | Use attribute routing | +| Global.asax.cs | Program.cs | High | Application lifecycle | +| Web.config | appsettings.json | Medium | Configuration migration | +| packages.config | *.csproj | Low | PackageReference format | + +### DataLayer -> DataLayer.Core + +| Source Path | Target Path | Migration Complexity | Notes | +|-------------|-------------|---------------------|-------| +| DataClasses/ | DataClasses/ | Low | Entity classes mostly unchanged | +| DataClasses/Concrete/Post.cs | DataClasses/Concrete/Post.cs | Low | Minor attribute updates | +| DataClasses/Concrete/Tag.cs | DataClasses/Concrete/Tag.cs | Low | Minor attribute updates | +| DataClasses/Concrete/Blog.cs | DataClasses/Concrete/Blog.cs | Low | Minor attribute updates | +| SampleWebAppDb.cs | SampleWebAppDb.cs | High | EF6 to EF Core DbContext | +| Startup/ | Startup/ | Medium | Database initialization | +| Startup/DataLayerInitialise.cs | Extensions/ | Medium | EF Core migrations | +| App.config | N/A | N/A | Not needed in .NET Core | +| packages.config | DataLayer.Core.csproj | Low | PackageReference format | + +### ServiceLayer -> ServiceLayer.Core + +| Source Path | Target Path | Migration Complexity | Notes | +|-------------|-------------|---------------------|-------| +| PostServices/ | PostServices/ | Medium | DTO updates | +| PostServices/DetailPostDto.cs | PostServices/DetailPostDto.cs | Medium | GenericServices migration | +| PostServices/DetailPostDtoAsync.cs | PostServices/DetailPostDto.cs | Low | Merge with sync DTO | +| PostServices/SimplePostDto.cs | PostServices/SimplePostDto.cs | Low | Minimal changes | +| TagServices/ | TagServices/ | Medium | DTO updates | +| TagServices/TagListDto.cs | TagServices/TagListDto.cs | Low | Minimal changes | +| BlogServices/ | BlogServices/ | Medium | DTO updates | +| BlogServices/BlogListDto.cs | BlogServices/BlogListDto.cs | Low | Minimal changes | +| UiClasses/ | UiClasses/ | Low | UI helper classes | +| Startup/ | Extensions/ | Medium | Service registration | +| App.config | N/A | N/A | Not needed | +| packages.config | ServiceLayer.Core.csproj | Low | PackageReference format | + +### Tests -> Tests.Core + +| Source Path | Target Path | Migration Complexity | Notes | +|-------------|-------------|---------------------|-------| +| UnitTests/ | UnitTests/ | Medium | Update test framework | +| IntegrationTests/ | IntegrationTests/ | High | EF Core InMemory provider | +| Helpers/ | Helpers/ | Low | Test utilities | +| packages.config | Tests.Core.csproj | Low | xUnit already configured | + +## Dependency Chain + +``` +SampleWebApp.Core + ├── ServiceLayer.Core + │ └── DataLayer.Core + └── DataLayer.Core + +Tests.Core + ├── SampleWebApp.Core + ├── ServiceLayer.Core + └── DataLayer.Core +``` + +## Migration Phases + +### Phase 1: Infrastructure Setup (Current) +- [x] Create .NET Core solution structure +- [x] Configure project references +- [x] Set up CI/CD pipeline +- [x] Create Docker configuration +- [x] Document migration strategy + +### Phase 2: Data Layer Migration +- [ ] Migrate entity classes to DataLayer.Core +- [ ] Convert DbContext to EF Core +- [ ] Create EF Core migrations +- [ ] Update connection string handling +- [ ] Test database operations + +### Phase 3: Service Layer Migration +- [ ] Migrate DTOs to ServiceLayer.Core +- [ ] Update AutoMapper configurations +- [ ] Replace GenericServices with EF Core patterns +- [ ] Implement service interfaces +- [ ] Test service operations + +### Phase 4: Web Application Migration +- [ ] Migrate controllers to SampleWebApp.Core +- [ ] Convert views to ASP.NET Core Razor +- [ ] Update dependency injection +- [ ] Migrate authentication/authorization +- [ ] Update static file handling +- [ ] Test web endpoints + +### Phase 5: Testing & Validation +- [ ] Migrate unit tests to Tests.Core +- [ ] Create integration tests +- [ ] Perform side-by-side testing +- [ ] Validate feature parity +- [ ] Performance testing + +### Phase 6: Cutover +- [ ] Deploy .NET Core application +- [ ] Configure reverse proxy routing +- [ ] Gradual traffic migration +- [ ] Monitor and validate +- [ ] Decommission legacy application + +## Risk Assessment + +| Risk | Impact | Probability | Mitigation | +|------|--------|-------------|------------| +| GenericServices incompatibility | High | High | Custom implementation or alternative library | +| EF6 to EF Core data loss | High | Low | Comprehensive testing, backup strategy | +| Authentication migration issues | Medium | Medium | Parallel authentication during transition | +| Performance regression | Medium | Medium | Load testing, performance benchmarks | +| Breaking API changes | Medium | Low | API versioning, backward compatibility | + +## Success Criteria + +1. All unit tests pass in .NET Core +2. Feature parity with legacy application +3. No data loss during migration +4. Performance equal to or better than legacy +5. Successful deployment to production +6. Zero downtime during cutover diff --git a/docs/PackageMigration.md b/docs/PackageMigration.md new file mode 100644 index 0000000..0edfe42 --- /dev/null +++ b/docs/PackageMigration.md @@ -0,0 +1,162 @@ +# Package Migration Strategy + +This document outlines the migration strategy for NuGet packages from .NET Framework 4.5.1 to .NET Core 8.0. + +## Overview + +The existing SampleMvcWebApp uses various NuGet packages that need to be migrated to their .NET Core equivalents. This document provides a comprehensive mapping of current packages to their recommended .NET Core replacements. + +## Package Migration Matrix + +### Core Framework Packages + +| Current Package | Version | .NET Core Replacement | Target Version | Notes | +|-----------------|---------|----------------------|----------------|-------| +| EntityFramework | 6.1.3 | Microsoft.EntityFrameworkCore | 8.0.x | Complete rewrite required for DbContext | +| EntityFramework | 6.1.3 | Microsoft.EntityFrameworkCore.SqlServer | 8.0.x | SQL Server provider | +| EntityFramework | 6.1.3 | Microsoft.EntityFrameworkCore.Tools | 8.0.x | EF Core CLI tools | + +### Dependency Injection + +| Current Package | Version | .NET Core Replacement | Target Version | Notes | +|-----------------|---------|----------------------|----------------|-------| +| Autofac | 3.5.0 | Autofac | 8.0.x | Compatible with .NET Core | +| Autofac.Mvc5 | 3.3.1 | Autofac.Extensions.DependencyInjection | 9.0.x | Integration with ASP.NET Core DI | + +### Object Mapping + +| Current Package | Version | .NET Core Replacement | Target Version | Notes | +|-----------------|---------|----------------------|----------------|-------| +| AutoMapper | 4.2.1 | AutoMapper | 12.0.x | API changes in newer versions | +| AutoMapper | 4.2.1 | AutoMapper.Extensions.Microsoft.DependencyInjection | 12.0.x | DI integration | + +### ASP.NET MVC + +| Current Package | Version | .NET Core Replacement | Target Version | Notes | +|-----------------|---------|----------------------|----------------|-------| +| Microsoft.AspNet.Mvc | 5.2.3 | Built-in ASP.NET Core MVC | N/A | Part of framework | +| Microsoft.AspNet.Razor | 3.2.3 | Built-in ASP.NET Core Razor | N/A | Part of framework | +| Microsoft.AspNet.WebPages | 3.2.3 | Built-in ASP.NET Core | N/A | Part of framework | + +### Identity & Authentication + +| Current Package | Version | .NET Core Replacement | Target Version | Notes | +|-----------------|---------|----------------------|----------------|-------| +| Microsoft.AspNet.Identity.Core | 2.1.0 | Microsoft.AspNetCore.Identity | 8.0.x | Built into ASP.NET Core | +| Microsoft.AspNet.Identity.EntityFramework | 2.1.0 | Microsoft.AspNetCore.Identity.EntityFrameworkCore | 8.0.x | EF Core integration | +| Microsoft.AspNet.Identity.Owin | 2.1.0 | Built-in ASP.NET Core Identity | N/A | OWIN not needed | + +### OWIN Middleware + +| Current Package | Version | .NET Core Replacement | Target Version | Notes | +|-----------------|---------|----------------------|----------------|-------| +| Microsoft.Owin | 3.0.0 | Built-in ASP.NET Core middleware | N/A | Not needed | +| Microsoft.Owin.Security | 3.0.0 | Microsoft.AspNetCore.Authentication | 8.0.x | Built-in | +| Microsoft.Owin.Security.Cookies | 3.0.0 | Microsoft.AspNetCore.Authentication.Cookies | 8.0.x | Cookie auth | +| Microsoft.Owin.Security.OAuth | 3.0.0 | Microsoft.AspNetCore.Authentication.OAuth | 8.0.x | OAuth support | +| Microsoft.Owin.Security.Facebook | 3.0.0 | Microsoft.AspNetCore.Authentication.Facebook | 8.0.x | Facebook auth | +| Microsoft.Owin.Security.Google | 3.0.0 | Microsoft.AspNetCore.Authentication.Google | 8.0.x | Google auth | +| Microsoft.Owin.Security.MicrosoftAccount | 3.0.0 | Microsoft.AspNetCore.Authentication.MicrosoftAccount | 8.0.x | Microsoft auth | +| Microsoft.Owin.Security.Twitter | 2.1.0 | Microsoft.AspNetCore.Authentication.Twitter | 8.0.x | Twitter auth | + +### SignalR + +| Current Package | Version | .NET Core Replacement | Target Version | Notes | +|-----------------|---------|----------------------|----------------|-------| +| Microsoft.AspNet.SignalR | 2.0.3 | Microsoft.AspNetCore.SignalR | 8.0.x | Complete rewrite | +| Microsoft.AspNet.SignalR.Core | 2.0.3 | Microsoft.AspNetCore.SignalR | 8.0.x | Part of core package | +| Microsoft.AspNet.SignalR.JS | 2.0.3 | @microsoft/signalr | 8.0.x | npm package | +| Microsoft.AspNet.SignalR.SystemWeb | 2.0.3 | Not needed | N/A | Integrated in ASP.NET Core | + +### Frontend Libraries + +| Current Package | Version | .NET Core Replacement | Target Version | Notes | +|-----------------|---------|----------------------|----------------|-------| +| Bootstrap | 3.3.2 | Bootstrap (via npm/LibMan) | 5.3.x | Major version upgrade | +| jQuery | 1.10.2 | jQuery (via npm/LibMan) | 3.7.x | Security updates | +| jQuery.UI.Combined | 1.10.4 | jQuery UI (via npm/LibMan) | 1.13.x | Optional | +| jQuery.Validation | 1.11.1 | jquery-validation (via npm/LibMan) | 1.19.x | Form validation | +| Modernizr | 2.6.2 | modernizr (via npm/LibMan) | 3.13.x | Feature detection | +| Respond | 1.2.0 | Not needed | N/A | IE8 support, deprecated | + +### Utility Libraries + +| Current Package | Version | .NET Core Replacement | Target Version | Notes | +|-----------------|---------|----------------------|----------------|-------| +| Newtonsoft.Json | 6.0.4 | System.Text.Json or Newtonsoft.Json | 13.0.x | Built-in JSON or upgrade | +| log4net | 2.0.3 | Microsoft.Extensions.Logging + Serilog | 8.0.x | Modern logging | +| MarkdownSharp | 1.13.0.0 | Markdig | 0.34.x | Modern Markdown parser | +| DelegateDecompiler | 0.18.0 | DelegateDecompiler | 0.32.x | Compatible version | +| DelegateDecompiler.EntityFramework | 0.18.0 | DelegateDecompiler.EntityFrameworkCore | 0.32.x | EF Core version | + +### Bundling & Optimization + +| Current Package | Version | .NET Core Replacement | Target Version | Notes | +|-----------------|---------|----------------------|----------------|-------| +| Microsoft.AspNet.Web.Optimization | 1.1.3 | WebOptimizer.Core | 3.0.x | Or use bundler tools | +| WebGrease | 1.5.2 | Not needed | N/A | Use modern bundlers | +| Antlr | 3.4.1.9004 | Not needed | N/A | WebGrease dependency | + +### GenericServices (Custom Library) + +| Current Package | Version | .NET Core Replacement | Target Version | Notes | +|-----------------|---------|----------------------|----------------|-------| +| GenericServices | 1.0.9 | Custom implementation or EfCore.GenericServices | 5.x | May need custom port | +| GenericLibsBase | 1.0.1 | Custom implementation | N/A | May need custom port | + +## Migration Priority + +### Phase 1 - Critical (Infrastructure) +1. Entity Framework to EF Core +2. ASP.NET MVC to ASP.NET Core MVC +3. Autofac DI integration + +### Phase 2 - Authentication +1. ASP.NET Identity to ASP.NET Core Identity +2. OWIN middleware to ASP.NET Core middleware +3. External authentication providers + +### Phase 3 - Features +1. SignalR migration +2. AutoMapper upgrade +3. Logging infrastructure + +### Phase 4 - Frontend +1. Bootstrap upgrade +2. jQuery updates +3. Bundling/optimization + +## Recommended Package Installation Commands + +```bash +# Core packages for DataLayer.Core +dotnet add DataLayer.Core package Microsoft.EntityFrameworkCore --version 8.0.0 +dotnet add DataLayer.Core package Microsoft.EntityFrameworkCore.SqlServer --version 8.0.0 + +# Core packages for ServiceLayer.Core +dotnet add ServiceLayer.Core package AutoMapper --version 12.0.1 + +# Core packages for SampleWebApp.Core +dotnet add SampleWebApp.Core package Autofac.Extensions.DependencyInjection --version 9.0.0 +dotnet add SampleWebApp.Core package AutoMapper.Extensions.Microsoft.DependencyInjection --version 12.0.1 +dotnet add SampleWebApp.Core package Microsoft.AspNetCore.Identity.EntityFrameworkCore --version 8.0.0 +dotnet add SampleWebApp.Core package Serilog.AspNetCore --version 8.0.0 + +# Test packages for Tests.Core +dotnet add Tests.Core package Microsoft.EntityFrameworkCore.InMemory --version 8.0.0 +dotnet add Tests.Core package Moq --version 4.20.70 +``` + +## Breaking Changes to Address + +1. **Entity Framework**: Complete DbContext rewrite, LINQ query changes, migration format changes +2. **AutoMapper**: Profile-based configuration, projection changes +3. **Identity**: Different user/role management APIs +4. **SignalR**: Hub method signatures, client library changes +5. **Bundling**: Different approach to static file handling + +## References + +- [Microsoft .NET Upgrade Assistant](https://docs.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview) +- [Porting from .NET Framework to .NET Core](https://docs.microsoft.com/en-us/dotnet/core/porting/) +- [EF6 to EF Core Migration](https://docs.microsoft.com/en-us/ef/efcore-and-ef6/)