Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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**

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -40,7 +40,7 @@ The server's configuration needs to be transferred to code within the `Program.c

The `<customErrors>` element within `<system.web>` 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 `<customErrors>` element will provide a default redirect, and may contain `<error>` 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
<customErrors mode="RemoteOnly" defaultRedirect="~/Error">
Expand All @@ -64,7 +64,7 @@ The [`<pages>/<namespaces>`](https://learn.microsoft.com/en-us/windows/win32/wsd

### HTTP Handler Routes

The [`<httpHandlers>`](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 [`<httpHandlers>`](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

Expand Down Expand Up @@ -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"];
Expand Down
7 changes: 5 additions & 2 deletions public/uploads/rules/migrate-from-edmx-to-ef-core/rule.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Comment on lines +384 to +386

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check this, the flag does not seem to ever exist in EF Core


# Generate an idempotent SQL script (useful for deployments)
dotnet ef migrations script --context MyDbContext --idempotent --output migrations.sql
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check this


```csharp
public interface IRequestContext
Expand Down Expand Up @@ -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.
24 changes: 12 additions & 12 deletions public/uploads/rules/migrate-global-asax-to-asp-net-core/rule.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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");
}
}
```
Expand All @@ -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"));

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't try to map to older lifehooks as they are different, just call it Stopping and Stopped

app.Lifetime.ApplicationStopped.Register(() => Console.WriteLine("Disposed"));
```
**✅ Figure: The above code migrated to ASP.NET Core.**

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
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.
Loading
Loading