---
# System prepended metadata

title: Net 6 Upgrade to Net 8
tags: [.Net Core]

---

## .NET Extension : .NET Upgrade Assistant
- Automatically upgrade to target framework
[Reference](https://medium.com/@hany.habib1988/my-journey-to-migrate-from-net-core-6-to-net-core-8-c5e9a164c695)

## SqlServer => (Default) Encrypt = true
>Q: 新版 Microsoft.EntityFrameworkCore.SqlServer version > 7後，將Encrypt加密預設為true;
>A: 開發時，將連線字串加上 ";TrustServerCertificate=true"，使憑證通過。(not for production cuz it will bypassed identity certificate)
[Microsoft.Data.SqlClient breaking-changes](https://learn.microsoft.com/zh-tw/ef/core/what-is-new/ef-core-7.0/breaking-changes?tabs=v7#encrypt-true)
>A: In production mode, you should configured certificated Authority to the sqlServer.
>using Active Directory produce CA, authorized in client side. https://blog.darkthread.net/blog/setup-ad-n-ca/
---
## System.Text.Json.JsonSerializer => .Net 8 序列過程 將「延遲加載」(lazy loading) 改成 「提前解析」(eager loading)
>Q: 嘗試Deserialize時，會遇到以下錯誤，在反序列化過程中，Encoding 的屬性可能使用 ReadOnlySpan<byte> 或其他類似的字節流來進行字符解碼。 (因為ReadOnlySpan<byte> 是pointer type or ref struct)Unsuported，.Net 7 延遲加載可能會忽略某些屬性，.Net 8提前解析是針對每一個屬性，就不會有不支援的屬性被忽略的問題。
反序列化過程與 ref struct 類型的衝突：因為 ref struct 類型（如 ReadOnlySpan<byte>）不能直接存儲在Heap(堆)上或直接序列化，因此可能會在反序列化過程中觸發錯誤。
```CSharp
({"The type 'System.ReadOnlySpan`1[System.Byte]' of property 'Preamble' on type 'System.Text.Encoding' is invalid for serialization or deserialization because it is a pointer type, is a ref struct, or contains generic parameters that have not been replaced by specific types."})
```
>A: 使用自定義的JsonEncodingConverter，讓Encoding屬性可以被正常解析
```csharp
internal class JsonEncodingConverter : JsonConverter<Encoding> 
{
    public override Encoding Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        => Encoding.GetEncoding(reader.GetString());  // 直接返回Encoding

    public override void Write(Utf8JsonWriter writer, Encoding value, JsonSerializerOptions options)
        => writer.WriteStringValue(value.EncodingName); // 只寫入Encoding的名稱
}
    
var options = new JsonSerializerOptions();
options.Converters.Add(new JsonEncodingConverter());
var file = System.IO.File.ReadAllText(path);
var model = JsonSerializer.Deserialize<VM_Mail>(file, options);
```
```csharp
    
/// <summary> email附件 </summary>
[JsonIgnore]
public List<Attachment> Attachments { get; set; }
public class Attachment : AttachmentBase
{
    private string? _name;
    private Encoding? _nameEncoding;
    //因為這個Attachment型別裡面有包到Encoding，只要Ignore就好
    //System.Text.Json 預設不支援轉換 Encoding
```
![image](https://hackmd.io/_uploads/rkCPnZmQyx.png)

```
    JsonConverter<T>是抽象泛型類別，覆寫他裡面Read(反序列化)及Write(序列化)的方法
    
    Encoding.GetEncoding(reader.GetString())，這是根據讀取到的字串（代表編碼名稱）來返回對應的 Encoding 類型物件。
    
    writer.WriteStringValue(value.EncodingName)，這會把 Encoding 類型的 EncodingName（編碼名稱）寫入 JSON 中。
```
```這個 JsonEncodingConverter 類別是用來處理 Encoding 類型的自定義 JSON 序列化和反序列化過程。它實現了 JsonConverter<Encoding> 類別，這使得它能夠定義如何將 Encoding 類型的資料進行序列化（轉換成 JSON 格式）和反序列化（從 JSON 格式轉回 Encoding 類型）。讓我們逐行解釋這個類別的內容：

1. Read 方法（反序列化）
public override Encoding Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    => Encoding.GetEncoding(reader.GetString());  // 直接返回 Encoding
功能：這個方法處理 JSON 反序列化過程，將 JSON 字串中的編碼名稱轉換為對應的 Encoding 類型。

詳細解釋：

reader.GetString()：從 JSON 中讀取一個字串（該字串應該是編碼名稱，像是 "UTF-8" 或 "ASCII" 等）。
Encoding.GetEncoding()：根據從 JSON 中讀取的編碼名稱，返回對應的 Encoding 類型對象。
例子：假設 JSON 中有這樣一個屬性：
{
  "encoding": "UTF-8"
}
當反序列化時，這個方法會將 "UTF-8" 轉換為對應的 Encoding.UTF8。

2. Write 方法（序列化）
public override void Write(Utf8JsonWriter writer, Encoding value, JsonSerializerOptions options)
    => writer.WriteStringValue(value.EncodingName); // 只寫入 Encoding 的名稱
功能：這個方法處理 JSON 序列化過程，將 Encoding 類型轉換成它的編碼名稱字串，然後將這個字串寫入 JSON。

詳細解釋：

value.EncodingName：Encoding 類型有一個 EncodingName 屬性，它會返回該 Encoding 的名稱字串（例如，Encoding.UTF8.EncodingName 會返回 "UTF-8"）。
writer.WriteStringValue()：將這個編碼名稱字串寫入到 JSON 中。
例子：假設我們有一個 Encoding 對象 Encoding.UTF8，這段方法會將其序列化為字串 "UTF-8"。

3. 總結
這個 JsonEncodingConverter 實現了如何將 Encoding 類型與 JSON 進行互轉：

反序列化（Read）：從 JSON 中讀取編碼名稱字串並轉換為對應的 Encoding 對象。
序列化（Write）：將 Encoding 對象的編碼名稱寫入 JSON 中。
例子：如何在程式中使用
如果你想要在 JSON 序列化/反序列化過程中使用這個自定義的轉換器，你需要將它註冊到 JsonSerializerOptions：
var options = new JsonSerializerOptions();
options.Converters.Add(new JsonEncodingConverter());

var model = JsonSerializer.Deserialize<VM_Mail>(file, options);  // 在反序列化時會使用自定義轉換器
這樣，當 VM_Mail 類別中包含 Encoding 類型的屬性時，JsonEncodingConverter 會被用來處理該屬性的序列化和反序列化。 
```
- ReadOnlySpan<byte> 是 ref struct，這類型不適合在 JSON 中進行序列化或反序列化。
- System.Text.Json 嘗試對 Encoding 的所有屬性進行序列化時，遇到這些不支持的類型就會報錯。
- System.Text.Encoding 的內部屬性（例如 Preamble）是 ReadOnlySpan<byte> 類型。
https://stackoverflow.com/questions/78347638/jsonconverterattribute-is-not-used-by-jsonserializer
    
官方公告的System.Text.Json在.Net 8的改變
https://github.com/dotnet/docs/issues/37041
    
```
1. 以前（.NET 7）
在 .NET 7 中，System.Text.Json 的反射序列化器採用了「延遲加載」(lazy loading) 的方式來解析物件的屬性元資料。這意味著，如果 POCO（Plain Old CLR Object）包含某些不支援的屬性類型，反序列化可能仍會成功，只要該屬性在實際序列化/反序列化的過程中沒有被觸及。也就是說，即使 POCO 中有不支援的屬性，JSON 並沒有綁定到這些屬性時，反序列化依然能正常運行。

舉例來說，以下代碼在 .NET 7 中不會引發錯誤：
public class MyPoco
{
    public int Value { get; set; }
    public NestedValue Unsupported { get; set; }
}

public class NestedValue
{
    public ReadOnlySpan<byte> Span => Array.Empty<byte>();
}
即使 ReadOnlySpan<byte> 類型不是支持的序列化類型，這段代碼仍能成功反序列化 JSON 字串。因為在反序列化過程中，Unsupported 屬性沒有被觸及，所以不會引發錯誤。

2. 現在（.NET 8）
在 .NET 8 中，這種「延遲加載」的方式被移除了，改為「提前解析」(eager loading) 所有屬性元資料。這意味著在序列化和反序列化過程中，所有屬性都會被立即解析，即便它們最終沒有被實際使用。

這個變更的目的，是為了更好地支持多個解析器（如源生成器）結合使用，這樣可以提高序列化的效能。但這樣的改變也帶來了兼容性問題。比如，在 .NET 8 中，當你嘗試反序列化包含不支援的屬性類型（如 ReadOnlySpan<byte>）的 POCO 時，會引發以下錯誤：
System.InvalidOperationException: The type 'System.ReadOnlySpan`1[System.Byte]' of property 'Span' on type 'NestedValue' is invalid for serialization or deserialization because it is a pointer type, is a ref struct, or contains generic parameters that have not been replaced by specific types.
這是因為 ReadOnlySpan<byte> 類型屬性不再被忽略，並且不再支持序列化或反序列化，因為它是一個「指標類型」(pointer type) 或是「引用結構」(ref struct)，這是無法被直接序列化的。

3. 原因
這個改變是因為 .NET 8 引入了新的要求，特別是對於快速序列化支持。這需要對類型圖進行提前分析，以便能夠快速序列化。在這樣的變更下，原來的延遲加載行為不再能支持這樣的需求，並且會導致類似上述的錯誤。

4. 解決方案
官方提供了幾種解決方式來處理這個問題：

解決方案 1：移除不支援的屬性
如果某個屬性不支持序列化，最簡單的解決方式就是將該屬性移除：

public class MyPoco
{
    public int Value { get; set; }
    public NestedValue Unsupported { get; set; }  // 移除這行
}
解決方案 2：自定義轉換器 (JsonConverter)
如果移除屬性不可行，你可以為不支援的屬性類型編寫一個自定義的 JsonConverter，來處理這些屬性的序列化和反序列化。例如，對於 ReadOnlySpan<byte> 類型，可以寫一個自定義的轉換器來進行處理。

解決方案 3：使用 JsonIgnore 屬性
如果你不想讓某些屬性參與序列化，最簡單的方法就是給這些屬性加上 JsonIgnore 屬性，這樣它們就不會被序列化或反序列化了。

public class MyPoco
{
    public int Value { get; set; }

    [JsonIgnore]
    public NestedValue Unsupported { get; set; }  // 加上 JsonIgnore，讓這個屬性不被序列化
}
5. 額外寫一個類別來處理
根據你遇到的問題，為了能正確處理不支援的類型（如 ReadOnlySpan<byte>），你需要編寫一個類別（如 JsonEncodingConverter）來處理這些特殊的序列化需求。這是因為在 .NET 8 中，這些不支援的類型（例如 ReadOnlySpan<byte>）不再被隱式忽略，而是會引發錯誤。這就需要你顯式處理這些類型，才能確保它們能夠正確地序列化或反序列化。

如果你不希望手動處理這些屬性類型，那麼使用 JsonIgnore 是最簡單的解決方案。

總結：
舊的行為：延遲加載，某些不支援的屬性不會影響反序列化。
新的行為：提前解析所有屬性，這導致不支援的屬性類型會引發錯誤。
解決方案：可以移除不支援的屬性，使用自定義的轉換器，或者使用 JsonIgnore 屬性來避免序列化這些屬性。
```
---
    
[.Net 8 Breaking Changes](https://learn.microsoft.com/en-us/dotnet/core/compatibility/8.0)
    
[黑暗執行緒 - .Net 8 升級 改變 ActivatorUtilitiesConstructor](https://blog.darkthread.net/blog/net8-issue-activatorutilitiesconstructor/)