diff --git a/public/uploads/rules/do-you-check-your-api-serialisation-format/rule.mdx b/public/uploads/rules/do-you-check-your-api-serialisation-format/rule.mdx index 84254a93367..2a4d64d654b 100644 --- a/public/uploads/rules/do-you-check-your-api-serialisation-format/rule.mdx +++ b/public/uploads/rules/do-you-check-your-api-serialisation-format/rule.mdx @@ -38,7 +38,7 @@ The primary reason for switching to `System.Text.Json` is its [faster performanc ## The differences -[This Microsoft documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-9-0#table-of-differences&WT.mc_id=DT-MVP-33518) contains a compiled list of differences between `System.Text.Json` and `Newtonsoft.Json`. +[This Microsoft documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-10-0#table-of-differences&WT.mc_id=DT-MVP-33518) contains a compiled list of differences between `System.Text.Json` and `Newtonsoft.Json`. ### Notable Things to Check @@ -50,9 +50,9 @@ The primary reason for switching to `System.Text.Json` is its [faster performanc * **Option A:** Implement a per-controller override for migrated legacy APIs to maintain the same behaviour by setting `JsonSerializerOptions.PropertyNamingPolicy = null`, e.g., via a custom attribute using `ActionFilterAttribute`. * **Option B:** Apply a global JSON serialisation override to retain `JsonSerializerOptions.PropertyNamingPolicy = null`. -* ⚠️ **No Support for JSON Patch Documents** +* ⚠️ **JSON Patch Documents** - Deserialisation of JSON Patch documents might fail due to [lack of support for JSON Path queries](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-9-0#json-path-queries-not-supported&WT.mc_id=DT-MVP-33518), e.g., commonly used in legacy `PATCH` endpoints. + Historically `System.Text.Json` could not handle JSON Patch documents, so legacy `PATCH` endpoints relied on `Newtonsoft.Json`. **As of .NET 10**, ASP.NET Core supports JSON Patch with `System.Text.Json` via the [`Microsoft.AspNetCore.JsonPatch.SystemTextJson`](https://www.nuget.org/packages/Microsoft.AspNetCore.JsonPatch.SystemTextJson) package - though some [behavioural differences](https://learn.microsoft.com/en-us/aspnet/core/web-api/jsonpatch?view=aspnetcore-10.0) still apply. When targeting an earlier framework, JSON Patch still requires `Newtonsoft.Json`. * ⚠️ **Limited OData Support** diff --git a/public/uploads/rules/know-how-to-migrate-web-config-to-asp-net-core/rule.mdx b/public/uploads/rules/know-how-to-migrate-web-config-to-asp-net-core/rule.mdx index 0a8a52f5c14..09484b6f578 100644 --- a/public/uploads/rules/know-how-to-migrate-web-config-to-asp-net-core/rule.mdx +++ b/public/uploads/rules/know-how-to-migrate-web-config-to-asp-net-core/rule.mdx @@ -22,7 +22,7 @@ type: rule uri: know-how-to-migrate-web-config-to-asp-net-core --- -The [`Web.Config`](https://learn.microsoft.com/en-us/troubleshoot/developer/webapps/aspnet/health-diagnostic-performance/create-web-config?WT.mc_id=DT-MVP-33518) file was used in ASP.NET to control the behaviour of individual ASP.NET applications and configure IIS. By default, modern ASP.NET Core applications use the Kestrel web server which is [configured in code](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/options?view=aspnetcore-7.0&WT.mc_id=DT-MVP-33518). Unless you are deploying your application using IIS, you will need to migrate your `Web.Config` file. +The [`Web.Config`](https://learn.microsoft.com/en-us/troubleshoot/developer/webapps/aspnet/health-diagnostic-performance/create-web-config?WT.mc_id=DT-MVP-33518) file was used in ASP.NET to control the behaviour of individual ASP.NET applications and configure IIS. By default, modern ASP.NET Core applications use the Kestrel web server which is [configured in code](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/options?view=aspnetcore-10.0&WT.mc_id=DT-MVP-33518). Unless you are deploying your application using IIS, you will need to migrate your `Web.Config` file. The `Web.Config` file contains data about the package inclusions, module inclusions and configuration values. @@ -40,7 +40,7 @@ The server's configuration needs to be transferred to code within the `Program.c The `` element within `` specifies redirects for the server to use if a response with a HTTP error code is generated. When the relevant [SSW Rule on useful error pages](/404-useful-error-page/) is followed, the mode will be 'RemoteOnly', meaning that the redirect will only be used if accessed from a separate host. The `` element will provide a default redirect, and may contain `` elements that provide more specific redirects for specific error codes. -The easiest way to transcode this configuration is using [`UseStatusCodePagesWithRedirects`](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-7.0#usestatuscodepageswithredirects&WT.mc_id=DT-MVP-33518). +The easiest way to transcode this configuration is using [`UseStatusCodePagesWithRedirects`](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-10.0#usestatuscodepageswithredirects&WT.mc_id=DT-MVP-33518). ```xml @@ -64,7 +64,7 @@ The [`/`](https://learn.microsoft.com/en-us/windows/win32/wsd ### HTTP Handler Routes -The [``](https://learn.microsoft.com/en-us/previous-versions/aspnet/bb398986(v=vs.100)) element links routes to [`IHttpHandler`](https://learn.microsoft.com/en-us/dotnet/api/system.web.ihttphandler?view=netframework-4.8.1&WT.mc_id=DT-MVP-33518) implementations. See the [ASP.NET Core Fundamentals article on Routing](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-7.0&WT.mc_id=DT-MVP-33518) for replacement options, including the use of [`MapGet`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.endpointroutebuilderextensions.mapget?view=aspnetcore-7.0#microsoft-aspnetcore-builder-endpointroutebuilderextensions-mapget(microsoft-aspnetcore-routing-iendpointroutebuilder-system-string-system-delegate&WT.mc_id=DT-MVP-33518)) and [`MapPost`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.endpointroutebuilderextensions.mappost?view=aspnetcore-7.0#microsoft-aspnetcore-builder-endpointroutebuilderextensions-mappost(microsoft-aspnetcore-routing-iendpointroutebuilder-system-string-system-delegate&WT.mc_id=DT-MVP-33518)). +The [``](https://learn.microsoft.com/en-us/previous-versions/aspnet/bb398986(v=vs.100)) element links routes to [`IHttpHandler`](https://learn.microsoft.com/en-us/dotnet/api/system.web.ihttphandler?view=netframework-4.8.1&WT.mc_id=DT-MVP-33518) implementations. See the [ASP.NET Core Fundamentals article on Routing](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-10.0&WT.mc_id=DT-MVP-33518) for replacement options, including the use of [`MapGet`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.endpointroutebuilderextensions.mapget?view=aspnetcore-10.0#microsoft-aspnetcore-builder-endpointroutebuilderextensions-mapget(microsoft-aspnetcore-routing-iendpointroutebuilder-system-string-system-delegate&WT.mc_id=DT-MVP-33518)) and [`MapPost`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.endpointroutebuilderextensions.mappost?view=aspnetcore-10.0#microsoft-aspnetcore-builder-endpointroutebuilderextensions-mappost(microsoft-aspnetcore-routing-iendpointroutebuilder-system-string-system-delegate&WT.mc_id=DT-MVP-33518)). ### HTTP Modules @@ -94,7 +94,7 @@ In the case of non-secret values, they can be moved to an `appsettings.json` fil **Figure: The application settings example migrated to `appsettings.json`** -The class used to access configuration values will also need to be changed if the program is using [System.Configuration.ConfigurationManager](https://learn.microsoft.com/en-us/dotnet/api/system.configuration.configurationmanager?WT.mc_id=DT-MVP-33518) as that class is not available under ASP.NET Core. Instead, use a dependency injected [`IConfiguration`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.configuration.iconfiguration?view=dotnet-plat-ext-7.0&WT.mc_id=DT-MVP-33518) implementation from the `Microsoft.Extensions.Configuration` package. +The class used to access configuration values will also need to be changed if the program is using [System.Configuration.ConfigurationManager](https://learn.microsoft.com/en-us/dotnet/api/system.configuration.configurationmanager?WT.mc_id=DT-MVP-33518) as that class is not available under ASP.NET Core. Instead, use a dependency injected [`IConfiguration`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.configuration.iconfiguration?view=net-10.0&WT.mc_id=DT-MVP-33518) implementation from the `Microsoft.Extensions.Configuration` package. ```cs String visibility = ConfigurationManager.AppSettings["DefaultVisibility"]; diff --git a/public/uploads/rules/migrate-from-edmx-to-ef-core/rule.mdx b/public/uploads/rules/migrate-from-edmx-to-ef-core/rule.mdx index 8e3165b89ac..d5160f25a20 100644 --- a/public/uploads/rules/migrate-from-edmx-to-ef-core/rule.mdx +++ b/public/uploads/rules/migrate-from-edmx-to-ef-core/rule.mdx @@ -12,6 +12,8 @@ authors: url: 'https://www.ssw.com.au/people/gordon-beeming' - title: Kaha Mason url: 'https://www.ssw.com.au/people/kaha-mason' + - title: Kosta Madorsky + url: 'https://www.ssw.com.au/people/kosta-madorsky' related: - rule: public/uploads/rules/dotnet-upgrade-for-complex-projects/rule.mdx - rule: public/uploads/rules/migrate-from-system-web-to-modern-alternatives/rule.mdx @@ -379,8 +381,9 @@ dotnet ef migrations add InitialCreate --context MyDbContext # Apply migrations to the database dotnet ef database update --context MyDbContext -# If you’re starting from an existing database and want a "baseline" migration -dotnet ef migrations add Baseline --context MyDbContext --ignore-changes +# Starting from an existing database? Add the initial migration to capture the +# current schema, then mark it as applied (insert its row into __EFMigrationsHistory) +# instead of running it - the old `--ignore-changes` flag has been removed. # Generate an idempotent SQL script (useful for deployments) dotnet ef migrations script --context MyDbContext --idempotent --output migrations.sql diff --git a/public/uploads/rules/migrate-from-system-web-to-modern-alternatives/rule.mdx b/public/uploads/rules/migrate-from-system-web-to-modern-alternatives/rule.mdx index 52692fa5a54..8ce85300746 100644 --- a/public/uploads/rules/migrate-from-system-web-to-modern-alternatives/rule.mdx +++ b/public/uploads/rules/migrate-from-system-web-to-modern-alternatives/rule.mdx @@ -98,7 +98,7 @@ If you multi-target .NET and .NET Framework, we need to add back System.Web refe **Figure: Conditional inclusion of the "System.Web" reference in a .NET Framework 4.7.2 project.** -Next step is to define an interface. For this case, we’ll only expose currently authenticated user. We are calling it IApplicationContext as it contains context for current request, whether it’s coming from an HttpContext or somewhere else if it’s a background job or console application. +Next step is to define an interface. For this case, we’ll only expose currently authenticated user. We are calling it IRequestContext as it contains context for current request, whether it’s coming from an HttpContext or somewhere else if it’s a background job or console application. ```csharp public interface IRequestContext @@ -189,6 +189,6 @@ For larger applications or those with significant shared libraries, you might wa These adapters allow you to access **`HttpContext.Current`** without needing to port the code. Be aware that while this might seem like an easy solution, not all projects have been able to adopt this without issues. -Please refer to the official Microsoft documentation on [System.Web adapters | Microsoft Learn](https://learn.microsoft.com/en-us/aspnet/core/migration/fx-to-core/inc/systemweb-adapters?view=aspnetcore-9.0) for more details. +Please refer to the official Microsoft documentation on [System.Web adapters | Microsoft Learn](https://learn.microsoft.com/en-us/aspnet/core/migration/fx-to-core/inc/systemweb-adapters?view=aspnetcore-10.0) for more details. NOTE: The above strategies are not mutually exclusive and can be combined depending on your specific needs and constraints. The goal is to make your code more adaptable and ready for the migration to .NET or .NET Standard. diff --git a/public/uploads/rules/migrate-global-asax-to-asp-net-core/rule.mdx b/public/uploads/rules/migrate-global-asax-to-asp-net-core/rule.mdx index 3b22e9a91b6..0c834227362 100644 --- a/public/uploads/rules/migrate-global-asax-to-asp-net-core/rule.mdx +++ b/public/uploads/rules/migrate-global-asax-to-asp-net-core/rule.mdx @@ -26,11 +26,11 @@ The [`Global.asax`](https://learn.microsoft.com/en-us/previous-versions/aspnet/1 The methods given below are automatically linked to event handlers on the [HttpApplication](https://learn.microsoft.com/en-us/dotnet/api/system.web.httpapplication?view=netframework-4.8.1&WT.mc_id=DT-MVP-33518) class at runtime. -The `Application_Start()` or `Application_OnStart()` method is called once upon the first request being received by the server, and is typically used to initialize static values. The logic for this starting method should be included at the beginning of [`Program.cs`](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/startup?view=aspnetcore-7.0&WT.mc_id=DT-MVP-33518) in the ASP.NET Core project. +The `Application_Start()` or `Application_OnStart()` method is called once upon the first request being received by the server, and is typically used to initialize static values. The logic for this starting method should be included at the beginning of [`Program.cs`](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/startup?view=aspnetcore-10.0&WT.mc_id=DT-MVP-33518) in the ASP.NET Core project. -The `Application_Init()` method is called after all event handler modules have been added. Its logic can be migrated by registering the logic with the [WebApplication.Lifetime](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.webapplication.lifetime?view=aspnetcore-7.0&WT.mc_id=DT-MVP-33518) property [ApplicationStarted](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.ihostapplicationlifetime.applicationstarted?view=dotnet-plat-ext-7.0#microsoft-extensions-hosting-ihostapplicationlifetime-applicationstarted&WT.mc_id=DT-MVP-33518). +The `Application_Init()` method is called after all event handler modules have been added. Its logic can be migrated by registering the logic with the [WebApplication.Lifetime](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.webapplication.lifetime?view=aspnetcore-10.0&WT.mc_id=DT-MVP-33518) property [ApplicationStarted](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.ihostapplicationlifetime.applicationstarted?view=net-10.0#microsoft-extensions-hosting-ihostapplicationlifetime-applicationstarted&WT.mc_id=DT-MVP-33518). -The `Application_End()` and `Application_Disposed()` methods are fired upon application termination. They can be migrated by registering the logic with the [WebApplication.Lifetime](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.webapplication.lifetime?view=aspnetcore-7.0&WT.mc_id=DT-MVP-33518) properties [ApplicationStopping](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.ihostapplicationlifetime.applicationstopping?view=dotnet-plat-ext-7.0#microsoft-extensions-hosting-ihostapplicationlifetime-applicationstopping&WT.mc_id=DT-MVP-33518) and [ApplicationStopped](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.ihostapplicationlifetime.applicationstopped?view=dotnet-plat-ext-7.0#microsoft-extensions-hosting-ihostapplicationlifetime-applicationstopped&WT.mc_id=DT-MVP-33518). +The `Application_End()` and `Application_Disposed()` methods are fired upon application termination. They can be migrated by registering the logic with the [WebApplication.Lifetime](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.webapplication.lifetime?view=aspnetcore-10.0&WT.mc_id=DT-MVP-33518) properties [ApplicationStopping](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.ihostapplicationlifetime.applicationstopping?view=net-10.0#microsoft-extensions-hosting-ihostapplicationlifetime-applicationstopping&WT.mc_id=DT-MVP-33518) and [ApplicationStopped](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.ihostapplicationlifetime.applicationstopped?view=net-10.0#microsoft-extensions-hosting-ihostapplicationlifetime-applicationstopped&WT.mc_id=DT-MVP-33518). Therefore, the following `Global.asax.cs` snippet would migrate as per the figures below. @@ -45,12 +45,12 @@ public class MvcApplication : HttpApplication Console.WriteLine("Init"); } - protected void Application_Stopping() { - Console.WriteLine("Stopping"); + protected void Application_End() { + Console.WriteLine("End"); } - protected void Application_Stopped() { - Console.WriteLine("Stopped"); + protected void Application_Disposed() { + Console.WriteLine("Disposed"); } } ``` @@ -63,8 +63,8 @@ var builder = WebApplication.CreateBuilder(args); // ... var app = builder.Build(); app.Lifetime.ApplicationStarted.Register(() => Console.WriteLine("Init")); -app.Lifetime.ApplicationStopping.Register(() => Console.WriteLine("Stopping")); -app.Lifetime.ApplicationStopped.Register(() => Console.WriteLine("Stopped")); +app.Lifetime.ApplicationStopping.Register(() => Console.WriteLine("End")); +app.Lifetime.ApplicationStopped.Register(() => Console.WriteLine("Disposed")); ``` **✅ Figure: The above code migrated to ASP.NET Core.** @@ -76,7 +76,7 @@ The `Session_Start()` is called when a new user session is detected. The `Sessio ## Request Lifecycle Methods -The events raised during a request are [documented in the HttpApplication API](https://learn.microsoft.com/en-us/dotnet/api/system.web.httpapplication?view=netframework-4.8.1#remarks&WT.mc_id=DT-MVP-33518). The logic to be executed before and after a request should be implemented using [middleware](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-7.0&WT.mc_id=DT-MVP-33518). +The events raised during a request are [documented in the HttpApplication API](https://learn.microsoft.com/en-us/dotnet/api/system.web.httpapplication?view=netframework-4.8.1#remarks&WT.mc_id=DT-MVP-33518). The logic to be executed before and after a request should be implemented using [middleware](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-10.0&WT.mc_id=DT-MVP-33518). ```cs public class MvcApplication : HttpApplication @@ -107,7 +107,7 @@ app.Use(async (context, next) => ## Error Handling -Global error handling logic in [`Application_Error()`](https://learn.microsoft.com/en-us/dotnet/api/system.web.httpapplication.error?view=netframework-4.8.1&WT.mc_id=DT-MVP-33518) method should be migrated to use middleware registered with the [`UseExceptionHandler()`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.exceptionhandlerextensions.useexceptionhandler?view=aspnetcore-7.0&WT.mc_id=DT-MVP-33518) method. +Global error handling logic in [`Application_Error()`](https://learn.microsoft.com/en-us/dotnet/api/system.web.httpapplication.error?view=netframework-4.8.1&WT.mc_id=DT-MVP-33518) method should be migrated to use middleware registered with the [`UseExceptionHandler()`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.exceptionhandlerextensions.useexceptionhandler?view=aspnetcore-10.0&WT.mc_id=DT-MVP-33518) method. ```cs public class MvcApplication : HttpApplication @@ -139,4 +139,4 @@ app.UseExceptionHandler(exceptionHandlerApp => ``` **✅ Figure: Using exception handling middleware.** -See [here](https://learn.microsoft.com/en-us/aspnet/core/web-api/handle-errors?view=aspnetcore-7.0&WT.mc_id=DT-MVP-33518) for more options for handling errors in ASP.NET Core. \ No newline at end of file +See [here](https://learn.microsoft.com/en-us/aspnet/core/web-api/handle-errors?view=aspnetcore-10.0&WT.mc_id=DT-MVP-33518) for more options for handling errors in ASP.NET Core. \ No newline at end of file diff --git a/public/uploads/rules/migrating-web-apps-to-dotnet/rule.mdx b/public/uploads/rules/migrating-web-apps-to-dotnet/rule.mdx index 0afef7bfdd4..d7ed76a148b 100644 --- a/public/uploads/rules/migrating-web-apps-to-dotnet/rule.mdx +++ b/public/uploads/rules/migrating-web-apps-to-dotnet/rule.mdx @@ -44,7 +44,7 @@ The differences between a web app built with ASP.NET Framework and one built wit ## To YARP, or not to YARP -There exists, somewhere, a line that separates the "big bang" and "stranger fig" approach as being the *recommended* way to tackle web app migrations. While this decision point is unique to every project, you can examine a couple of metrics to help guide your decision. +There exists, somewhere, a line that separates the "big bang" and "strangler fig" approach as being the *recommended* way to tackle web app migrations. While this decision point is unique to every project, you can examine a couple of metrics to help guide your decision. * How many Sprints do you estimate the migration work will take? * Will feature development continue during the migration process? @@ -122,7 +122,7 @@ RouteConfig[] GetRoutes() ### Migrating with GitHub Copilot -Once you have created a side-by-side project, you can start migration by interating with GitHub Copilot following these steps: +Once you have created a side-by-side project, you can start migration by iterating with GitHub Copilot following these steps: 1. Open your .NET project or solution that needs migration. 2. Access the GitHub Copilot app modernization agent using one of these methods: diff --git a/public/uploads/rules/migration-plans/rule.mdx b/public/uploads/rules/migration-plans/rule.mdx index 13d00dd70f4..a347933cac5 100644 --- a/public/uploads/rules/migration-plans/rule.mdx +++ b/public/uploads/rules/migration-plans/rule.mdx @@ -158,7 +158,7 @@ Multi-targeting both your original .NET Framework version *and* your future .NET In all your project files, change the `TargetFramework` tag to `TargetFrameworks`. You want to do this early on to allow for multiple target frameworks and enable a smoother flow later to avoid needing to reload projects or have to close and reopen Visual Studio. ```csharp -net48;net8.0 +net48;net10.0 ``` diff --git a/public/uploads/rules/use-banned-api-analyzers/rule.mdx b/public/uploads/rules/use-banned-api-analyzers/rule.mdx index 396aba219cb..a07328d2be2 100644 --- a/public/uploads/rules/use-banned-api-analyzers/rule.mdx +++ b/public/uploads/rules/use-banned-api-analyzers/rule.mdx @@ -100,7 +100,7 @@ Putting it all together, your `.csproj` should look like this: ```xml - net4.8 + net48 RS0030