diff --git a/Migration-Examples/AppConfiguration.NetCore.cs b/Migration-Examples/AppConfiguration.NetCore.cs
new file mode 100644
index 000000000..a5cc78322
--- /dev/null
+++ b/Migration-Examples/AppConfiguration.NetCore.cs
@@ -0,0 +1,391 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Globalization;
+using System.IO;
+using System.Security.Principal;
+using System.Threading;
+
+namespace Vaiona.Utils.Cfg
+{
+ public class Constants
+ {
+ public static readonly string AnonymousUser = @"Anonymous";
+ public static readonly string EveryoneRole = "Everyone";
+ }
+
+ ///
+ /// .NET Core version of AppConfiguration using dependency injection instead of static access.
+ /// Replaces System.Web dependencies with ASP.NET Core equivalents.
+ ///
+ public class AppConfiguration
+ {
+ private readonly IConfiguration _configuration;
+ private readonly IWebHostEnvironment _environment;
+ private readonly IHttpContextAccessor _httpContextAccessor;
+ private readonly IServiceProvider _serviceProvider;
+
+ public AppConfiguration(
+ IConfiguration configuration,
+ IWebHostEnvironment environment,
+ IHttpContextAccessor httpContextAccessor,
+ IServiceProvider serviceProvider)
+ {
+ _configuration = configuration;
+ _environment = environment;
+ _httpContextAccessor = httpContextAccessor;
+ _serviceProvider = serviceProvider;
+ }
+
+ public bool IsWebContext => _httpContextAccessor.HttpContext != null;
+
+ public bool IsPostBack(HttpRequest request)
+ {
+ if (request.Headers.Referer.Count == 0) return false;
+ if (!Uri.TryCreate(request.Headers.Referer.ToString(), UriKind.Absolute, out Uri refererUri)) return false;
+
+ bool isPost = "POST".Equals(request.Method, StringComparison.CurrentCultureIgnoreCase);
+ bool isSameUrl = request.Path.Equals(refererUri.AbsolutePath, StringComparison.CurrentCultureIgnoreCase);
+
+ return isPost && isSameUrl;
+ }
+
+ public string DefaultApplicationConnection => _configuration.GetConnectionString("ApplicationServices");
+
+ public string DefaultCulture => _configuration["DefaultCulture"] ?? "en-US";
+
+ public string DatabaseMappingFile => _configuration["DatabaseMappingFile"] ?? string.Empty;
+
+ public string DatabaseDialect => _configuration["DatabaseDialect"] ?? "DB2Dialect";
+
+ public bool AutoCommitTransactions => _configuration.GetValue("AutoCommitTransactions", false);
+
+ public string IoCProviderTypeInfo => _configuration["IoCProviderTypeInfo"] ?? string.Empty;
+
+ public string AppRoot
+ {
+ get
+ {
+ // Try configuration first, then fall back to content root
+ string path = _configuration["ApplicationRoot"];
+ return string.IsNullOrWhiteSpace(path) ? _environment.ContentRootPath : path;
+ }
+ }
+
+ public string AreasPath
+ {
+ get
+ {
+ // Use WebRootPath instead of HostingEnvironment.MapPath
+ string path = Path.Combine(_environment.WebRootPath ?? _environment.ContentRootPath, "Areas");
+ if (Directory.Exists(path))
+ {
+ return path;
+ }
+ return Path.Combine(AppRoot, "Areas");
+ }
+ }
+
+ ///
+ /// DataPath shows the root folder containing business data.
+ /// Uses IConfiguration instead of ConfigurationManager.AppSettings
+ ///
+ public string DataPath
+ {
+ get
+ {
+ string path = _configuration["DataPath"];
+ return string.IsNullOrWhiteSpace(path) ? Path.Combine(AppRoot, "Data") : path;
+ }
+ }
+
+ private string _workspaceRootPath = string.Empty;
+
+ public string WorkspaceRootPath
+ {
+ get
+ {
+ if (!string.IsNullOrWhiteSpace(_workspaceRootPath))
+ return _workspaceRootPath;
+
+ string path = _configuration["WorkspacePath"] ?? string.Empty;
+ int level = 0;
+
+ if (string.IsNullOrWhiteSpace(path))
+ level = 0;
+ else if (path.Contains(@"..\"))
+ {
+ level = path.Split(@"\".ToCharArray()).Length - 1;
+ }
+ else
+ {
+ _workspaceRootPath = path;
+ return _workspaceRootPath;
+ }
+
+ // Find directory without Web.config (use appsettings.json as indicator for web root)
+ DirectoryInfo di = new DirectoryInfo(AppRoot);
+ while (di.GetFiles("appsettings.json").Length >= 1)
+ di = di.Parent;
+
+ for (int i = 1; i < level; i++)
+ {
+ di = di.Parent;
+ }
+
+ _workspaceRootPath = Path.Combine(di.FullName, "Workspace");
+ return _workspaceRootPath;
+ }
+ }
+
+ public string WorkspaceComponentRoot => Path.Combine(WorkspaceRootPath, "Components");
+ public string WorkspaceModulesRoot => Path.Combine(WorkspaceRootPath, "Modules");
+ public string WorkspaceGeneralRoot => Path.Combine(WorkspaceRootPath, "General");
+ public string WorkspaceTenantsRoot => Path.Combine(WorkspaceRootPath, "Tenants");
+
+ public string GetModuleWorkspacePath(string moduleName) => Path.Combine(WorkspaceModulesRoot, moduleName);
+ public string GetComponentWorkspacePath(string componentName) => Path.Combine(WorkspaceComponentRoot, componentName);
+
+ public bool UseSchemaInDatabaseGeneration => _configuration.GetValue("UseSchemaInDatabaseGeneration", false);
+ public bool CreateDatabase => _configuration.GetValue("CreateDatabase", false);
+ public bool ShowQueries => _configuration.GetValue("ShowQueries", false);
+
+ private bool? _cacheQueryResults = null;
+ public bool CacheQueryResults
+ {
+ get
+ {
+ if (_cacheQueryResults.HasValue)
+ return _cacheQueryResults.Value;
+
+ _cacheQueryResults = _configuration.GetValue("CacheQueryResults", false);
+ return _cacheQueryResults.Value;
+ }
+ }
+
+ private string _themesPath;
+ public string ThemesPath
+ {
+ get
+ {
+ if (!string.IsNullOrEmpty(_themesPath))
+ return _themesPath;
+
+ _themesPath = _configuration["ThemesPath"] ?? "~/Themes";
+ return _themesPath;
+ }
+ }
+
+ private string _defaultThemeName;
+ public string DefaultThemeName
+ {
+ get
+ {
+ if (!string.IsNullOrEmpty(_defaultThemeName))
+ return _defaultThemeName;
+
+ _defaultThemeName = _configuration["DefaultThemeName"] ?? "Default";
+ return _defaultThemeName;
+ }
+ }
+
+ private string _activeLayoutName;
+ public string ActiveLayoutName
+ {
+ get
+ {
+ if (!string.IsNullOrEmpty(_activeLayoutName))
+ return _activeLayoutName;
+
+ _activeLayoutName = _configuration["ActiveLayoutName"] ?? "_Layout";
+ return _activeLayoutName;
+ }
+ }
+
+ public bool ThrowErrorWhenParialContentNotFound => _configuration.GetValue("ThrowErrorWhenParialContentNotFound", false);
+
+ // Replace HttpContext.Current with IHttpContextAccessor
+ public HttpContext HttpContext => _httpContextAccessor.HttpContext;
+
+ public Thread CurrentThread => Thread.CurrentThread;
+ public CultureInfo UICulture => Thread.CurrentThread.CurrentUICulture;
+ public CultureInfo Culture => Thread.CurrentThread.CurrentCulture;
+ public DateTime UTCDateTime => DateTime.UtcNow;
+ public DateTime DateTime => System.DateTime.Now;
+
+ public byte[] GetUTCAsBytes() => System.Text.Encoding.UTF8.GetBytes(UTCDateTime.ToBinary().ToString());
+
+ public Uri CurrentRequestURL
+ {
+ get
+ {
+ try
+ {
+ var request = _httpContextAccessor.HttpContext?.Request;
+ if (request != null)
+ {
+ return new Uri($"{request.Scheme}://{request.Host}{request.Path}{request.QueryString}");
+ }
+ return new Uri("http://NotFound.htm");
+ }
+ catch
+ {
+ return new Uri("http://NotFound.htm");
+ }
+ }
+ }
+
+ public IPrincipal User
+ {
+ get
+ {
+ var httpContext = _httpContextAccessor.HttpContext;
+ if (httpContext?.User?.Identity == null || !httpContext.User.Identity.IsAuthenticated)
+ {
+ return CreateUser(Constants.AnonymousUser, Constants.EveryoneRole);
+ }
+ return httpContext.User;
+ }
+ }
+
+ public bool TryGetCurrentUser(ref string userName)
+ {
+ try
+ {
+ var httpContext = _httpContextAccessor.HttpContext;
+ userName = httpContext?.User?.Identity?.Name ?? string.Empty;
+ return httpContext?.User?.Identity?.IsAuthenticated ?? false;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ internal static IPrincipal CreateUser(string userName, string roleName)
+ {
+ // Simplified user creation - in real implementation, integrate with ASP.NET Core Identity
+ return new GenericPrincipal(new GenericIdentity(userName), new[] { roleName });
+ }
+
+ // Logging properties with caching
+ private bool? _isLoggingEnable = null;
+ public bool IsLoggingEnable
+ {
+ get
+ {
+ if (_isLoggingEnable.HasValue)
+ return _isLoggingEnable.Value;
+
+ _isLoggingEnable = _configuration.GetValue("IsLoggingEnable", false);
+ return _isLoggingEnable.Value;
+ }
+ }
+
+ private bool? _isPerformanceLoggingEnable = null;
+ public bool IsPerformanceLoggingEnable
+ {
+ get
+ {
+ if (_isPerformanceLoggingEnable.HasValue)
+ return _isPerformanceLoggingEnable.Value;
+
+ _isPerformanceLoggingEnable = _configuration.GetValue("IsPerformanceLoggingEnable", false);
+ return _isPerformanceLoggingEnable.Value;
+ }
+ }
+
+ private bool? _isDiagnosticLoggingEnable = null;
+ public bool IsDiagnosticLoggingEnable
+ {
+ get
+ {
+ if (_isDiagnosticLoggingEnable.HasValue)
+ return _isDiagnosticLoggingEnable.Value;
+
+ _isDiagnosticLoggingEnable = _configuration.GetValue("IsDiagnosticLoggingEnable", false);
+ return _isDiagnosticLoggingEnable.Value;
+ }
+ }
+
+ private bool? _isCallLoggingEnable = null;
+ public bool IsCallLoggingEnable
+ {
+ get
+ {
+ if (_isCallLoggingEnable.HasValue)
+ return _isCallLoggingEnable.Value;
+
+ _isCallLoggingEnable = _configuration.GetValue("IsCallLoggingEnable", false);
+ return _isCallLoggingEnable.Value;
+ }
+ }
+
+ private bool? _isExceptionLoggingEnable = null;
+ public bool IsExceptionLoggingEnable
+ {
+ get
+ {
+ if (_isExceptionLoggingEnable.HasValue)
+ return _isExceptionLoggingEnable.Value;
+
+ _isExceptionLoggingEnable = _configuration.GetValue("IsExceptionLoggingEnable", false);
+ return _isExceptionLoggingEnable.Value;
+ }
+ }
+
+ private bool? _isDataLoggingEnable = null;
+ public bool IsDataLoggingEnable
+ {
+ get
+ {
+ if (_isDataLoggingEnable.HasValue)
+ return _isDataLoggingEnable.Value;
+
+ _isDataLoggingEnable = _configuration.GetValue("IsDataLoggingEnable", false);
+ return _isDataLoggingEnable.Value;
+ }
+ }
+
+ private string _tenantId = "";
+ public string TenantId
+ {
+ get
+ {
+ if (!string.IsNullOrWhiteSpace(_tenantId))
+ return _tenantId;
+
+ _tenantId = _configuration["TenantId"] ?? string.Empty;
+ return _tenantId;
+ }
+ }
+
+ private int? _conversationIsolationLevel = null;
+ public int ConversationIsolationLevel
+ {
+ get
+ {
+ if (_conversationIsolationLevel.HasValue)
+ return _conversationIsolationLevel.Value;
+
+ _conversationIsolationLevel = _configuration.GetValue("ConversationIsolationLevel", 2);
+ return _conversationIsolationLevel.Value;
+ }
+ }
+ }
+
+ ///
+ /// Extension methods for registering AppConfiguration in .NET Core DI container
+ ///
+ public static class AppConfigurationExtensions
+ {
+ public static IServiceCollection AddAppConfiguration(this IServiceCollection services)
+ {
+ services.AddHttpContextAccessor();
+ services.AddSingleton();
+ return services;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Migration-Examples/BExIS.Web.Shell.NetCore.csproj b/Migration-Examples/BExIS.Web.Shell.NetCore.csproj
new file mode 100644
index 000000000..4c8fdde09
--- /dev/null
+++ b/Migration-Examples/BExIS.Web.Shell.NetCore.csproj
@@ -0,0 +1,179 @@
+
+
+
+ net6.0
+ enable
+ enable
+ BExIS.Web.Shell
+ BExIS.Web.Shell
+
+
+ InProcess
+ AspNetCoreModuleV2
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+
+
+
+ full
+ true
+ DEBUG;TRACE
+ false
+
+
+
+ pdbonly
+ false
+ TRACE
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Migration-Examples/Migration-Checklist.md b/Migration-Examples/Migration-Checklist.md
new file mode 100644
index 000000000..d851fa7bd
--- /dev/null
+++ b/Migration-Examples/Migration-Checklist.md
@@ -0,0 +1,234 @@
+# BEXIS2 .NET Core Migration Checklist
+
+This document provides a comprehensive checklist for migrating BEXIS2 from .NET Framework 4.8 to .NET 6 for Linux compatibility.
+
+## Phase 1: Infrastructure Migration (Weeks 1-3)
+
+### Core Components Migration
+
+#### Week 1: Configuration and Utilities
+- [ ] **AppConfiguration.cs** - Replace with dependency injection pattern
+ - [ ] Replace `HttpContext.Current` with `IHttpContextAccessor`
+ - [ ] Replace `ConfigurationManager` with `IConfiguration`
+ - [ ] Replace `HostingEnvironment.MapPath` with `IWebHostEnvironment`
+ - [ ] Update path handling for cross-platform compatibility
+ - [ ] Add constructor injection for dependencies
+ - [ ] Test configuration loading from appsettings.json
+
+- [ ] **Project Files Conversion**
+ - [ ] Convert all 65+ projects from packages.config to PackageReference
+ - [ ] Update from .NET Framework 4.8 to .NET 6.0
+ - [ ] Replace legacy project format with SDK-style projects
+ - [ ] Update NuGet package references to .NET 6 compatible versions
+ - [ ] Remove Windows-specific build configurations
+
+#### Week 2: Session and Context Management
+- [ ] **Session Management**
+ - [ ] Replace `System.Web.SessionState` with ASP.NET Core session services
+ - [ ] Implement `ISessionManager` interface for type-safe session access
+ - [ ] Create session extension methods for backward compatibility
+ - [ ] Configure distributed session storage for scalability
+ - [ ] Test session persistence and timeout behavior
+
+- [ ] **HTTP Context Handling**
+ - [ ] Replace all static `HttpContext.Current` usage
+ - [ ] Implement `IHttpContextAccessor` injection throughout codebase
+ - [ ] Update request/response handling patterns
+ - [ ] Test cross-platform HTTP context behavior
+
+#### Week 3: Configuration System
+- [ ] **Web.config to appsettings.json Migration**
+ - [ ] Convert 18+ Web.config files to appsettings.json format
+ - [ ] Create environment-specific configuration files
+ - [ ] Implement strongly-typed configuration classes
+ - [ ] Set up configuration validation and binding
+ - [ ] Test configuration loading in different environments
+
+### Testing Infrastructure Updates
+- [ ] Update unit test projects to .NET 6
+- [ ] Replace MSTest/NUnit with xUnit if needed
+- [ ] Update test configuration and mocking frameworks
+- [ ] Create integration tests for configuration loading
+- [ ] Set up test databases for Linux environment
+
+## Phase 2: Application Framework Migration (Weeks 4-7)
+
+### Week 4: MVC Framework Migration
+- [ ] **ASP.NET MVC 5 to ASP.NET Core MVC**
+ - [ ] Replace `System.Web.Mvc` with `Microsoft.AspNetCore.Mvc`
+ - [ ] Update controller base classes and action filters
+ - [ ] Migrate custom model binders and value providers
+ - [ ] Update view engine configurations
+ - [ ] Test routing and action execution
+
+- [ ] **View Engine Updates**
+ - [ ] Replace custom view engines with view location expanders
+ - [ ] Update Razor view syntax for .NET Core
+ - [ ] Migrate HTML helpers to tag helpers where applicable
+ - [ ] Test view resolution and rendering
+
+### Week 5: Web API Migration
+- [ ] **ASP.NET Web API 2 to ASP.NET Core Web API**
+ - [ ] Replace `System.Web.Http` with `Microsoft.AspNetCore.Mvc`
+ - [ ] Update API controller base classes
+ - [ ] Migrate custom message handlers to middleware
+ - [ ] Update serialization configuration
+ - [ ] Test API endpoints and data binding
+
+### Week 6: Authentication and Authorization
+- [ ] **Security Framework Migration**
+ - [ ] Replace `System.Web.Security` with ASP.NET Core Identity
+ - [ ] Migrate custom authentication providers
+ - [ ] Update authorization policies and attributes
+ - [ ] Configure cookie authentication for compatibility
+ - [ ] Test user authentication and role management
+
+### Week 7: Dependency Injection and IoC
+- [ ] **IoC Container Migration**
+ - [ ] Integrate existing IoC configuration with ASP.NET Core DI
+ - [ ] Replace Unity/Ninject registrations with built-in DI
+ - [ ] Update service lifetimes and scoping
+ - [ ] Test dependency resolution and injection
+
+## Phase 3: Application Lifecycle and Startup (Weeks 8-9)
+
+### Week 8: Global.asax to Startup.cs Migration
+- [ ] **Application Lifecycle Events**
+ - [ ] Replace `Application_Start` with `Startup.ConfigureServices`
+ - [ ] Replace `Application_End` with hosted service shutdown
+ - [ ] Convert `Application_BeginRequest` to middleware
+ - [ ] Convert `Application_EndRequest` to middleware
+ - [ ] Convert `Application_Error` to exception handling middleware
+ - [ ] Convert `Session_Start` to session initialization middleware
+
+- [ ] **Middleware Pipeline Configuration**
+ - [ ] Set up middleware pipeline in correct order
+ - [ ] Configure CORS, authentication, and error handling
+ - [ ] Add custom middleware for BExIS-specific functionality
+ - [ ] Test middleware execution order and behavior
+
+### Week 9: Bundling and Optimization
+- [ ] **Asset Management Migration**
+ - [ ] Replace `System.Web.Optimization` with WebOptimizer
+ - [ ] Update CSS and JavaScript bundling configuration
+ - [ ] Configure static file serving for wwwroot
+ - [ ] Test asset loading and optimization
+
+## Phase 4: Module Migration (Weeks 10-12)
+
+### Week 10: Core Modules
+- [ ] **Primary Module Migration**
+ - [ ] Migrate DCM (Data Collection Module)
+ - [ ] Migrate DIM (Data Information Module)
+ - [ ] Migrate SAM (Security Administration Module)
+ - [ ] Update module routing and area configurations
+ - [ ] Test module functionality and integration
+
+### Week 11: Secondary Modules
+- [ ] **Additional Module Migration**
+ - [ ] Migrate VIM (Visualization Module)
+ - [ ] Migrate BAM (Business Administration Module)
+ - [ ] Migrate DDM (Data Discovery Module)
+ - [ ] Update inter-module communication patterns
+ - [ ] Test module isolation and data sharing
+
+### Week 12: Third-party Dependencies
+- [ ] **External Library Updates**
+ - [ ] Update Telerik UI controls for ASP.NET Core
+ - [ ] Migrate jQuery and JavaScript dependencies
+ - [ ] Update database drivers and ORM configurations
+ - [ ] Test all third-party integrations
+
+## Phase 5: Linux Deployment (Week 13)
+
+### Linux Environment Setup
+- [ ] **Docker Configuration**
+ - [ ] Create Dockerfile for .NET 6 application
+ - [ ] Configure Linux-compatible file paths
+ - [ ] Set up database connections for Linux
+ - [ ] Configure logging for containerized environment
+
+- [ ] **Production Deployment**
+ - [ ] Set up reverse proxy (nginx/Apache)
+ - [ ] Configure SSL certificates and HTTPS
+ - [ ] Set up monitoring and health checks
+ - [ ] Configure backup and maintenance procedures
+
+### Performance and Security Testing
+- [ ] **Load Testing**
+ - [ ] Test application performance under load
+ - [ ] Compare performance with .NET Framework version
+ - [ ] Optimize bottlenecks and memory usage
+ - [ ] Test session management under concurrent users
+
+- [ ] **Security Validation**
+ - [ ] Perform security scanning and penetration testing
+ - [ ] Validate authentication and authorization workflows
+ - [ ] Test CORS and API security configurations
+ - [ ] Verify data protection and privacy compliance
+
+## Critical Success Factors
+
+### Code Quality Gates
+- [ ] All existing unit tests pass
+- [ ] Integration tests validate core functionality
+- [ ] Performance benchmarks meet requirements
+- [ ] Security scans show no critical vulnerabilities
+- [ ] All configuration settings are properly migrated
+
+### Documentation Updates
+- [ ] Update deployment documentation for Linux
+- [ ] Create troubleshooting guides for common issues
+- [ ] Update development environment setup instructions
+- [ ] Document breaking changes and migration notes
+- [ ] Create rollback procedures
+
+### Training and Knowledge Transfer
+- [ ] Train development team on .NET Core differences
+- [ ] Create code review guidelines for .NET Core
+- [ ] Document new development patterns and practices
+- [ ] Establish monitoring and maintenance procedures
+
+## Risk Mitigation
+
+### High-Risk Areas Requiring Special Attention
+1. **Custom HTTP Modules** - Need complete rewrite as middleware
+2. **File System Operations** - Ensure cross-platform compatibility
+3. **Database Connections** - Test connection strings and drivers
+4. **Third-party Controls** - May require significant updates
+5. **Custom Authentication** - Complex migration to ASP.NET Core Identity
+
+### Rollback Plan
+- [ ] Maintain parallel .NET Framework deployment
+- [ ] Create automated rollback scripts
+- [ ] Document rollback procedures and timelines
+- [ ] Test rollback scenarios in staging environment
+
+### Success Metrics
+- [ ] Application starts successfully on Linux
+- [ ] All core functionality works as expected
+- [ ] Performance is equal or better than .NET Framework
+- [ ] No data loss during migration
+- [ ] User authentication and authorization work correctly
+- [ ] All modules load and function properly
+
+## Post-Migration Tasks
+
+### Monitoring and Optimization
+- [ ] Set up application performance monitoring
+- [ ] Configure logging aggregation and analysis
+- [ ] Monitor memory usage and garbage collection
+- [ ] Track user experience and error rates
+
+### Continuous Improvement
+- [ ] Plan for .NET LTS upgrade schedule
+- [ ] Optimize Docker images and deployment process
+- [ ] Implement automated testing and deployment pipelines
+- [ ] Plan for future feature development on .NET Core
+
+---
+
+**Estimated Timeline: 13 weeks**
+**Resource Requirements: 2-3 senior developers**
+**Risk Level: Medium-High (due to extensive Windows dependencies)**
+**Success Probability: High (with proper planning and testing)**
\ No newline at end of file
diff --git a/Migration-Examples/README.md b/Migration-Examples/README.md
new file mode 100644
index 000000000..dd58edc9b
--- /dev/null
+++ b/Migration-Examples/README.md
@@ -0,0 +1,49 @@
+# .NET Core Migration Examples
+
+This directory contains concrete examples showing how to migrate Windows-dependent BEXIS2 components to .NET Core for Linux compatibility.
+
+## Files Included
+
+1. **AppConfiguration.NetCore.cs** - Refactored AppConfiguration class using dependency injection and IConfiguration
+2. **Startup.cs** - Replacement for Global.asax.cs using ASP.NET Core startup patterns
+3. **BExIS.Web.Shell.NetCore.csproj** - Example project file migration from .NET Framework to .NET 6
+4. **SessionManagement.NetCore.cs** - Replacement patterns for System.Web.SessionState
+5. **appsettings.json** - Configuration replacement for Web.config
+
+## Key Migration Patterns
+
+### 1. HttpContext.Current → IHttpContextAccessor
+```csharp
+// Old (.NET Framework)
+HttpContext.Current.User
+
+// New (.NET Core)
+private readonly IHttpContextAccessor _httpContextAccessor;
+_httpContextAccessor.HttpContext.User
+```
+
+### 2. HostingEnvironment.MapPath → IWebHostEnvironment
+```csharp
+// Old (.NET Framework)
+HostingEnvironment.MapPath("~/Areas")
+
+// New (.NET Core)
+private readonly IWebHostEnvironment _environment;
+Path.Combine(_environment.WebRootPath, "Areas")
+```
+
+### 3. ConfigurationManager → IConfiguration
+```csharp
+// Old (.NET Framework)
+ConfigurationManager.AppSettings["key"]
+
+// New (.NET Core)
+private readonly IConfiguration _configuration;
+_configuration["key"]
+```
+
+### 4. Global.asax → Startup.cs
+Application lifecycle events are replaced with middleware pipeline configuration.
+
+### 5. Web.config → appsettings.json
+XML-based configuration is replaced with JSON-based configuration with strong typing support.
\ No newline at end of file
diff --git a/Migration-Examples/SessionManagement.NetCore.cs b/Migration-Examples/SessionManagement.NetCore.cs
new file mode 100644
index 000000000..b6c3bc72c
--- /dev/null
+++ b/Migration-Examples/SessionManagement.NetCore.cs
@@ -0,0 +1,548 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Vaiona.Model.MTnt;
+
+namespace BExIS.Web.Shell.Helpers
+{
+ ///
+ /// .NET Core session management that replaces System.Web.SessionState functionality.
+ /// Provides strongly-typed session access and cross-platform compatibility.
+ ///
+ public interface ISessionManager
+ {
+ // Basic session operations
+ T Get(string key);
+ void Set(string key, T value);
+ void Remove(string key);
+ void Clear();
+ bool Exists(string key);
+ IEnumerable Keys { get; }
+
+ // BExIS-specific session methods
+ void SetTenant(Tenant tenant);
+ Tenant GetTenant();
+ void SetUser(string userName, bool isAuthenticated);
+ (string UserName, bool IsAuthenticated) GetUser();
+ void ApplyCulture(string culture);
+ string GetCulture();
+
+ // Session lifecycle
+ Task InitializeAsync();
+ Task RefreshAsync();
+ bool IsNewSession { get; }
+ }
+
+ ///
+ /// Implementation of session management for ASP.NET Core.
+ /// Replaces the static session access patterns from System.Web.
+ ///
+ public class SessionManager : ISessionManager
+ {
+ private readonly IHttpContextAccessor _httpContextAccessor;
+ private readonly ILogger _logger;
+
+ public SessionManager(IHttpContextAccessor httpContextAccessor, ILogger logger)
+ {
+ _httpContextAccessor = httpContextAccessor;
+ _logger = logger;
+ }
+
+ private ISession Session => _httpContextAccessor.HttpContext?.Session;
+
+ public bool IsNewSession
+ {
+ get
+ {
+ var session = Session;
+ return session != null && !session.Keys.Any();
+ }
+ }
+
+ public IEnumerable Keys => Session?.Keys ?? new List();
+
+ ///
+ /// Gets a strongly-typed value from session.
+ /// Replaces: HttpContext.Current.Session["key"]
+ ///
+ public T Get(string key)
+ {
+ try
+ {
+ var session = Session;
+ if (session == null) return default(T);
+
+ var value = session.GetString(key);
+ if (string.IsNullOrEmpty(value))
+ return default(T);
+
+ if (typeof(T) == typeof(string))
+ return (T)(object)value;
+
+ if (typeof(T).IsValueType)
+ {
+ return (T)Convert.ChangeType(value, typeof(T));
+ }
+
+ return JsonConvert.DeserializeObject(value);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error getting session value for key: {Key}", key);
+ return default(T);
+ }
+ }
+
+ ///
+ /// Sets a strongly-typed value in session.
+ /// Replaces: HttpContext.Current.Session["key"] = value
+ ///
+ public void Set(string key, T value)
+ {
+ try
+ {
+ var session = Session;
+ if (session == null) return;
+
+ if (value == null)
+ {
+ session.Remove(key);
+ return;
+ }
+
+ string serializedValue;
+ if (typeof(T) == typeof(string))
+ {
+ serializedValue = value.ToString();
+ }
+ else if (typeof(T).IsValueType)
+ {
+ serializedValue = value.ToString();
+ }
+ else
+ {
+ serializedValue = JsonConvert.SerializeObject(value);
+ }
+
+ session.SetString(key, serializedValue);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error setting session value for key: {Key}", key);
+ }
+ }
+
+ ///
+ /// Removes a value from session.
+ /// Replaces: HttpContext.Current.Session.Remove("key")
+ ///
+ public void Remove(string key)
+ {
+ try
+ {
+ Session?.Remove(key);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error removing session value for key: {Key}", key);
+ }
+ }
+
+ ///
+ /// Clears all session values.
+ /// Replaces: HttpContext.Current.Session.Clear()
+ ///
+ public void Clear()
+ {
+ try
+ {
+ Session?.Clear();
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error clearing session");
+ }
+ }
+
+ ///
+ /// Checks if a session key exists.
+ /// Replaces: HttpContext.Current.Session["key"] != null
+ ///
+ public bool Exists(string key)
+ {
+ try
+ {
+ var session = Session;
+ return session != null && session.Keys.Contains(key);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error checking session key existence: {Key}", key);
+ return false;
+ }
+ }
+
+ #region BExIS-Specific Session Methods
+
+ ///
+ /// Sets the current tenant in session.
+ /// Replaces: Session["Tenant"] = tenant
+ ///
+ public void SetTenant(Tenant tenant)
+ {
+ const string key = "CurrentTenant";
+ Set(key, tenant);
+ _logger.LogDebug("Tenant set in session: {TenantId}", tenant?.Id);
+ }
+
+ ///
+ /// Gets the current tenant from session.
+ /// Replaces: (Tenant)Session["Tenant"]
+ ///
+ public Tenant GetTenant()
+ {
+ const string key = "CurrentTenant";
+ return Get(key);
+ }
+
+ ///
+ /// Sets user information in session.
+ /// Replaces manual session management for user state.
+ ///
+ public void SetUser(string userName, bool isAuthenticated)
+ {
+ Set("UserName", userName);
+ Set("IsAuthenticated", isAuthenticated);
+ _logger.LogDebug("User set in session: {UserName}, Authenticated: {IsAuthenticated}", userName, isAuthenticated);
+ }
+
+ ///
+ /// Gets user information from session.
+ ///
+ public (string UserName, bool IsAuthenticated) GetUser()
+ {
+ var userName = Get("UserName") ?? string.Empty;
+ var isAuthenticated = Get("IsAuthenticated");
+ return (userName, isAuthenticated);
+ }
+
+ ///
+ /// Applies culture settings to the current thread and stores in session.
+ /// Replaces: Thread.CurrentThread.CurrentCulture = culture
+ ///
+ public void ApplyCulture(string culture)
+ {
+ try
+ {
+ var cultureInfo = new System.Globalization.CultureInfo(culture);
+ System.Threading.Thread.CurrentThread.CurrentCulture = cultureInfo;
+ System.Threading.Thread.CurrentThread.CurrentUICulture = cultureInfo;
+
+ Set("Culture", culture);
+ _logger.LogDebug("Culture applied: {Culture}", culture);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error applying culture: {Culture}", culture);
+ }
+ }
+
+ ///
+ /// Gets the current culture from session.
+ ///
+ public string GetCulture()
+ {
+ return Get("Culture") ?? "en-US";
+ }
+
+ #endregion
+
+ #region Session Lifecycle
+
+ ///
+ /// Initializes a new session with default values.
+ /// Replaces: Global.asax Session_Start logic
+ ///
+ public async Task InitializeAsync()
+ {
+ try
+ {
+ if (IsNewSession)
+ {
+ _logger.LogInformation("Initializing new session");
+
+ // Set default values
+ Set("SessionStartTime", DateTime.UtcNow);
+ Set("SessionId", Guid.NewGuid().ToString());
+
+ // Apply default culture
+ ApplyCulture("en-US");
+
+ _logger.LogDebug("Session initialized successfully");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error initializing session");
+ throw;
+ }
+ }
+
+ ///
+ /// Refreshes session timeout.
+ /// Useful for keeping active sessions alive.
+ ///
+ public async Task RefreshAsync()
+ {
+ try
+ {
+ var session = Session;
+ if (session != null)
+ {
+ Set("LastActivity", DateTime.UtcNow);
+ await session.CommitAsync();
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error refreshing session");
+ }
+ }
+
+ #endregion
+ }
+
+ ///
+ /// Extension methods for ISession to provide backward compatibility
+ /// with existing BExIS session usage patterns.
+ ///
+ public static class SessionExtensions
+ {
+ ///
+ /// Extension method that replicates the old Session.ApplyCulture functionality.
+ /// Usage: context.Session.ApplyCulture("en-US")
+ ///
+ public static void ApplyCulture(this ISession session, string defaultCulture)
+ {
+ try
+ {
+ var culture = new System.Globalization.CultureInfo(defaultCulture);
+ System.Threading.Thread.CurrentThread.CurrentCulture = culture;
+ System.Threading.Thread.CurrentThread.CurrentUICulture = culture;
+
+ session.SetString("Culture", defaultCulture);
+ }
+ catch (Exception)
+ {
+ // Fallback to en-US if culture is invalid
+ var fallbackCulture = new System.Globalization.CultureInfo("en-US");
+ System.Threading.Thread.CurrentThread.CurrentCulture = fallbackCulture;
+ System.Threading.Thread.CurrentThread.CurrentUICulture = fallbackCulture;
+ session.SetString("Culture", "en-US");
+ }
+ }
+
+ ///
+ /// Extension method for setting tenant in session.
+ /// Usage: context.Session.SetTenant(tenant)
+ ///
+ public static void SetTenant(this ISession session, Tenant tenant)
+ {
+ if (tenant != null)
+ {
+ var tenantJson = JsonConvert.SerializeObject(tenant);
+ session.SetString("CurrentTenant", tenantJson);
+ session.SetString("TenantId", tenant.Id.ToString());
+ session.SetString("TenantName", tenant.Name ?? string.Empty);
+ }
+ }
+
+ ///
+ /// Extension method for getting tenant from session.
+ /// Usage: var tenant = context.Session.GetTenant()
+ ///
+ public static Tenant GetTenant(this ISession session)
+ {
+ try
+ {
+ var tenantJson = session.GetString("CurrentTenant");
+ if (!string.IsNullOrEmpty(tenantJson))
+ {
+ return JsonConvert.DeserializeObject(tenantJson);
+ }
+
+ // Fallback: try to construct from individual properties
+ var tenantIdString = session.GetString("TenantId");
+ var tenantName = session.GetString("TenantName");
+
+ if (!string.IsNullOrEmpty(tenantIdString) && long.TryParse(tenantIdString, out long tenantId))
+ {
+ return new Tenant { Id = tenantId, Name = tenantName };
+ }
+ }
+ catch (Exception)
+ {
+ // Return null if deserialization fails
+ }
+
+ return null;
+ }
+
+ ///
+ /// Generic extension method for getting strongly-typed values from session.
+ /// Usage: var value = context.Session.Get("key")
+ ///
+ public static T Get(this ISession session, string key)
+ {
+ try
+ {
+ var value = session.GetString(key);
+ if (string.IsNullOrEmpty(value))
+ return default(T);
+
+ if (typeof(T) == typeof(string))
+ return (T)(object)value;
+
+ if (typeof(T).IsValueType)
+ {
+ return (T)Convert.ChangeType(value, typeof(T));
+ }
+
+ return JsonConvert.DeserializeObject(value);
+ }
+ catch
+ {
+ return default(T);
+ }
+ }
+
+ ///
+ /// Generic extension method for setting strongly-typed values in session.
+ /// Usage: context.Session.Set("key", myObject)
+ ///
+ public static void Set(this ISession session, string key, T value)
+ {
+ try
+ {
+ if (value == null)
+ {
+ session.Remove(key);
+ return;
+ }
+
+ string serializedValue;
+ if (typeof(T) == typeof(string))
+ {
+ serializedValue = value.ToString();
+ }
+ else if (typeof(T).IsValueType)
+ {
+ serializedValue = value.ToString();
+ }
+ else
+ {
+ serializedValue = JsonConvert.SerializeObject(value);
+ }
+
+ session.SetString(key, serializedValue);
+ }
+ catch
+ {
+ // Silently fail - logging should be handled at higher level
+ }
+ }
+ }
+
+ ///
+ /// Service collection extensions for registering session management services.
+ ///
+ public static class SessionManagerServiceExtensions
+ {
+ ///
+ /// Adds session management services to the dependency injection container.
+ /// Call this in Startup.ConfigureServices.
+ ///
+ public static IServiceCollection AddBExISSessionManagement(this IServiceCollection services)
+ {
+ // Add distributed memory cache for session storage
+ services.AddDistributedMemoryCache();
+
+ // Configure session options
+ services.AddSession(options =>
+ {
+ options.IdleTimeout = TimeSpan.FromMinutes(30);
+ options.Cookie.HttpOnly = true;
+ options.Cookie.IsEssential = true;
+ options.Cookie.Name = "BExIS.SessionId";
+ options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
+ options.Cookie.SameSite = SameSiteMode.Lax;
+ });
+
+ // Register session manager
+ services.AddScoped();
+
+ return services;
+ }
+ }
+}
+
+/*
+Migration Guide: System.Web.SessionState to ASP.NET Core Sessions
+
+OLD (.NET Framework):
+===================
+1. HttpContext.Current.Session["key"] = value
+2. var value = (MyType)HttpContext.Current.Session["key"]
+3. HttpContext.Current.Session.Remove("key")
+4. HttpContext.Current.Session.Clear()
+5. HttpContext.Current.Session.Abandon()
+
+NEW (.NET Core):
+===============
+1. Using ISessionManager (Dependency Injection):
+ - _sessionManager.Set("key", value)
+ - var value = _sessionManager.Get("key")
+ - _sessionManager.Remove("key")
+ - _sessionManager.Clear()
+
+2. Using ISession directly:
+ - context.Session.Set("key", value) // extension method
+ - var value = context.Session.Get("key") // extension method
+ - context.Session.Remove("key")
+ - context.Session.Clear()
+
+3. In Controllers:
+ - HttpContext.Session.Set("key", value)
+ - var value = HttpContext.Session.Get("key")
+
+Key Differences:
+================
+1. No more static HttpContext.Current access
+2. Dependency injection replaces static access
+3. Sessions are async-aware (CommitAsync)
+4. Better type safety with generic methods
+5. JSON serialization for complex objects
+6. Configurable session providers (memory, Redis, SQL Server)
+7. Cross-platform compatible storage
+
+Configuration Required:
+======================
+1. Add services.AddSession() in ConfigureServices
+2. Add app.UseSession() in Configure pipeline
+3. Register ISessionManager in DI container
+4. Configure session options (timeout, cookie settings)
+
+Benefits:
+=========
+1. Testable (can mock ISessionManager)
+2. Type-safe with generics
+3. Better error handling
+4. Configurable storage backends
+5. Linux/Docker compatible
+6. Better security options
+*/
\ No newline at end of file
diff --git a/Migration-Examples/Startup.cs b/Migration-Examples/Startup.cs
new file mode 100644
index 000000000..c9048b1a8
--- /dev/null
+++ b/Migration-Examples/Startup.cs
@@ -0,0 +1,428 @@
+using BExIS.App.Bootstrap;
+using BExIS.UI.Helpers;
+using BExIS.Utils.Config;
+using BExIS.Web.Shell.Helpers;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+using System;
+using System.Collections.Generic;
+using Vaiona.IoC;
+using Vaiona.Model.MTnt;
+using Vaiona.MultiTenancy.Api;
+using Vaiona.Utils.Cfg;
+using Vaiona.Web.Extensions;
+
+namespace BExIS.Web.Shell
+{
+ ///
+ /// .NET Core Startup class that replaces Global.asax.cs functionality.
+ /// Handles application configuration, dependency injection, and middleware pipeline.
+ ///
+ public class Startup
+ {
+ private BExIS.App.Bootstrap.Application _app = null;
+
+ public Startup(IConfiguration configuration, IWebHostEnvironment environment)
+ {
+ Configuration = configuration;
+ Environment = environment;
+ }
+
+ public IConfiguration Configuration { get; }
+ public IWebHostEnvironment Environment { get; }
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ public void ConfigureServices(IServiceCollection services)
+ {
+ // Configure JSON serialization (replaces Global.asax JsonConvert.DefaultSettings)
+ services.Configure(options =>
+ {
+ options.SerializerSettings.Formatting = Formatting.Indented;
+ options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
+ options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
+ });
+
+ // Add framework services
+ services.AddControllersWithViews()
+ .AddNewtonsoftJson() // For compatibility with existing JSON handling
+ .AddRazorRuntimeCompilation(); // For development-time view compilation
+
+ // Add session services (replaces System.Web.SessionState)
+ services.AddDistributedMemoryCache();
+ services.AddSession(options =>
+ {
+ options.IdleTimeout = TimeSpan.FromMinutes(30);
+ options.Cookie.HttpOnly = true;
+ options.Cookie.IsEssential = true;
+ options.Cookie.Name = "BExIS.SessionId"; // Custom session cookie name
+ });
+
+ // Add AppConfiguration with dependency injection
+ services.AddAppConfiguration();
+
+ // Add HTTP context accessor for accessing HTTP context in services
+ services.AddHttpContextAccessor();
+
+ // Add custom view location expanders (replaces Global.asax CustomViewEngine)
+ services.Configure(options =>
+ {
+ options.ViewLocationExpanders.Add(new CustomViewLocationExpander());
+ });
+
+ // Add CORS services
+ services.AddCors(options =>
+ {
+ options.AddDefaultPolicy(builder =>
+ {
+ builder.AllowAnyOrigin()
+ .AllowAnyMethod()
+ .AllowAnyHeader();
+ });
+ });
+
+ // Add IoC container integration
+ ConfigureIoCContainer(services);
+
+ // Register BExIS application services
+ ConfigureBExISServices(services);
+
+ // Add bundling and minification (replaces BundleConfig)
+ services.AddWebOptimizer(pipeline =>
+ {
+ // Configure CSS and JS bundling here
+ pipeline.AddCssBundle("/css/bundle.css", "css/**/*.css");
+ pipeline.AddJavaScriptBundle("/js/bundle.js", "js/**/*.js");
+ });
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger logger)
+ {
+ // Configure error handling (replaces Application_Error in Global.asax)
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+ else
+ {
+ app.UseExceptionHandler("/Home/Error");
+ app.UseHsts();
+ }
+
+ // Add custom error handling middleware
+ app.UseMiddleware();
+
+ // Security headers (replaces Application_PreSendRequestHeaders)
+ app.Use(async (context, next) =>
+ {
+ context.Response.Headers.Remove("Server");
+ context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
+ context.Response.Headers.Add("X-Frame-Options", "DENY");
+ context.Response.Headers.Add("X-XSS-Protection", "1; mode=block");
+ await next();
+ });
+
+ app.UseHttpsRedirection();
+ app.UseStaticFiles();
+
+ // Use bundling and minification
+ app.UseWebOptimizer();
+
+ // CORS middleware (replaces Application_BeginRequest CORS headers)
+ app.UseCors();
+
+ // Session middleware (replaces System.Web.SessionState)
+ app.UseSession();
+
+ // Custom session initialization middleware (replaces Session_Start)
+ app.UseMiddleware();
+
+ app.UseRouting();
+
+ app.UseAuthentication();
+ app.UseAuthorization();
+
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapControllerRoute(
+ name: "areas",
+ pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
+
+ endpoints.MapControllerRoute(
+ name: "default",
+ pattern: "{controller=Home}/{action=Index}/{id?}");
+
+ // API routes
+ endpoints.MapControllers();
+ });
+
+ // Initialize BExIS application (replaces Application_Start)
+ InitializeBExISApplication();
+
+ logger.LogInformation("BExIS application started successfully");
+ }
+
+ private void ConfigureIoCContainer(IServiceCollection services)
+ {
+ // Configure IoC container integration
+ // This replaces the IoC initialization from Global.asax
+ services.AddSingleton();
+
+ // Add other IoC registrations here
+ }
+
+ private void ConfigureBExISServices(IServiceCollection services)
+ {
+ // Register BExIS-specific services
+ services.AddScoped();
+
+ // Add other BExIS service registrations
+ }
+
+ private void InitializeBExISApplication()
+ {
+ // This replaces Application_Start logic from Global.asax
+ try
+ {
+ _app = BExIS.App.Bootstrap.Application.GetInstance(RunStage.Production);
+ _app.Start(ConfigureWebApi, true);
+ }
+ catch (Exception ex)
+ {
+ // Log startup errors
+ // Consider using ILogger here
+ throw new ApplicationException("Failed to initialize BExIS application", ex);
+ }
+ }
+
+ private void ConfigureWebApi(IServiceCollection services)
+ {
+ // Web API configuration - replaces WebApiConfig.Register
+ services.AddControllers()
+ .AddNewtonsoftJson(options =>
+ {
+ options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
+ options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
+ });
+ }
+
+ // This replaces Application_End from Global.asax
+ public void Dispose()
+ {
+ _app?.Stop();
+ }
+ }
+
+ ///
+ /// Custom view location expander that replaces the CustomViewEngine logic from Global.asax
+ ///
+ public class CustomViewLocationExpander : IViewLocationExpander
+ {
+ public IEnumerable ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable viewLocations)
+ {
+ // Add additional view search paths
+ var additionalLocations = new[]
+ {
+ "/Areas/{2}/Views/{1}/{0}.cshtml",
+ "/Areas/{2}/Views/Shared/{0}.cshtml",
+ "/Areas/{2}/{3}.{1}.UI/Views/{1}/{0}.cshtml", // One directory lower
+ "/Areas/{2}/{3}.{1}.UI/Views/Shared/{0}.cshtml"
+ };
+
+ return additionalLocations.Concat(viewLocations);
+ }
+
+ public void PopulateValues(ViewLocationExpanderContext context)
+ {
+ // Can add contextual values for view location expansion
+ }
+ }
+
+ ///
+ /// Middleware that handles session initialization (replaces Session_Start from Global.asax)
+ ///
+ public class SessionInitializationMiddleware
+ {
+ private readonly RequestDelegate _next;
+ private readonly IServiceProvider _serviceProvider;
+
+ public SessionInitializationMiddleware(RequestDelegate next, IServiceProvider serviceProvider)
+ {
+ _next = next;
+ _serviceProvider = serviceProvider;
+ }
+
+ public async Task InvokeAsync(HttpContext context)
+ {
+ // Check if this is a new session
+ if (!context.Session.Keys.Any())
+ {
+ await InitializeSession(context);
+ }
+
+ await _next(context);
+ }
+
+ private async Task InitializeSession(HttpContext context)
+ {
+ try
+ {
+ // Session timeout handling (replaces Global.asax Session_Start logic)
+ var sessionCookie = context.Request.Cookies["BExIS.SessionId"];
+ if (!string.IsNullOrEmpty(sessionCookie))
+ {
+ // Handle session timeout scenario
+ context.Response.Redirect("/Home/SessionTimeout");
+ return;
+ }
+
+ // Initialize IoC session-level container
+ using (var scope = _serviceProvider.CreateScope())
+ {
+ var iocFactory = scope.ServiceProvider.GetService();
+ iocFactory?.Container?.StartSessionLevelContainer();
+
+ // Apply culture settings
+ var appConfig = scope.ServiceProvider.GetService();
+ if (appConfig != null)
+ {
+ context.Session.ApplyCulture(appConfig.DefaultCulture);
+ }
+
+ // Resolve tenant
+ var tenantResolver = scope.ServiceProvider.GetService();
+ if (tenantResolver != null)
+ {
+ var tenant = tenantResolver.Resolve(context.Request);
+ context.Session.SetTenant(tenant);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ // Log session initialization errors
+ // In production, consider graceful degradation
+ throw new ApplicationException("Failed to initialize session", ex);
+ }
+ }
+ }
+
+ ///
+ /// Custom error handling middleware (replaces Application_Error from Global.asax)
+ ///
+ public class CustomErrorHandlingMiddleware
+ {
+ private readonly RequestDelegate _next;
+ private readonly ILogger _logger;
+ private readonly IConfiguration _configuration;
+
+ public CustomErrorHandlingMiddleware(RequestDelegate next, ILogger logger, IConfiguration configuration)
+ {
+ _next = next;
+ _logger = logger;
+ _configuration = configuration;
+ }
+
+ public async Task InvokeAsync(HttpContext context)
+ {
+ try
+ {
+ await _next(context);
+ }
+ catch (Exception ex)
+ {
+ await HandleExceptionAsync(context, ex);
+ }
+ }
+
+ private async Task HandleExceptionAsync(HttpContext context, Exception exception)
+ {
+ bool sendExceptions = _configuration.GetValue("SendExceptions", false);
+ var statusCode = 500;
+
+ if (exception is HttpRequestException httpEx)
+ {
+ statusCode = 400; // Bad Request
+ }
+
+ _logger.LogError(exception, "An unhandled exception occurred");
+
+ // Replaces the error email functionality from Global.asax
+ if (sendExceptions &&
+ statusCode != 404 &&
+ !(exception is InvalidOperationException) &&
+ !exception.Message.StartsWith("Multiple types were found that match the controller named"))
+ {
+ await SendErrorEmail(exception);
+ }
+
+ context.Response.StatusCode = statusCode;
+
+ if (!context.Response.HasStarted)
+ {
+ await context.Response.WriteAsync("An error occurred while processing your request.");
+ }
+ }
+
+ private async Task SendErrorEmail(Exception exception)
+ {
+ try
+ {
+ // Implement error email functionality
+ // This replaces ErrorHelper.SendEmailWithErrors from Global.asax
+ _logger.LogCritical(exception, "Critical error occurred - email notification should be sent");
+ }
+ catch (Exception emailEx)
+ {
+ _logger.LogError(emailEx, "Failed to send error notification email");
+ }
+ }
+ }
+
+ ///
+ /// Extension methods for session management (replaces System.Web.SessionState extensions)
+ ///
+ public static class SessionExtensions
+ {
+ public static void ApplyCulture(this ISession session, string defaultCulture)
+ {
+ // Apply culture settings to current thread
+ var culture = new System.Globalization.CultureInfo(defaultCulture);
+ System.Threading.Thread.CurrentThread.CurrentCulture = culture;
+ System.Threading.Thread.CurrentThread.CurrentUICulture = culture;
+
+ // Store in session for persistence
+ session.SetString("Culture", defaultCulture);
+ }
+
+ public static void SetTenant(this ISession session, Tenant tenant)
+ {
+ if (tenant != null)
+ {
+ session.SetString("TenantId", tenant.Id.ToString());
+ session.SetString("TenantName", tenant.Name ?? string.Empty);
+ }
+ }
+
+ public static Tenant GetTenant(this ISession session)
+ {
+ var tenantId = session.GetString("TenantId");
+ var tenantName = session.GetString("TenantName");
+
+ if (!string.IsNullOrEmpty(tenantId) && long.TryParse(tenantId, out long id))
+ {
+ return new Tenant { Id = id, Name = tenantName };
+ }
+
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Migration-Examples/appsettings.json b/Migration-Examples/appsettings.json
new file mode 100644
index 000000000..1d97c36b2
--- /dev/null
+++ b/Migration-Examples/appsettings.json
@@ -0,0 +1,199 @@
+{
+ // Configuration settings that replace Web.config appSettings and connectionStrings
+ // This file provides cross-platform compatibility for Linux deployment
+
+ "ConnectionStrings": {
+ "ApplicationServices": "Data Source=localhost;Initial Catalog=BExIS;Integrated Security=True;MultipleActiveResultSets=True",
+ "DefaultConnection": "Data Source=localhost;Initial Catalog=BExIS;Integrated Security=True;MultipleActiveResultSets=True"
+ },
+
+ // Application settings (replaces Web.config appSettings)
+ "DefaultCulture": "en-US",
+ "DatabaseMappingFile": "Database.Mapping.xml",
+ "DatabaseDialect": "SqlServer2012Dialect",
+ "AutoCommitTransactions": false,
+ "IoCProviderTypeInfo": "Vaiona.IoC.Unity.UnityIoCProvider, Vaiona.IoC.Unity",
+
+ // Path configurations (cross-platform compatible)
+ "ApplicationRoot": "", // Empty = use ContentRootPath
+ "DataPath": "Data",
+ "WorkspacePath": "../Workspace",
+ "ThemesPath": "~/Themes",
+ "DefaultThemeName": "Default",
+ "ActiveLayoutName": "_Layout",
+
+ // Database settings
+ "UseSchemaInDatabaseGeneration": false,
+ "CreateDatabase": false,
+ "ShowQueries": false,
+ "CacheQueryResults": true,
+ "ConversationIsolationLevel": 2,
+
+ // Logging configuration (replaces Web.config logging settings)
+ "IsLoggingEnable": true,
+ "IsPerformanceLoggingEnable": false,
+ "IsDiagnosticLoggingEnable": false,
+ "IsCallLoggingEnable": false,
+ "IsExceptionLoggingEnable": true,
+ "IsDataLoggingEnable": false,
+
+ // Multi-tenancy
+ "TenantId": "",
+
+ // Error handling
+ "SendExceptions": false,
+ "ThrowErrorWhenParialContentNotFound": false,
+
+ // ASP.NET Core specific logging configuration
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information",
+ "Microsoft.AspNetCore": "Warning",
+ "BExIS": "Information"
+ },
+ "Console": {
+ "IncludeScopes": true
+ },
+ "File": {
+ "Path": "Logs/bexis-{Date}.log",
+ "MinLevel": "Information",
+ "OutputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
+ }
+ },
+
+ // Session configuration (replaces System.Web.SessionState)
+ "Session": {
+ "TimeoutMinutes": 30,
+ "CookieName": "BExIS.SessionId",
+ "CookieHttpOnly": true,
+ "CookieSecure": "SameAsRequest", // SameAsRequest, Always, Never
+ "SameSite": "Lax" // Strict, Lax, None
+ },
+
+ // CORS settings (replaces Global.asax CORS headers)
+ "Cors": {
+ "AllowedOrigins": [ "*" ],
+ "AllowedMethods": [ "GET", "POST", "PUT", "DELETE", "OPTIONS" ],
+ "AllowedHeaders": [ "*" ],
+ "AllowCredentials": false
+ },
+
+ // Bundling and optimization settings
+ "WebOptimizer": {
+ "EnableCaching": true,
+ "EnableCompression": true,
+ "EnableTagHelperBundling": true
+ },
+
+ // Security settings
+ "Security": {
+ "RequireHttps": false,
+ "HstsMaxAge": 31536000,
+ "ContentSecurityPolicy": "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"
+ },
+
+ // Application insights (optional - for monitoring)
+ "ApplicationInsights": {
+ "InstrumentationKey": "",
+ "EnableAdaptiveSampling": true
+ },
+
+ // Health checks configuration
+ "HealthChecks": {
+ "Database": {
+ "Enabled": true,
+ "TimeoutSeconds": 30
+ },
+ "FileSystem": {
+ "Enabled": true,
+ "Paths": [ "Data", "Workspace" ]
+ }
+ },
+
+ // Kestrel server configuration (for Linux deployment)
+ "Kestrel": {
+ "Endpoints": {
+ "Http": {
+ "Url": "http://localhost:5000"
+ },
+ "Https": {
+ "Url": "https://localhost:5001",
+ "Certificate": {
+ "Path": "certificates/bexis.pfx",
+ "Password": ""
+ }
+ }
+ },
+ "Limits": {
+ "MaxConcurrentConnections": 100,
+ "MaxRequestBodySize": 104857600, // 100MB
+ "RequestHeadersTimeout": "00:00:30",
+ "KeepAliveTimeout": "00:02:00"
+ }
+ },
+
+ // Authentication settings (if using JWT or similar)
+ "Authentication": {
+ "DefaultScheme": "Cookies",
+ "Cookies": {
+ "LoginPath": "/Account/Login",
+ "LogoutPath": "/Account/Logout",
+ "AccessDeniedPath": "/Account/AccessDenied",
+ "ExpireTimeSpan": "01:00:00", // 1 hour
+ "SlidingExpiration": true
+ },
+ "JwtBearer": {
+ "ValidateIssuer": true,
+ "ValidateAudience": true,
+ "ValidateLifetime": true,
+ "ValidateIssuerSigningKey": true,
+ "Issuer": "https://localhost:5001",
+ "Audience": "BExIS.API",
+ "SecretKey": "YourSecretKeyHereMinimum32Characters!"
+ }
+ },
+
+ // Email settings (if using SMTP)
+ "Email": {
+ "SmtpServer": "localhost",
+ "SmtpPort": 587,
+ "SmtpUsername": "",
+ "SmtpPassword": "",
+ "EnableSsl": true,
+ "FromAddress": "noreply@bexis.uni-jena.de",
+ "FromName": "BExIS System"
+ },
+
+ // File upload settings
+ "FileUpload": {
+ "MaxFileSize": 104857600, // 100MB in bytes
+ "AllowedExtensions": [ ".csv", ".xlsx", ".txt", ".xml", ".json", ".zip" ],
+ "UploadPath": "Data/Uploads",
+ "TempPath": "Data/Temp"
+ }
+}
+
+/*
+Migration Notes from Web.config to appsettings.json:
+
+1. ConnectionStrings section maps directly
+2. appSettings become root-level properties or grouped logically
+3. system.web/httpRuntime settings -> Kestrel configuration
+4. system.web/compilation -> Not needed (handled by MSBuild)
+5. system.web/authentication -> Authentication configuration section
+6. system.web/sessionState -> Session configuration section
+7. system.web/customErrors -> Exception handling middleware
+8. system.web/globalization -> RequestLocalization middleware
+9. system.webServer/handlers -> Not needed (built into ASP.NET Core)
+10. system.webServer/modules -> Middleware pipeline in Startup.cs
+
+Key Benefits:
+- JSON is cross-platform and easier to parse
+- Strong typing with IConfiguration.Get()
+- Environment-specific overrides (appsettings.Development.json)
+- Secret management integration
+- Hierarchical configuration with colons (e.g., "Database:ConnectionString")
+- Built-in validation and binding to POCOs
+*/
\ No newline at end of file