diff --git a/docs/migration/DEPENDENCY_ANALYSIS.md b/docs/migration/DEPENDENCY_ANALYSIS.md new file mode 100644 index 0000000..1b7ba4e --- /dev/null +++ b/docs/migration/DEPENDENCY_ANALYSIS.md @@ -0,0 +1,594 @@ +# Dependency Analysis Document: .NET Framework to .NET Core Migration + +## Overview + +This document provides a comprehensive analysis of all .NET Framework-specific packages in the SampleMvcWebApp application and their .NET Core alternatives. The application is currently built on .NET Framework 4.5.1 and targets migration to .NET Core 6.0 or later. + +## 1. Entity Framework Migration + +### Current State +- **Package**: Entity Framework 6.1.3 +- **Location**: `DataLayer/DataLayer.csproj` (lines 76-82) +- **Usage**: Database access layer with DbContext, entity configurations, and LINQ queries + +### Target +- **Package**: Microsoft.EntityFrameworkCore 8.0.x (recommended for new projects) or 6.0.x (LTS) +- **Additional Packages Required**: + - `Microsoft.EntityFrameworkCore.SqlServer` - SQL Server provider + - `Microsoft.EntityFrameworkCore.Tools` - Migrations tooling + - `Microsoft.EntityFrameworkCore.Design` - Design-time support + +### Breaking Changes Between EF 6.1.3 and EF Core + +#### DbContext Changes +| EF 6 Feature | EF Core Equivalent | Migration Notes | +|--------------|-------------------|-----------------| +| `Database.SetInitializer()` | `Database.EnsureCreated()` or Migrations | EF Core uses migrations by default; `EnsureCreated()` for simple scenarios | +| `DbContext.Database.Initialize()` | `Database.Migrate()` | Explicit migration application | +| `DbEntityValidationException` | `DbUpdateException` | Validation handled differently in EF Core | +| `DbEntityEntry.GetValidationResult()` | Custom validation | Must implement `IValidatableObject` or use FluentValidation | +| `ObjectContext` | Not available | EF Core is DbContext-only | + +#### Query Changes +| EF 6 Feature | EF Core Equivalent | Migration Notes | +|--------------|-------------------|-----------------| +| `DbSet.Find(id)` | `DbSet.Find(id)` | Same API, works identically | +| `DbSet.FindAsync(id)` | `DbSet.FindAsync(id)` | Same API, works identically | +| Lazy loading (default) | Explicit opt-in | Add `Microsoft.EntityFrameworkCore.Proxies` and configure | +| `Include()` with string | `Include()` with lambda only | String-based includes removed | +| `DbQuery` | `DbSet.HasNoKey()` | Keyless entity types | + +#### Configuration Changes +| EF 6 Feature | EF Core Equivalent | Migration Notes | +|--------------|-------------------|-----------------| +| `DbConfiguration` class | `DbContextOptionsBuilder` | Configuration moved to startup | +| `SqlAzureExecutionStrategy` | `EnableRetryOnFailure()` | Built into SQL Server provider | +| `connectionStrings` in Web.config | `appsettings.json` | Configuration source change | +| Fluent API in `OnModelCreating` | Same, with syntax changes | Some methods renamed | + +#### Specific Code Changes Required + +**Current `SampleWebAppDb.cs` (EF 6):** +```csharp +public class SampleWebAppDb : DbContext, IGenericServicesDbContext +{ + public SampleWebAppDb() : base("name=" + NameOfConnectionString) {} + + protected override DbEntityValidationResult ValidateEntity( + DbEntityEntry entityEntry, IDictionary items) + { + // Custom validation logic + } +} +``` + +**Target `SampleWebAppDb.cs` (EF Core):** +```csharp +public class SampleWebAppDb : DbContext +{ + public SampleWebAppDb(DbContextOptions options) : base(options) {} + + public override int SaveChanges() + { + // Custom validation must be handled differently + ValidateEntities(); + return base.SaveChanges(); + } + + private void ValidateEntities() + { + // Implement custom validation logic + } +} +``` + +**Current `DataLayerInitialise.cs` (EF 6):** +```csharp +Database.SetInitializer(new CreateDatabaseIfNotExists()); +``` + +**Target (EF Core):** +```csharp +// In Program.cs or Startup.cs +using (var scope = app.Services.CreateScope()) +{ + var db = scope.ServiceProvider.GetRequiredService(); + db.Database.EnsureCreated(); // or db.Database.Migrate(); +} +``` + +**Current `EfConfiguration.cs` (EF 6):** +```csharp +public class EfConfiguration : DbConfiguration +{ + public EfConfiguration() + { + if (IsAzure) + SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy()); + } +} +``` + +**Target (EF Core):** +```csharp +// In Program.cs +builder.Services.AddDbContext(options => + options.UseSqlServer(connectionString, sqlOptions => + sqlOptions.EnableRetryOnFailure( + maxRetryCount: 5, + maxRetryDelay: TimeSpan.FromSeconds(30), + errorNumbersToAdd: null))); +``` + +--- + +## 2. ASP.NET MVC Migration + +### Current State +- **Package**: Microsoft.AspNet.Mvc 5.2.3 +- **Location**: `SampleWebApp/SampleWebApp.csproj` (lines 150-152) +- **Usage**: Web application framework with controllers, views, and routing + +### Target +- **Package**: Built into ASP.NET Core (Microsoft.AspNetCore.Mvc) +- **Framework**: ASP.NET Core 6.0+ (included in .NET 6 SDK) + +### Breaking Changes Between ASP.NET MVC 5 and ASP.NET Core MVC + +#### Controller Base Classes +| MVC 5 | ASP.NET Core | Migration Notes | +|-------|--------------|-----------------| +| `System.Web.Mvc.Controller` | `Microsoft.AspNetCore.Mvc.Controller` | Different namespace, similar API | +| `ActionResult` | `IActionResult` | Interface-based return type | +| `HttpStatusCodeResult` | `StatusCodeResult` | Name change | +| `JsonResult` | `JsonResult` | Same name, different implementation | +| `ViewResult` | `ViewResult` | Same name, different implementation | + +#### Action Result Types +| MVC 5 | ASP.NET Core | Migration Notes | +|-------|--------------|-----------------| +| `new HttpStatusCodeResult(404)` | `NotFound()` | Helper methods available | +| `new JsonResult { Data = obj }` | `Json(obj)` | Simplified syntax | +| `new RedirectToRouteResult(...)` | `RedirectToAction(...)` | Same helper method | +| `Content(string)` | `Content(string)` | Same API | + +#### Routing Configuration + +**Current `RouteConfig.cs` (MVC 5):** +```csharp +public class RouteConfig +{ + public static void RegisterRoutes(RouteCollection routes) + { + routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); + routes.MapRoute( + name: "Default", + url: "{controller}/{action}/{id}", + defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } + ); + } +} +``` + +**Target (ASP.NET Core):** +```csharp +// In Program.cs +app.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); +``` + +#### View Rendering + +| MVC 5 Feature | ASP.NET Core Equivalent | Migration Notes | +|---------------|------------------------|-----------------| +| `@Html.ActionLink()` | `` or `@Html.ActionLink()` | Tag helpers preferred | +| `@Html.BeginForm()` | `
` | Tag helpers preferred | +| `@Html.EditorFor()` | `` | Tag helpers preferred | +| `@Styles.Render()` | Link tags or bundling middleware | Different bundling approach | +| `@Scripts.Render()` | Script tags or bundling middleware | Different bundling approach | +| `Web.config` in Views | `_ViewImports.cshtml` | Tag helper imports | + +#### Specific Controller Changes Required + +**Current `PostsController.cs` (MVC 5):** +```csharp +using System.Web.Mvc; + +public class PostsController : Controller +{ + public ActionResult Index(int? id, IListService service) + { + // Service injected via DiModelBinder + return View(query.ToList()); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public ActionResult Edit(DetailPostDto dto, IUpdateService service) + { + if (!ModelState.IsValid) + return View(service.ResetDto(dto)); + // ... + } +} +``` + +**Target (ASP.NET Core):** +```csharp +using Microsoft.AspNetCore.Mvc; + +public class PostsController : Controller +{ + private readonly IListService _listService; + private readonly IUpdateService _updateService; + + public PostsController(IListService listService, IUpdateService updateService) + { + _listService = listService; + _updateService = updateService; + } + + public IActionResult Index(int? id) + { + // Service injected via constructor + return View(query.ToList()); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public IActionResult Edit(DetailPostDto dto) + { + if (!ModelState.IsValid) + return View(_updateService.ResetDto(dto)); + // ... + } +} +``` + +#### Global.asax to Program.cs + +**Current `Global.asax.cs` (MVC 5):** +```csharp +public class MvcApplication : System.Web.HttpApplication +{ + protected void Application_Start() + { + AreaRegistration.RegisterAllAreas(); + FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); + RouteConfig.RegisterRoutes(RouteTable.Routes); + BundleConfig.RegisterBundles(BundleTable.Bundles); + ModelBinders.Binders.DefaultBinder = new DiModelBinder(); + WebUiInitialise.InitialiseThis(this); + } +} +``` + +**Target `Program.cs` (ASP.NET Core):** +```csharp +var builder = WebApplication.CreateBuilder(args); + +// Add services +builder.Services.AddControllersWithViews(); +builder.Services.AddDbContext(options => ...); +// Register other services + +var app = builder.Build(); + +// Configure middleware +app.UseStaticFiles(); +app.UseRouting(); +app.UseAuthorization(); + +app.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); + +app.Run(); +``` + +--- + +## 3. Autofac Dependency Injection + +### Current State +- **Package**: Autofac 3.5.0, Autofac.Mvc5 3.3.1 +- **Location**: `SampleWebApp/SampleWebApp.csproj` (lines 43-49) +- **Usage**: Dependency injection container for all layers + +### Target Options + +#### Option A: Continue with Autofac (Recommended for minimal changes) +- **Package**: Autofac 8.x + Autofac.Extensions.DependencyInjection +- **Rationale**: Preserves existing module structure, minimal code changes + +#### Option B: Migrate to Built-in .NET Core DI +- **Package**: Microsoft.Extensions.DependencyInjection (built-in) +- **Rationale**: Simpler, no external dependency, sufficient for most scenarios + +### Recommendation: Option A (Autofac) + +The application uses Autofac modules extensively (`ServiceLayerModule`, `DataLayerModule`), and Autofac provides features not available in built-in DI (property injection, modules, advanced lifetime scopes). Continuing with Autofac minimizes migration risk. + +### Breaking Changes Between Autofac 3.5.0 and Autofac 8.x + +| Autofac 3.5 Feature | Autofac 8.x Equivalent | Migration Notes | +|---------------------|----------------------|-----------------| +| `ContainerBuilder.Build()` | Same API | No change | +| `builder.RegisterModule()` | Same API | No change | +| `AutofacDependencyResolver` | `AutofacServiceProviderFactory` | Integration approach changed | +| `DependencyResolver.SetResolver()` | `builder.Host.UseServiceProviderFactory()` | ASP.NET Core integration | +| `InstancePerHttpRequest` | `InstancePerLifetimeScope` | Scope naming changed | + +### Configuration Changes Required + +**Current `AutofacDi.cs` (Autofac 3.5):** +```csharp +internal static IContainer SetupDependency() +{ + var builder = new ContainerBuilder(); + builder.RegisterModule(new ServiceLayerModule()); + _container = builder.Build(); + return _container; +} +``` + +**Current `WebUiInitialise.cs` (MVC 5):** +```csharp +var container = AutofacDi.SetupDependency(); +var mvcResolver = new AutofacDependencyResolver(container); +DependencyResolver.SetResolver(mvcResolver); +``` + +**Target `Program.cs` (ASP.NET Core with Autofac):** +```csharp +var builder = WebApplication.CreateBuilder(args); + +// Use Autofac as the service provider factory +builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); + +builder.Host.ConfigureContainer(containerBuilder => +{ + containerBuilder.RegisterModule(new ServiceLayerModule()); +}); + +builder.Services.AddControllersWithViews(); +// ... other services + +var app = builder.Build(); +``` + +### DiModelBinder Replacement + +The current `DiModelBinder` allows services to be injected as action parameters. In ASP.NET Core, this pattern is replaced by: + +1. **Constructor Injection** (preferred): Services injected via controller constructor +2. **`[FromServices]` Attribute**: Services injected as action parameters + +**Current Pattern (MVC 5):** +```csharp +public ActionResult Index(int? id, IListService service) +{ + // service injected via DiModelBinder +} +``` + +**Target Pattern (ASP.NET Core):** +```csharp +// Option 1: Constructor injection +private readonly IListService _service; +public PostsController(IListService service) => _service = service; + +public IActionResult Index(int? id) +{ + // use _service +} + +// Option 2: FromServices attribute +public IActionResult Index(int? id, [FromServices] IListService service) +{ + // service injected via attribute +} +``` + +--- + +## 4. System.Web Dependencies + +### Current State +System.Web is used throughout the presentation layer for HTTP abstractions, MVC components, and web infrastructure. + +### Files with System.Web Dependencies + +| File | System.Web Usages | ASP.NET Core Equivalent | +|------|-------------------|------------------------| +| `Global.asax.cs` | `HttpApplication`, `System.Web.Mvc`, `System.Web.Optimization`, `System.Web.Routing` | `Program.cs` with middleware | +| `WebUiInitialise.cs` | `System.Web.HttpApplication`, `System.Web.Mvc.DependencyResolver` | ASP.NET Core DI | +| `DiModelBinder.cs` | `System.Web.Mvc.DefaultModelBinder`, `ControllerContext`, `ModelBindingContext` | Constructor injection or `[FromServices]` | +| `RouteConfig.cs` | `System.Web.Routing.RouteCollection` | Endpoint routing | +| `BundleConfig.cs` | `System.Web.Optimization.BundleCollection` | WebOptimizer or manual bundling | +| `FilterConfig.cs` | `System.Web.Mvc.GlobalFilterCollection` | `builder.Services.AddControllersWithViews(options => ...)` | +| `ValidationHelper.cs` | `System.Web.Mvc.ModelStateDictionary`, `System.Web.Mvc.JsonResult` | Same names in `Microsoft.AspNetCore.Mvc` | +| All Controllers | `System.Web.Mvc.Controller`, `ActionResult` | `Microsoft.AspNetCore.Mvc.Controller`, `IActionResult` | + +### Detailed Migration for Each System.Web Usage + +#### HttpApplication (Global.asax) +- **Current**: `MvcApplication : System.Web.HttpApplication` +- **Target**: Remove entirely; use `Program.cs` with minimal hosting model +- **Action**: Delete `Global.asax` and `Global.asax.cs`; move initialization to `Program.cs` + +#### HttpContext +- **Current**: `System.Web.HttpContext.Current` +- **Target**: `IHttpContextAccessor` injected via DI +- **Action**: Inject `IHttpContextAccessor` where needed; avoid static access + +#### Server.MapPath +- **Current**: `application.Server.MapPath(path)` +- **Target**: `IWebHostEnvironment.ContentRootPath` or `WebRootPath` +- **Action**: Inject `IWebHostEnvironment` and use `Path.Combine()` + +#### ModelStateDictionary +- **Current**: `System.Web.Mvc.ModelStateDictionary` +- **Target**: `Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateDictionary` +- **Action**: Update namespace; API is similar + +#### JsonResult +- **Current**: `new JsonResult { Data = obj }` +- **Target**: `Json(obj)` helper method or `new JsonResult(obj)` +- **Action**: Use controller helper methods + +--- + +## 5. Additional Dependencies Analysis + +### 5.1 AutoMapper + +#### Current State +- **Package**: AutoMapper 4.2.1 +- **Location**: All projects (`packages.config`) +- **Usage**: Entity-to-DTO mapping in GenericServices + +#### Target +- **Package**: AutoMapper 13.x +- **Compatibility**: Fully compatible with .NET Core + +#### Breaking Changes +| AutoMapper 4.x | AutoMapper 13.x | Migration Notes | +|----------------|-----------------|-----------------| +| `Mapper.CreateMap()` | Profile-based configuration | Static API removed | +| `Mapper.Map(source)` | `IMapper.Map(source)` | Instance-based mapping | +| Static configuration | `AddAutoMapper(assemblies)` | DI integration | + +#### Migration Steps +1. Create mapping profiles inheriting from `Profile` +2. Register AutoMapper in DI: `builder.Services.AddAutoMapper(typeof(Program).Assembly)` +3. Inject `IMapper` where needed +4. Remove static `Mapper` calls + +### 5.2 ASP.NET Identity + +#### Current State +- **Package**: Microsoft.AspNet.Identity.Core 2.1.0, Microsoft.AspNet.Identity.EntityFramework 2.1.0, Microsoft.AspNet.Identity.Owin 2.1.0 +- **Location**: `SampleWebApp/packages.config` (lines 19-21) +- **Usage**: Referenced but not actively used in current codebase + +#### Target +- **Package**: Microsoft.AspNetCore.Identity.EntityFrameworkCore +- **Compatibility**: Complete rewrite required if Identity is used + +#### Breaking Changes +| ASP.NET Identity 2.x | ASP.NET Core Identity | Migration Notes | +|---------------------|----------------------|-----------------| +| `UserManager` | `UserManager` | Similar API, different namespace | +| `SignInManager` | `SignInManager` | Generic type parameter added | +| OWIN middleware | ASP.NET Core middleware | Complete configuration change | +| `IdentityDbContext` | `IdentityDbContext` | Similar but different namespace | + +#### Recommendation +Since Identity appears to be referenced but not actively used (authentication is disabled in Web.config), consider removing these packages during migration unless authentication is planned. + +### 5.3 SignalR + +#### Current State +- **Package**: Microsoft.AspNet.SignalR 2.0.3 +- **Location**: `SampleWebApp/packages.config` (lines 24-27) +- **Usage**: Referenced but appears unused in current codebase (ActionRunner scripts commented out) + +#### Target +- **Package**: Microsoft.AspNetCore.SignalR (built into ASP.NET Core) +- **Compatibility**: Complete rewrite required + +#### Breaking Changes +| SignalR 2.x | ASP.NET Core SignalR | Migration Notes | +|-------------|---------------------|-----------------| +| `Hub` base class | `Hub` base class | Different namespace | +| `$.connection.hub` | `HubConnectionBuilder` | JavaScript client rewrite | +| OWIN startup | Endpoint routing | `app.MapHub("/path")` | +| `GlobalHost.ConnectionManager` | `IHubContext` via DI | Dependency injection | + +#### Recommendation +Since SignalR appears unused (ActionRunner functionality commented out in BundleConfig.cs), consider removing these packages during migration unless real-time features are planned. + +### 5.4 GenericServices + +#### Current State +- **Package**: GenericServices 1.0.9, GenericLibsBase 1.0.1 +- **Location**: All projects +- **Usage**: Core service layer abstraction for CRUD operations + +#### Target +- **Replacement**: Custom services + MediatR pattern (as per migration plan) +- **Rationale**: GenericServices is not maintained for .NET Core; MediatR provides similar patterns + +#### Components to Replace + +| GenericServices Component | Replacement Strategy | +|--------------------------|---------------------| +| `IListService` | Custom query handlers with MediatR | +| `IDetailService` / `IDetailServiceAsync` | MediatR query handlers | +| `ICreateService` / `ICreateServiceAsync` | MediatR command handlers | +| `IUpdateService` / `IUpdateServiceAsync` | MediatR command handlers | +| `IDeleteService` / `IDeleteServiceAsync` | MediatR command handlers | +| `EfGenericDto` | AutoMapper profiles + custom DTOs | +| `EfGenericDtoAsync` | AutoMapper profiles + custom DTOs | +| `IGenericServicesDbContext` | Standard `DbContext` | +| `ISuccessOrErrors` | `Result` pattern or FluentResults | + +#### Migration Approach +1. Create MediatR request/response classes for each operation +2. Implement handlers that replace GenericServices functionality +3. Replace DTO base classes with plain DTOs + AutoMapper profiles +4. Implement result pattern for success/error handling + +### 5.5 Other Dependencies + +#### log4net 2.0.3 +- **Target**: Microsoft.Extensions.Logging + Serilog (or keep log4net) +- **Compatibility**: log4net works with .NET Core +- **Recommendation**: Migrate to `ILogger` for consistency with ASP.NET Core + +#### Newtonsoft.Json 6.0.4 +- **Target**: System.Text.Json (built-in) or Newtonsoft.Json 13.x +- **Compatibility**: Both work with .NET Core +- **Recommendation**: Use System.Text.Json unless specific Newtonsoft features needed + +#### DelegateDecompiler 0.18.0 +- **Target**: DelegateDecompiler.EntityFramework.Core +- **Compatibility**: .NET Core version available +- **Note**: May not be needed if computed properties are refactored + +--- + +## Summary: Recommended Migration Approach + +### Phase 1: Infrastructure (Low Risk) +1. Migrate to .NET 6/8 SDK project format +2. Update Autofac to latest version +3. Update AutoMapper to latest version +4. Update Newtonsoft.Json or migrate to System.Text.Json + +### Phase 2: Data Layer (Medium Risk) +1. Migrate Entity Framework 6 to EF Core +2. Update DbContext configuration +3. Migrate connection string to appsettings.json +4. Update database initialization logic + +### Phase 3: Service Layer (High Risk) +1. Replace GenericServices with custom services + MediatR +2. Update DTOs to remove GenericServices base classes +3. Implement result pattern for error handling +4. Update AutoMapper configurations + +### Phase 4: Presentation Layer (Medium Risk) +1. Migrate controllers from MVC 5 to ASP.NET Core MVC +2. Replace Global.asax with Program.cs +3. Update routing configuration +4. Migrate views (minimal changes with Razor) +5. Update bundling/minification approach + +### Phase 5: Cleanup (Low Risk) +1. Remove unused packages (Identity, SignalR if not needed) +2. Update logging to ILogger +3. Remove System.Web references +4. Final testing and validation diff --git a/docs/migration/MIGRATION_BOUNDARIES.md b/docs/migration/MIGRATION_BOUNDARIES.md new file mode 100644 index 0000000..49dfac3 --- /dev/null +++ b/docs/migration/MIGRATION_BOUNDARIES.md @@ -0,0 +1,868 @@ +# Migration Boundaries Document: .NET Framework to .NET Core Migration + +## Overview + +This document provides a detailed breakdown of each layer in the SampleMvcWebApp application, identifying migration boundaries and listing all files that will require modification during the .NET Framework to .NET Core migration. + +## Architecture Overview + +The application follows a classic 3-tier layered architecture: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ SampleWebApp │ +│ (Presentation Layer - ASP.NET MVC) │ +│ Controllers, Views, Infrastructure, Configuration │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ ServiceLayer │ +│ (Business Logic / Service Layer) │ +│ DTOs, Service Interfaces, AutoMapper Configurations │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ DataLayer │ +│ (Data Access Layer - Entity Framework) │ +│ Entities, DbContext, Database Configurations │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 1. DataLayer Analysis + +### Location +`DataLayer/DataLayer.csproj` + +### Purpose +The DataLayer provides data access functionality using Entity Framework 6. It contains entity definitions, the DbContext, database initialization logic, and Autofac module registration. + +### Key Components + +#### 1.1 Entity Classes + +| File | Description | Migration Impact | +|------|-------------|------------------| +| `DataClasses/Concrete/Blog.cs` | Blog entity with Name, EmailAddress, Posts collection | **Low** - Data annotations compatible | +| `DataClasses/Concrete/Post.cs` | Post entity with Title, Content, Tags, implements IValidatableObject | **Medium** - Validation logic needs review | +| `DataClasses/Concrete/Tag.cs` | Tag entity with Slug, Name, Posts collection | **Low** - Data annotations compatible | +| `DataClasses/Concrete/Helpers/TrackUpdate.cs` | Base class for tracking LastUpdated | **Low** - No EF-specific code | + +#### Entity Relationships +- **Blog** (1) → (Many) **Post**: One-to-many via `BlogId` foreign key +- **Post** (Many) ↔ (Many) **Tag**: Many-to-many via junction table (EF convention) + +#### Entity Class Changes Required + +**`Post.cs` - IValidatableObject Implementation:** +```csharp +// Current (EF 6) - Works in EF Core +public class Post : TrackUpdate, IValidatableObject +{ + public IEnumerable Validate(ValidationContext validationContext) + { + // Custom validation logic - compatible with EF Core + } +} +``` +**Migration Notes**: The `IValidatableObject` interface works in EF Core, but validation is not automatically called. Must be invoked manually or via `SaveChanges` override. + +#### 1.2 DbContext + +| File | Description | Migration Impact | +|------|-------------|------------------| +| `DataClasses/SampleWebAppDb.cs` | Main DbContext with DbSets, SaveChanges override, ValidateEntity override | **High** - Significant changes required | + +**Current Implementation Analysis:** + +```csharp +public class SampleWebAppDb : DbContext, IGenericServicesDbContext +{ + // Connection string from Web.config + public SampleWebAppDb() : base("name=" + NameOfConnectionString) {} + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + public DbSet Tags { get; set; } + + // Override for change tracking + public override int SaveChanges() + { + HandleChangeTracking(); + return base.SaveChanges(); + } + + // Override for async + public override Task SaveChangesAsync() + { + HandleChangeTracking(); + return base.SaveChangesAsync(); + } + + // EF 6 specific - ValidateEntity override + protected override DbEntityValidationResult ValidateEntity( + DbEntityEntry entityEntry, IDictionary items) + { + // Custom validation for Tag uniqueness + } +} +``` + +**Required Changes for EF Core:** +1. Remove `IGenericServicesDbContext` interface (GenericServices replacement) +2. Change constructor to accept `DbContextOptions` +3. Remove `ValidateEntity` override (not available in EF Core) +4. Implement custom validation in `SaveChanges`/`SaveChangesAsync` +5. Update `HandleChangeTracking` to use EF Core change tracker API + +#### 1.3 Configuration + +| File | Description | Migration Impact | +|------|-------------|------------------| +| `DataClasses/EfConfiguration.cs` | DbConfiguration for Azure retry strategy | **High** - Complete replacement | + +**Current Implementation:** +```csharp +public class EfConfiguration : DbConfiguration +{ + public static bool IsAzure { get; internal set; } + + public EfConfiguration() + { + if (IsAzure) + SetExecutionStrategy("System.Data.SqlClient", + () => new SqlAzureExecutionStrategy()); + } +} +``` + +**Required Changes**: Delete this file; configure retry in `Program.cs`: +```csharp +options.UseSqlServer(connectionString, sqlOptions => + sqlOptions.EnableRetryOnFailure()); +``` + +#### 1.4 Startup/Initialization + +| File | Description | Migration Impact | +|------|-------------|------------------| +| `Startup/DataLayerInitialise.cs` | Database initialization, seeding | **High** - Database.SetInitializer replacement | +| `Startup/DataLayerModule.cs` | Autofac module for DI registration | **Medium** - Update for EF Core DI | +| `Startup/Internal/LoadDbDataFromXml.cs` | XML data loading for seeding | **Low** - No EF-specific code | + +**DataLayerInitialise.cs Changes:** +```csharp +// Current (EF 6) +Database.SetInitializer(new CreateDatabaseIfNotExists()); + +// Target (EF Core) - Move to Program.cs +using (var scope = app.Services.CreateScope()) +{ + var db = scope.ServiceProvider.GetRequiredService(); + db.Database.EnsureCreated(); +} +``` + +**DataLayerModule.cs Changes:** +```csharp +// Current (Autofac 3.5) +builder.RegisterType() + .As() + .As() + .InstancePerLifetimeScope(); + +// Target (Autofac 8.x with EF Core) +builder.RegisterType() + .AsSelf() + .InstancePerLifetimeScope(); +// Note: DbContext registration typically done via AddDbContext in ASP.NET Core +``` + +#### 1.5 Project File + +| File | Description | Migration Impact | +|------|-------------|------------------| +| `DataLayer.csproj` | Project file (old format) | **High** - Convert to SDK-style | +| `packages.config` | NuGet packages | **High** - Convert to PackageReference | +| `App.config` | Configuration | **Medium** - Merge into appsettings.json | + +### DataLayer Files Summary + +| File Path | Change Type | Priority | +|-----------|-------------|----------| +| `DataLayer.csproj` | Replace with SDK-style | High | +| `packages.config` | Delete (use PackageReference) | High | +| `DataClasses/SampleWebAppDb.cs` | Major refactor | High | +| `DataClasses/EfConfiguration.cs` | Delete | High | +| `Startup/DataLayerInitialise.cs` | Major refactor | High | +| `Startup/DataLayerModule.cs` | Update registrations | Medium | +| `DataClasses/Concrete/Blog.cs` | Namespace updates only | Low | +| `DataClasses/Concrete/Post.cs` | Namespace updates only | Low | +| `DataClasses/Concrete/Tag.cs` | Namespace updates only | Low | +| `DataClasses/Concrete/Helpers/TrackUpdate.cs` | No changes | None | +| `Startup/Internal/LoadDbDataFromXml.cs` | No changes | None | + +--- + +## 2. ServiceLayer Analysis + +### Location +`ServiceLayer/ServiceLayer.csproj` + +### Purpose +The ServiceLayer contains DTOs (Data Transfer Objects), service abstractions, and business logic. It heavily depends on the GenericServices library which will be replaced with custom services + MediatR. + +### Key Components + +#### 2.1 DTOs (Data Transfer Objects) + +| File | Description | Migration Impact | +|------|-------------|------------------| +| `PostServices/DetailPostDto.cs` | Full post DTO for CRUD, inherits `EfGenericDto` | **High** - Remove GenericServices base | +| `PostServices/DetailPostDtoAsync.cs` | Async version, inherits `EfGenericDtoAsync` | **High** - Remove GenericServices base | +| `PostServices/SimplePostDto.cs` | List view DTO, inherits `EfGenericDto` | **High** - Remove GenericServices base | +| `PostServices/SimplePostDtoAsync.cs` | Async list DTO | **High** - Remove GenericServices base | +| `BlogServices/BlogListDto.cs` | Blog list DTO, inherits `EfGenericDto` | **High** - Remove GenericServices base | +| `TagServices/TagListDto.cs` | Tag list DTO, inherits `EfGenericDto` | **High** - Remove GenericServices base | + +**Current DTO Pattern (GenericServices):** +```csharp +public class DetailPostDto : EfGenericDto +{ + [Key] + public int PostId { get; set; } + public string Title { get; set; } + public string Content { get; set; } + + // GenericServices-specific + protected override CrudFunctions SupportedFunctions + { + get { return CrudFunctions.AllCrud; } + } + + protected override void SetupSecondaryData( + IGenericServicesDbContext context, DetailPostDto dto) + { + // Setup dropdown lists + } + + protected override ISuccessOrErrors CreateDataFromDto( + IGenericServicesDbContext context, DetailPostDto source) + { + // Custom create logic + } +} +``` + +**Target DTO Pattern (Plain DTO + AutoMapper):** +```csharp +public class DetailPostDto +{ + [Key] + public int PostId { get; set; } + public string Title { get; set; } + public string Content { get; set; } + + // UI helper properties + public DropDownListType Bloggers { get; set; } + public MultiSelectListType UserChosenTags { get; set; } +} + +// Separate mapping profile +public class PostMappingProfile : Profile +{ + public PostMappingProfile() + { + CreateMap(); + CreateMap(); + } +} + +// Separate service for setup +public interface IPostSetupService +{ + Task SetupSecondaryDataAsync(DetailPostDto dto); +} +``` + +#### 2.2 UI Helper Classes + +| File | Description | Migration Impact | +|------|-------------|------------------| +| `UiClasses/DropDownListType.cs` | Dropdown list helper | **Low** - No framework dependencies | +| `UiClasses/MultiSelectListType.cs` | Multi-select list helper | **Low** - No framework dependencies | + +These classes are framework-agnostic and require no changes. + +#### 2.3 Startup/Initialization + +| File | Description | Migration Impact | +|------|-------------|------------------| +| `Startup/ServiceLayerInitialise.cs` | Layer initialization | **Medium** - Remove GenericServices init | +| `Startup/ServiceLayerModule.cs` | Autofac module | **High** - Update for new services | + +**ServiceLayerModule.cs Changes:** +```csharp +// Current (registers GenericServices) +builder.RegisterAssemblyTypes(typeof(IListService).Assembly) + .AsImplementedInterfaces(); + +// Target (register MediatR and custom services) +builder.RegisterMediatR(typeof(ServiceLayerModule).Assembly); +builder.RegisterAssemblyTypes(typeof(ServiceLayerModule).Assembly) + .Where(t => t.Name.EndsWith("Service")) + .AsImplementedInterfaces(); +``` + +#### 2.4 GenericServices Dependencies to Replace + +| GenericServices Interface | Current Usage | Replacement | +|--------------------------|---------------|-------------| +| `IListService` | List queries | MediatR `IRequest>` | +| `IDetailService` | Get by ID | MediatR `IRequest` | +| `IDetailServiceAsync` | Async get by ID | MediatR `IRequest` | +| `ICreateService` | Create entity | MediatR `IRequest` | +| `ICreateServiceAsync` | Async create | MediatR `IRequest` | +| `IUpdateService` | Update entity | MediatR `IRequest` | +| `IUpdateServiceAsync` | Async update | MediatR `IRequest` | +| `IDeleteService` | Delete entity | MediatR `IRequest` | +| `IDeleteServiceAsync` | Async delete | MediatR `IRequest` | +| `ICreateSetupService` | Setup for create | Custom service | +| `IUpdateSetupService` | Setup for update | Custom service | +| `ISuccessOrErrors` | Result type | `Result` pattern | +| `EfGenericDto<,>` | DTO base class | Plain DTO | +| `EfGenericDtoAsync<,>` | Async DTO base | Plain DTO | +| `IGenericServicesDbContext` | DbContext interface | Standard DbContext | + +#### 2.5 Project File + +| File | Description | Migration Impact | +|------|-------------|------------------| +| `ServiceLayer.csproj` | Project file (old format) | **High** - Convert to SDK-style | +| `packages.config` | NuGet packages | **High** - Convert to PackageReference | + +### ServiceLayer Files Summary + +| File Path | Change Type | Priority | +|-----------|-------------|----------| +| `ServiceLayer.csproj` | Replace with SDK-style | High | +| `packages.config` | Delete (use PackageReference) | High | +| `PostServices/DetailPostDto.cs` | Remove GenericServices base, refactor | High | +| `PostServices/DetailPostDtoAsync.cs` | Remove GenericServices base, refactor | High | +| `PostServices/SimplePostDto.cs` | Remove GenericServices base | High | +| `PostServices/SimplePostDtoAsync.cs` | Remove GenericServices base | High | +| `BlogServices/BlogListDto.cs` | Remove GenericServices base | High | +| `TagServices/TagListDto.cs` | Remove GenericServices base | High | +| `Startup/ServiceLayerModule.cs` | Update for MediatR | High | +| `Startup/ServiceLayerInitialise.cs` | Remove GenericServices init | Medium | +| `UiClasses/DropDownListType.cs` | No changes | None | +| `UiClasses/MultiSelectListType.cs` | No changes | None | + +### New Files to Create + +| File Path | Description | +|-----------|-------------| +| `PostServices/Queries/GetPostsQuery.cs` | MediatR query for post list | +| `PostServices/Queries/GetPostByIdQuery.cs` | MediatR query for single post | +| `PostServices/Commands/CreatePostCommand.cs` | MediatR command for create | +| `PostServices/Commands/UpdatePostCommand.cs` | MediatR command for update | +| `PostServices/Commands/DeletePostCommand.cs` | MediatR command for delete | +| `PostServices/Handlers/GetPostsHandler.cs` | Handler for list query | +| `PostServices/Handlers/GetPostByIdHandler.cs` | Handler for detail query | +| `PostServices/Handlers/CreatePostHandler.cs` | Handler for create | +| `PostServices/Handlers/UpdatePostHandler.cs` | Handler for update | +| `PostServices/Handlers/DeletePostHandler.cs` | Handler for delete | +| `Common/Result.cs` | Result pattern implementation | +| `Mapping/PostMappingProfile.cs` | AutoMapper profile | +| Similar files for Blog and Tag services | + +--- + +## 3. SampleWebApp (Presentation Layer) Analysis + +### Location +`SampleWebApp/SampleWebApp.csproj` + +### Purpose +The presentation layer contains ASP.NET MVC 5 controllers, Razor views, infrastructure components, and application configuration. + +### Key Components + +#### 3.1 Controllers + +| File | Description | Migration Impact | +|------|-------------|------------------| +| `Controllers/HomeController.cs` | Home page controller | **Medium** - Namespace changes | +| `Controllers/PostsController.cs` | Sync CRUD for posts | **High** - DI pattern change | +| `Controllers/PostsAsyncController.cs` | Async CRUD for posts | **High** - DI pattern change | +| `Controllers/TagsController.cs` | Sync CRUD for tags | **High** - DI pattern change | +| `Controllers/TagsAsyncController.cs` | Async CRUD for tags | **High** - DI pattern change | +| `Controllers/BlogsController.cs` | CRUD for blogs | **High** - DI pattern change | + +**Current Controller Pattern (MVC 5 with DiModelBinder):** +```csharp +public class PostsController : Controller +{ + // Services injected as action parameters via DiModelBinder + public ActionResult Index(int? id, IListService service) + { + var query = service.GetAll(); + return View(query.ToList()); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public ActionResult Edit(DetailPostDto dto, IUpdateService service) + { + if (!ModelState.IsValid) + return View(service.ResetDto(dto)); + + var response = service.Update(dto); + if (response.IsValid) + { + TempData["message"] = response.SuccessMessage; + return RedirectToAction("Index"); + } + + response.CopyErrorsToModelState(ModelState, dto); + return View(dto); + } +} +``` + +**Target Controller Pattern (ASP.NET Core with Constructor DI):** +```csharp +public class PostsController : Controller +{ + private readonly IMediator _mediator; + private readonly IPostSetupService _setupService; + + public PostsController(IMediator mediator, IPostSetupService setupService) + { + _mediator = mediator; + _setupService = setupService; + } + + public async Task Index(int? id) + { + var posts = await _mediator.Send(new GetPostsQuery { BlogId = id }); + return View(posts); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Edit(DetailPostDto dto) + { + if (!ModelState.IsValid) + { + await _setupService.SetupSecondaryDataAsync(dto); + return View(dto); + } + + var result = await _mediator.Send(new UpdatePostCommand { Dto = dto }); + if (result.IsSuccess) + { + TempData["message"] = result.Message; + return RedirectToAction("Index"); + } + + result.CopyErrorsToModelState(ModelState); + return View(dto); + } +} +``` + +#### 3.2 Infrastructure + +| File | Description | Migration Impact | +|------|-------------|------------------| +| `Infrastructure/WebUiInitialise.cs` | Application startup, DI setup | **High** - Move to Program.cs | +| `Infrastructure/AutofacDi.cs` | Autofac container setup | **High** - Move to Program.cs | +| `Infrastructure/DiModelBinder.cs` | Custom model binder for DI | **High** - Delete (use constructor DI) | +| `Infrastructure/ValidationHelper.cs` | ModelState error handling | **Medium** - Namespace changes | +| `Infrastructure/Log4NetGenericLogger.cs` | Log4net logger wrapper | **Medium** - Replace with ILogger | +| `Infrastructure/TraceGenericLogger.cs` | Trace logger wrapper | **Medium** - Replace with ILogger | + +**WebUiInitialise.cs - Complete Replacement:** +This file's functionality moves to `Program.cs`: +- Host type detection → Configuration/environment variables +- Logging setup → `builder.Logging.AddXxx()` +- Service layer initialization → Startup services +- Autofac setup → `builder.Host.UseServiceProviderFactory()` + +**DiModelBinder.cs - Delete:** +The custom model binder pattern is replaced by: +1. Constructor injection (preferred) +2. `[FromServices]` attribute for action parameters + +#### 3.3 App_Start Configuration + +| File | Description | Migration Impact | +|------|-------------|------------------| +| `App_Start/RouteConfig.cs` | Route registration | **High** - Move to Program.cs | +| `App_Start/BundleConfig.cs` | Script/CSS bundling | **High** - Replace with WebOptimizer or manual | +| `App_Start/FilterConfig.cs` | Global filters | **Medium** - Move to AddControllersWithViews | + +**RouteConfig.cs → Program.cs:** +```csharp +// Current (MVC 5) +routes.MapRoute( + name: "Default", + url: "{controller}/{action}/{id}", + defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } +); + +// Target (ASP.NET Core) +app.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); +``` + +**BundleConfig.cs → WebOptimizer or manual:** +```csharp +// Current (MVC 5) +bundles.Add(new ScriptBundle("~/bundles/javascript").Include( + "~/Scripts/jquery-{version}.js", + "~/Scripts/bootstrap.js")); + +// Target Option 1: WebOptimizer +builder.Services.AddWebOptimizer(pipeline => +{ + pipeline.AddJavaScriptBundle("/js/bundle.js", + "js/jquery.js", + "js/bootstrap.js"); +}); + +// Target Option 2: Manual in _Layout.cshtml + + +``` + +#### 3.4 Global.asax + +| File | Description | Migration Impact | +|------|-------------|------------------| +| `Global.asax` | Application entry point | **High** - Delete | +| `Global.asax.cs` | Application startup code | **High** - Move to Program.cs | + +**Global.asax.cs → Program.cs:** +```csharp +// Current (MVC 5) +public class MvcApplication : System.Web.HttpApplication +{ + protected void Application_Start() + { + AreaRegistration.RegisterAllAreas(); + FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); + RouteConfig.RegisterRoutes(RouteTable.Routes); + BundleConfig.RegisterBundles(BundleTable.Bundles); + ModelBinders.Binders.DefaultBinder = new DiModelBinder(); + WebUiInitialise.InitialiseThis(this); + } +} + +// Target (ASP.NET Core Program.cs) +var builder = WebApplication.CreateBuilder(args); + +// Services +builder.Services.AddControllersWithViews(); +builder.Services.AddDbContext(options => ...); +builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); +builder.Host.ConfigureContainer(containerBuilder => +{ + containerBuilder.RegisterModule(new ServiceLayerModule()); +}); + +var app = builder.Build(); + +// Middleware +app.UseStaticFiles(); +app.UseRouting(); +app.UseAuthorization(); + +app.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); + +// Database initialization +using (var scope = app.Services.CreateScope()) +{ + var db = scope.ServiceProvider.GetRequiredService(); + db.Database.EnsureCreated(); +} + +app.Run(); +``` + +#### 3.5 Configuration Files + +| File | Description | Migration Impact | +|------|-------------|------------------| +| `Web.config` | Main configuration | **High** - Split to appsettings.json | +| `Web.Debug.config` | Debug transforms | **Medium** - Use appsettings.Development.json | +| `Web.Release.config` | Release transforms | **Medium** - Use appsettings.Production.json | +| `Views/Web.config` | View configuration | **Medium** - Use _ViewImports.cshtml | + +**Web.config Connection String → appsettings.json:** +```xml + + + + +``` + +```json +// Target (appsettings.json) +{ + "ConnectionStrings": { + "SampleWebAppDb": "Data Source=(localdb)\\mssqllocaldb;Initial Catalog=SampleWebAppDb;..." + }, + "Logging": { + "LogLevel": { + "Default": "Information" + } + } +} +``` + +#### 3.6 Views + +| File/Folder | Description | Migration Impact | +|-------------|-------------|------------------| +| `Views/_ViewStart.cshtml` | View startup | **Low** - Compatible | +| `Views/Shared/_Layout.cshtml` | Main layout | **Medium** - Update bundling references | +| `Views/Shared/Error.cshtml` | Error page | **Low** - Compatible | +| `Views/Shared/EditorTemplates/*.cshtml` | Editor templates | **Low** - Compatible | +| `Views/Posts/*.cshtml` | Post views | **Low** - Compatible | +| `Views/PostsAsync/*.cshtml` | Async post views | **Low** - Compatible | +| `Views/Tags/*.cshtml` | Tag views | **Low** - Compatible | +| `Views/TagsAsync/*.cshtml` | Async tag views | **Low** - Compatible | +| `Views/Blogs/*.cshtml` | Blog views | **Low** - Compatible | +| `Views/Home/*.cshtml` | Home views | **Low** - Compatible | + +**_Layout.cshtml Changes:** +```html + +@Styles.Render("~/Content/css") +@Scripts.Render("~/bundles/javascript") + + + + + +``` + +**New File: _ViewImports.cshtml:** +```cshtml +@using SampleWebApp +@using SampleWebApp.Models +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +``` + +#### 3.7 Project File + +| File | Description | Migration Impact | +|------|-------------|------------------| +| `SampleWebApp.csproj` | Project file (old format) | **High** - Convert to SDK-style | +| `packages.config` | NuGet packages | **High** - Convert to PackageReference | + +### SampleWebApp Files Summary + +| File Path | Change Type | Priority | +|-----------|-------------|----------| +| `SampleWebApp.csproj` | Replace with SDK-style | High | +| `packages.config` | Delete (use PackageReference) | High | +| `Global.asax` | Delete | High | +| `Global.asax.cs` | Delete (move to Program.cs) | High | +| `Web.config` | Replace with appsettings.json | High | +| `Infrastructure/WebUiInitialise.cs` | Delete (move to Program.cs) | High | +| `Infrastructure/AutofacDi.cs` | Delete (move to Program.cs) | High | +| `Infrastructure/DiModelBinder.cs` | Delete | High | +| `App_Start/RouteConfig.cs` | Delete (move to Program.cs) | High | +| `App_Start/BundleConfig.cs` | Delete or replace | High | +| `App_Start/FilterConfig.cs` | Delete (move to Program.cs) | High | +| `Controllers/PostsController.cs` | Major refactor | High | +| `Controllers/PostsAsyncController.cs` | Major refactor | High | +| `Controllers/TagsController.cs` | Major refactor | High | +| `Controllers/TagsAsyncController.cs` | Major refactor | High | +| `Controllers/BlogsController.cs` | Major refactor | High | +| `Controllers/HomeController.cs` | Namespace changes | Medium | +| `Infrastructure/ValidationHelper.cs` | Namespace changes | Medium | +| `Infrastructure/Log4NetGenericLogger.cs` | Replace with ILogger | Medium | +| `Infrastructure/TraceGenericLogger.cs` | Replace with ILogger | Medium | +| `Views/Shared/_Layout.cshtml` | Update bundling | Medium | +| `Views/Web.config` | Delete | Medium | +| All other Views | Minor or no changes | Low | + +### New Files to Create + +| File Path | Description | +|-----------|-------------| +| `Program.cs` | Application entry point | +| `appsettings.json` | Configuration | +| `appsettings.Development.json` | Development configuration | +| `Views/_ViewImports.cshtml` | Tag helper imports | + +--- + +## 4. Dependencies Between Layers + +### Current Dependency Graph + +``` +SampleWebApp + ├── References: ServiceLayer + ├── References: DataLayer + ├── Uses: GenericServices (via ServiceLayer) + └── Uses: Autofac, Autofac.Mvc5 + +ServiceLayer + ├── References: DataLayer + ├── Uses: GenericServices + ├── Uses: GenericLibsBase + └── Uses: Autofac, AutoMapper + +DataLayer + ├── Uses: EntityFramework 6.1.3 + ├── Uses: GenericServices + ├── Uses: GenericLibsBase + └── Uses: Autofac, AutoMapper +``` + +### Target Dependency Graph + +``` +SampleWebApp + ├── References: ServiceLayer + ├── References: DataLayer (for DbContext registration) + ├── Uses: MediatR + └── Uses: Autofac.Extensions.DependencyInjection + +ServiceLayer + ├── References: DataLayer + ├── Uses: MediatR + ├── Uses: AutoMapper + └── Uses: Autofac + +DataLayer + ├── Uses: Microsoft.EntityFrameworkCore + ├── Uses: Microsoft.EntityFrameworkCore.SqlServer + └── Uses: AutoMapper +``` + +--- + +## 5. Recommended Migration Order + +### Phase 1: Project Infrastructure (Week 1) +**Objective**: Convert project files and establish .NET Core foundation + +1. Convert all `.csproj` files to SDK-style format +2. Remove `packages.config` files +3. Add PackageReference for .NET Core packages +4. Create `Program.cs` skeleton +5. Create `appsettings.json` + +**Risk Level**: Low +**Justification**: No functional changes; establishes foundation for subsequent phases + +### Phase 2: DataLayer Migration (Week 2) +**Objective**: Migrate Entity Framework 6 to EF Core + +1. Update `SampleWebAppDb.cs` for EF Core +2. Delete `EfConfiguration.cs` +3. Update `DataLayerInitialise.cs` +4. Update `DataLayerModule.cs` +5. Test database connectivity + +**Risk Level**: Medium +**Justification**: DataLayer has no upstream dependencies; can be tested in isolation + +### Phase 3: ServiceLayer Migration (Weeks 3-4) +**Objective**: Replace GenericServices with MediatR + custom services + +1. Create MediatR infrastructure (queries, commands, handlers) +2. Convert DTOs to plain classes +3. Create AutoMapper profiles +4. Implement result pattern +5. Update `ServiceLayerModule.cs` +6. Test all service operations + +**Risk Level**: High +**Justification**: Most complex phase; requires complete replacement of GenericServices + +### Phase 4: Presentation Layer Migration (Week 5) +**Objective**: Migrate ASP.NET MVC 5 to ASP.NET Core MVC + +1. Complete `Program.cs` with all middleware +2. Migrate controllers to constructor injection +3. Update views for ASP.NET Core +4. Delete obsolete files (Global.asax, Web.config, etc.) +5. Test all endpoints + +**Risk Level**: Medium +**Justification**: Depends on ServiceLayer completion; mostly mechanical changes + +### Phase 5: Testing and Cleanup (Week 6) +**Objective**: Validate migration and remove legacy code + +1. Run all existing tests +2. Create integration tests +3. Remove unused packages +4. Performance testing +5. Documentation updates + +**Risk Level**: Low +**Justification**: Validation phase; no new functionality + +--- + +## 6. Migration Checklist + +### Pre-Migration +- [ ] Backup existing codebase +- [ ] Document current functionality +- [ ] Set up .NET 6/8 SDK +- [ ] Create migration branch + +### DataLayer +- [ ] Convert DataLayer.csproj to SDK-style +- [ ] Update SampleWebAppDb.cs for EF Core +- [ ] Delete EfConfiguration.cs +- [ ] Update DataLayerInitialise.cs +- [ ] Update DataLayerModule.cs +- [ ] Verify entity classes compile +- [ ] Test database connection + +### ServiceLayer +- [ ] Convert ServiceLayer.csproj to SDK-style +- [ ] Install MediatR packages +- [ ] Create query/command classes +- [ ] Create handler classes +- [ ] Convert DTOs to plain classes +- [ ] Create AutoMapper profiles +- [ ] Implement Result pattern +- [ ] Update ServiceLayerModule.cs +- [ ] Test all CRUD operations + +### SampleWebApp +- [ ] Convert SampleWebApp.csproj to SDK-style +- [ ] Create Program.cs +- [ ] Create appsettings.json +- [ ] Migrate controllers +- [ ] Update views +- [ ] Create _ViewImports.cshtml +- [ ] Delete obsolete files +- [ ] Test all endpoints + +### Post-Migration +- [ ] Run all tests +- [ ] Performance validation +- [ ] Security review +- [ ] Documentation update +- [ ] Deployment verification diff --git a/docs/migration/MIGRATION_BOUNDARY_DIAGRAM.md b/docs/migration/MIGRATION_BOUNDARY_DIAGRAM.md new file mode 100644 index 0000000..eb90276 --- /dev/null +++ b/docs/migration/MIGRATION_BOUNDARY_DIAGRAM.md @@ -0,0 +1,463 @@ +# Migration Boundary Diagram + +## Application Architecture Overview + +The following diagram illustrates the three-layer architecture of the SampleMvcWebApp application, showing dependencies between layers, external dependencies for each layer, and the proposed migration order. + +```mermaid +flowchart TB + subgraph Legend["Legend"] + direction LR + L1[" "] --> L2["Migration Order"] + style L1 fill:#e8f5e9 + style L2 fill:#fff + end + + subgraph Phase4["Phase 4: Presentation Layer Migration (Week 5)"] + direction TB + subgraph SampleWebApp["SampleWebApp (Presentation Layer)"] + direction TB + Controllers["Controllers
- HomeController
- PostsController
- PostsAsyncController
- TagsController
- TagsAsyncController
- BlogsController"] + Views["Views
- Razor Views (.cshtml)
- _Layout.cshtml
- EditorTemplates"] + Infrastructure["Infrastructure
- WebUiInitialise
- AutofacDi
- DiModelBinder
- ValidationHelper"] + AppStart["App_Start
- RouteConfig
- BundleConfig
- FilterConfig"] + Config["Configuration
- Web.config
- Global.asax"] + end + end + + subgraph Phase3["Phase 3: Service Layer Migration (Weeks 3-4)"] + direction TB + subgraph ServiceLayer["ServiceLayer (Business Logic)"] + direction TB + DTOs["DTOs
- DetailPostDto
- SimplePostDto
- BlogListDto
- TagListDto"] + UiClasses["UI Classes
- DropDownListType
- MultiSelectListType"] + ServiceStartup["Startup
- ServiceLayerModule
- ServiceLayerInitialise"] + end + end + + subgraph Phase2["Phase 2: Data Layer Migration (Week 2)"] + direction TB + subgraph DataLayer["DataLayer (Data Access)"] + direction TB + Entities["Entities
- Blog
- Post
- Tag
- TrackUpdate"] + DbContext["DbContext
- SampleWebAppDb"] + DataConfig["Configuration
- EfConfiguration"] + DataStartup["Startup
- DataLayerModule
- DataLayerInitialise"] + end + end + + subgraph ExternalDeps["External Dependencies"] + direction TB + subgraph WebDeps["SampleWebApp Dependencies"] + ASPNETMVC["ASP.NET MVC 5.2.3"] + AutofacMvc["Autofac.Mvc5 3.3.1"] + Identity["ASP.NET Identity 2.1.0"] + SignalR["SignalR 2.0.3"] + OWIN["OWIN 3.0.0"] + Log4Net["log4net 2.0.3"] + end + + subgraph ServiceDeps["ServiceLayer Dependencies"] + GenericServices["GenericServices 1.0.9"] + GenericLibsBase["GenericLibsBase 1.0.1"] + AutoMapper["AutoMapper 4.2.1"] + Autofac["Autofac 3.5.0"] + end + + subgraph DataDeps["DataLayer Dependencies"] + EF6["Entity Framework 6.1.3"] + DelegateDecompiler["DelegateDecompiler 0.18.0"] + end + end + + %% Layer Dependencies + SampleWebApp --> ServiceLayer + SampleWebApp --> DataLayer + ServiceLayer --> DataLayer + + %% External Dependencies + Controllers --> ASPNETMVC + Infrastructure --> AutofacMvc + Infrastructure --> OWIN + Infrastructure --> Log4Net + Config --> Identity + Config --> SignalR + + DTOs --> GenericServices + DTOs --> GenericLibsBase + DTOs --> AutoMapper + ServiceStartup --> Autofac + + DbContext --> EF6 + DataConfig --> EF6 + Entities --> DelegateDecompiler + DataStartup --> Autofac + + %% Styling + style Phase4 fill:#ffebee,stroke:#c62828 + style Phase3 fill:#fff3e0,stroke:#ef6c00 + style Phase2 fill:#e8f5e9,stroke:#2e7d32 + style SampleWebApp fill:#ffcdd2 + style ServiceLayer fill:#ffe0b2 + style DataLayer fill:#c8e6c9 + style ExternalDeps fill:#e3f2fd,stroke:#1565c0 +``` + +## Detailed Layer Dependency Diagram + +```mermaid +flowchart LR + subgraph Presentation["Presentation Layer"] + direction TB + PC[PostsController] + PAC[PostsAsyncController] + TC[TagsController] + TAC[TagsAsyncController] + BC[BlogsController] + HC[HomeController] + end + + subgraph Services["Service Layer"] + direction TB + ILS[IListService] + IDS[IDetailService] + IDSA[IDetailServiceAsync] + ICS[ICreateService] + ICSA[ICreateServiceAsync] + IUS[IUpdateService] + IUSA[IUpdateServiceAsync] + IDELS[IDeleteService] + IDELSA[IDeleteServiceAsync] + end + + subgraph Data["Data Layer"] + direction TB + SDB[(SampleWebAppDb)] + Blog[Blog Entity] + Post[Post Entity] + Tag[Tag Entity] + end + + %% Controller to Service dependencies + PC --> ILS + PC --> IDS + PC --> ICS + PC --> IUS + PC --> IDELS + + PAC --> ILS + PAC --> IDSA + PAC --> ICSA + PAC --> IUSA + PAC --> IDELSA + + TC --> ILS + TC --> IDS + TC --> ICS + TC --> IUS + TC --> IDELS + + TAC --> ILS + TAC --> IDSA + TAC --> ICSA + TAC --> IUSA + TAC --> IDELSA + + BC --> ILS + BC --> ICS + BC --> IUS + BC --> IDELS + + %% Service to Data dependencies + ILS --> SDB + IDS --> SDB + IDSA --> SDB + ICS --> SDB + ICSA --> SDB + IUS --> SDB + IUSA --> SDB + IDELS --> SDB + IDELSA --> SDB + + %% DbContext to Entity dependencies + SDB --> Blog + SDB --> Post + SDB --> Tag + + %% Entity relationships + Blog -.->|1:N| Post + Post -.->|N:M| Tag + + style Presentation fill:#ffcdd2 + style Services fill:#ffe0b2 + style Data fill:#c8e6c9 +``` + +## Migration Flow Diagram + +```mermaid +flowchart TD + subgraph Phase1["Phase 1: Infrastructure (Week 1)"] + P1A[Convert .csproj to SDK-style] + P1B[Remove packages.config] + P1C[Create Program.cs skeleton] + P1D[Create appsettings.json] + P1A --> P1B --> P1C --> P1D + end + + subgraph Phase2["Phase 2: DataLayer (Week 2)"] + P2A[Update SampleWebAppDb for EF Core] + P2B[Delete EfConfiguration.cs] + P2C[Update DataLayerInitialise.cs] + P2D[Update DataLayerModule.cs] + P2E[Test database connectivity] + P2A --> P2B --> P2C --> P2D --> P2E + end + + subgraph Phase3["Phase 3: ServiceLayer (Weeks 3-4)"] + P3A[Create MediatR queries/commands] + P3B[Create handlers] + P3C[Convert DTOs to plain classes] + P3D[Create AutoMapper profiles] + P3E[Implement Result pattern] + P3F[Update ServiceLayerModule.cs] + P3A --> P3B --> P3C --> P3D --> P3E --> P3F + end + + subgraph Phase4["Phase 4: Presentation (Week 5)"] + P4A[Complete Program.cs] + P4B[Migrate controllers to constructor DI] + P4C[Update views] + P4D[Delete obsolete files] + P4E[Test all endpoints] + P4A --> P4B --> P4C --> P4D --> P4E + end + + subgraph Phase5["Phase 5: Cleanup (Week 6)"] + P5A[Run all tests] + P5B[Remove unused packages] + P5C[Performance testing] + P5D[Documentation updates] + P5A --> P5B --> P5C --> P5D + end + + Phase1 --> Phase2 --> Phase3 --> Phase4 --> Phase5 + + style Phase1 fill:#e3f2fd + style Phase2 fill:#e8f5e9 + style Phase3 fill:#fff3e0 + style Phase4 fill:#ffebee + style Phase5 fill:#f3e5f5 +``` + +## Current vs Target Architecture + +### Current Architecture (.NET Framework 4.5.1) + +```mermaid +flowchart TB + subgraph Current["Current Architecture"] + direction TB + + subgraph IIS["IIS / IIS Express"] + GlobalAsax["Global.asax
(HttpApplication)"] + end + + subgraph MVC5["ASP.NET MVC 5"] + WebConfig["Web.config"] + RouteConfig["RouteConfig.cs"] + BundleConfig["BundleConfig.cs"] + DiModelBinder["DiModelBinder
(Action Parameter DI)"] + end + + subgraph DI["Autofac 3.5"] + AutofacResolver["AutofacDependencyResolver"] + ServiceModule["ServiceLayerModule"] + DataModule["DataLayerModule"] + end + + subgraph GenSvc["GenericServices 1.0.9"] + IListSvc["IListService"] + ICrudSvc["CRUD Services"] + EfGenericDto["EfGenericDto Base"] + end + + subgraph EF6["Entity Framework 6.1.3"] + DbConfig["DbConfiguration"] + DbCtx["DbContext"] + LocalDB["LocalDB"] + end + + GlobalAsax --> MVC5 + MVC5 --> DI + DI --> GenSvc + GenSvc --> EF6 + end + + style Current fill:#ffebee + style IIS fill:#ffcdd2 + style MVC5 fill:#ffe0b2 + style DI fill:#fff9c4 + style GenSvc fill:#c8e6c9 + style EF6 fill:#b3e5fc +``` + +### Target Architecture (.NET Core 6+) + +```mermaid +flowchart TB + subgraph Target["Target Architecture"] + direction TB + + subgraph Kestrel["Kestrel Web Server"] + ProgramCs["Program.cs
(Minimal Hosting)"] + end + + subgraph AspNetCore["ASP.NET Core MVC"] + AppSettings["appsettings.json"] + EndpointRouting["Endpoint Routing"] + ConstructorDI["Constructor DI
(Built-in + Autofac)"] + end + + subgraph DI["Autofac 8.x + Built-in DI"] + ServiceProviderFactory["AutofacServiceProviderFactory"] + ServiceModule["ServiceLayerModule"] + DataModule["DataLayerModule"] + end + + subgraph MediatR["MediatR + Custom Services"] + Queries["Query Handlers"] + Commands["Command Handlers"] + PlainDTOs["Plain DTOs + AutoMapper"] + end + + subgraph EFCore["Entity Framework Core 8.x"] + DbCtxOptions["DbContextOptions"] + DbCtx["DbContext"] + SqlServer["SQL Server / LocalDB"] + end + + ProgramCs --> AspNetCore + AspNetCore --> DI + DI --> MediatR + MediatR --> EFCore + end + + style Target fill:#e8f5e9 + style Kestrel fill:#c8e6c9 + style AspNetCore fill:#a5d6a7 + style DI fill:#fff9c4 + style MediatR fill:#b3e5fc + style EFCore fill:#ce93d8 +``` + +## Entity Relationship Diagram + +```mermaid +erDiagram + Blog ||--o{ Post : "has many" + Post }o--o{ Tag : "many to many" + + Blog { + int BlogId PK + string Name + string EmailAddress + } + + Post { + int PostId PK + string Title + string Content + int BlogId FK + datetime LastUpdated + } + + Tag { + int TagId PK + string Slug + string Name + } +``` + +## File Migration Status Matrix + +```mermaid +flowchart LR + subgraph DataLayerFiles["DataLayer Files"] + direction TB + DL1["DataLayer.csproj
🔴 Replace"] + DL2["SampleWebAppDb.cs
🔴 Major Refactor"] + DL3["EfConfiguration.cs
🔴 Delete"] + DL4["DataLayerInitialise.cs
🔴 Major Refactor"] + DL5["DataLayerModule.cs
🟡 Update"] + DL6["Blog.cs
🟢 Minor"] + DL7["Post.cs
🟢 Minor"] + DL8["Tag.cs
🟢 Minor"] + end + + subgraph ServiceLayerFiles["ServiceLayer Files"] + direction TB + SL1["ServiceLayer.csproj
🔴 Replace"] + SL2["DetailPostDto.cs
🔴 Major Refactor"] + SL3["SimplePostDto.cs
🔴 Major Refactor"] + SL4["BlogListDto.cs
🔴 Major Refactor"] + SL5["TagListDto.cs
🔴 Major Refactor"] + SL6["ServiceLayerModule.cs
🔴 Major Refactor"] + SL7["DropDownListType.cs
🟢 No Change"] + SL8["MultiSelectListType.cs
🟢 No Change"] + end + + subgraph WebAppFiles["SampleWebApp Files"] + direction TB + WA1["SampleWebApp.csproj
🔴 Replace"] + WA2["Global.asax
🔴 Delete"] + WA3["Web.config
🔴 Replace"] + WA4["PostsController.cs
🔴 Major Refactor"] + WA5["WebUiInitialise.cs
🔴 Delete"] + WA6["DiModelBinder.cs
🔴 Delete"] + WA7["_Layout.cshtml
🟡 Update"] + WA8["Other Views
🟢 Minor"] + end + + style DL1 fill:#ffcdd2 + style DL2 fill:#ffcdd2 + style DL3 fill:#ffcdd2 + style DL4 fill:#ffcdd2 + style DL5 fill:#fff9c4 + style DL6 fill:#c8e6c9 + style DL7 fill:#c8e6c9 + style DL8 fill:#c8e6c9 + + style SL1 fill:#ffcdd2 + style SL2 fill:#ffcdd2 + style SL3 fill:#ffcdd2 + style SL4 fill:#ffcdd2 + style SL5 fill:#ffcdd2 + style SL6 fill:#ffcdd2 + style SL7 fill:#c8e6c9 + style SL8 fill:#c8e6c9 + + style WA1 fill:#ffcdd2 + style WA2 fill:#ffcdd2 + style WA3 fill:#ffcdd2 + style WA4 fill:#ffcdd2 + style WA5 fill:#ffcdd2 + style WA6 fill:#ffcdd2 + style WA7 fill:#fff9c4 + style WA8 fill:#c8e6c9 +``` + +### Legend +- 🔴 **Red**: High priority - Major changes or deletion required +- 🟡 **Yellow**: Medium priority - Updates needed +- 🟢 **Green**: Low priority - Minor or no changes + +## Summary + +The migration follows a bottom-up approach, starting with the DataLayer (lowest risk, no upstream dependencies), then ServiceLayer (highest complexity due to GenericServices replacement), and finally the Presentation Layer (depends on both lower layers). + +This phased approach allows for: +1. Incremental testing at each phase +2. Rollback capability if issues arise +3. Clear milestones for progress tracking +4. Isolation of high-risk changes (GenericServices replacement)