2022年7月19日 星期二

C# : 在多執行緒下分別對於的集合型別物件存取 - 使用 foreach 與 for

在多執行緒下分別對於的集合型別物件存取 - 使用 foreach 與 for

最近收到一份範例程式碼,如下所示

在這份程式碼中,將會看到在 Main 方法內,分別建立了兩個執行緒,第一個執行緒將會執行 Run1 方法,在此方法內將會執行 while (true) { list.Add("A"); } 敘述,應該是要表示這裡會不斷地加入新的物件到 List 這個集合物件內,而該 List<string> 集合物件將會是一個靜態資源,同時可以被這兩個執行緒來存取。

在第二個執行緒所指定的委派方法內,將會呼叫 Run2 這個方法,在其方法內將會執行這樣的程式碼 while (true) { foreach (var item in list) { } list.Clear(); } ,透過 foreach 來逐一存取這個 List<string> 集合物件內的每個字串,雖然,在迴圈內沒有執行其他額外程式碼,我的猜想這應該是要針對第一個執行緒所新增進來的集合物件內的個別項目,逐一進行處理,最後,在 foreach 迴圈之外,將會存取這個 List<string> 多執行緒下共享的集合物件資源,透過 Clear() 方法,將這個集合物件內的項目予以清除。

順便再來說明 Run3 這個方法 (雖然,在第二個執行緒內,呼叫 Run3(); 的敘述被註解起來),其程式碼為 while (true) { for (int i = 0; i < list.Count; i++) { var b = list[i]; } list.Clear(); } ,在這個無窮迴圈內,同樣的會使用 for 敘述來存取該集合物件內的每個項目,不過,多的一個將每個項目指定到一個區域變數內,不過,這樣的敘述,不影響整個問題,在離開迴圈之後,同樣的會清除該集合物件的所有項目。

提交這份範例程式碼的人提出一個疑問,當在第二個執行緒執行的時候,若呼叫 Run2 這個方法,將會產生例外異常,也就是使用 foreach 敘述,似乎無法在多執行緒運行,若將第二個執行緒內的要呼叫的方法改為 Run3 這個方法,將可以正常運作。

namespace ConsoleApp5
{
    internal class Program
    {
        static List<string> list = new List<string>();
        static void Main(string[] args)
        {

            new Thread(() =>
            {
                Run1();
            })
            { IsBackground = true }.Start();
            new Thread(() =>
            {
                Run2();
                //Run3();
            })
            { IsBackground = true }.Start();

            Console.ReadKey();
        }


        static void Run1()
        {
            while (true)
            {
                list.Add("A");
            }
        }
        static void Run2()
        {
            while (true)
            {
                foreach (var item in list)
                {

                }
                list.Clear();
            }
        }

        static void Run3()
        {
            while (true)
            {
                for (int i = 0; i < list.Count; i++)
                {
                    var b = list[i];
                }
                list.Clear();
            }
        }
    }
}

為了方便解釋且不影響執行結果,在這裡把上述的程式碼改寫成為底下內容

底下程式碼除了做些簡化的工作,另外,對於在新增、讀取、清除這個共用集合物件的時候,同樣的會在螢幕上輸出相對應的 A、R、C 這樣的文字,有了這些輸出訊息文字,更可以觀察到一些問題與現象。

在這裡,同樣的還是先將 #region 執行緒 3 ... #endregion 之間的程式碼先註解起來。

namespace MultipleThreadAccessCollection
{
    using static System.Console;

    internal class Program
    {
        static List<string> list = new List<string>();
        static void Main(string[] args)
        {
            #region 執行緒 1
            new Thread(() =>
            {
                while (true)
                { list.Add("A"); Write("A"); }
            })
            { IsBackground = true }.Start();
            #endregion

            #region 執行緒 2
            new Thread(() =>
            {
                while (true)
                    foreach (var item in list) { Write("R"); }
                list.Clear();
                Write("C");
            })
            { IsBackground = true }.Start();
            #endregion

            #region 執行緒 3
            //new Thread(() =>
            //{
            //    while (true)
            //        for (int i = 0; i < list.Count; i++)
            //        { var b = list[i]; Write("R"); }
            //    list.Clear();
            //    Write("C");
            //})
            //{ IsBackground = true }.Start();
            #endregion

            Console.ReadKey();
        }
    }
}

完成重新建構這個測試專案之後,便可以開始執行這個專案

很不幸的,不論執行幾次,都會遇到例外異常拋出,在 Visual Studio 工具內,將會看到這這樣的畫面

從 [未處理的例外狀況] 對話窗內容,可以看到現在遇到的問題將會是產生了一個 Collection was modified; enumeration operation may not execute 問題,從字面意義上來看,這應該是當在進行 foreach 敘述的時候,對於所提供的列舉集合,在此將是 List<string>,若這個 foreach 迴圈尚未完成的話,是不允許針對這個列舉集合物件進行項目的異動。

底下將會是這個例外異常的詳細說明內容

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
   at System.Collections.Generic.List`1.Enumerator.MoveNext()
   at MultipleThreadAccessCollection.Program.<>c.<Main>b__1_1() in C:\Vulcan\Github\CSharp2022\MultipleThreadAccessCollection\MultipleThreadAccessCollection\Program.cs:line 23
   at System.Threading.Thread.StartCallback()

現在來觀察執行結果,這裡將會截圖三次的執行結果,如下面圖片所示

從這些圖片可以看到,兩個執行緒確實都有在運作,對於第一個執行緒確實不斷的新增項目到集合物件內,但是,對於第二個執行緒而言,僅能夠第一個進行列舉讀取這個集合物件的項目(這裡有特別將 R 這個文字反白標示出來),一旦要進行第二次讀取列舉集合物件內的項目時候,可以看出,因為第一個執行緒不斷的在新增新的項目,這時將導致集合物件內的項目數量增加了,因此,便會造成 foreach 敘述無法繼續運作下去。

現在將 #region 執行緒 2 ... #endregion 內的敘述註解起來,並且將 #region 執行緒 3 ... #endregion 內的所有程式碼解除註解

底下的螢幕截圖,將會是執行結果

整體執行過程中沒有發生拋出例外異常的問題

分析

關於之前提到的例外異常,可以使用底下的程式碼再度來進行測試

這裡將會產生 100 個項目 Item 到 List<string> 中,此時,將會在另外一個執行緒內,使用 foreach 敘述,每隔 0.5 秒鐘來讀取一個項目出來,若使用者沒有按下任何一個按鍵,則一切相安無事,可是,只要使用者按下任何一個按鍵,將會導致在主執行緒內,開始再度新增 100 個項目到集合物件 List 內,現在,將會得到同樣的 Collection was modified; enumeration operation may not execute 例外異常訊息,這樣有比較清楚造成這個例外異常的發生原因了吧

namespace ConsoleApp6
{
    internal class Program
    {
        static List<string> list = new List<string>();
        static void Main(string[] args)
        {
            for (int i = 0; i < 100; i++)
            {
                list.Add("A");
            }
            ThreadPool.QueueUserWorkItem(_ =>
            {
                foreach (var item in list)
                {
                    Thread.Sleep(500);
                    Console.Write(".");
                }
                list.Clear();
            });

            Console.WriteLine("Press any key for continuing...");
            Console.ReadKey();

            for (int i = 0; i < 100; i++)
            {
                list.Add("A");
            }

            Console.WriteLine("Press any key for continuing...");
            Console.ReadKey();
        }
    }
}

還有一個問題那就是,雖然取消了執行緒2的執行,改採執行緒3,並且執行結果似乎沒有看到問題,現在來看看執行緒3的程式碼有那些問題

#region 執行緒 3
new Thread(() =>
{
    while (true)
        for (int i = 0; i < list.Count; i++)
        { var b = list[i]; Write("R"); }
    list.Clear();
    Write("C");
})
{ IsBackground = true }.Start();
#endregion

在執行緒3的委派方法內存在一個很大的問題,那就是若有這樣的時機發生,執行到 list.Clear(); 敘述,將會導致集合物件內的整個項目都會清空,可是,當在執行這個動作的同時,執行緒1同時也有新增項目到集合物件內,極有可能會造成這個項目尚未被執行緒3處理,就直接被清除掉了,若發生這樣的問題,將會是很難除錯的,因為,使用者或者身為開發者的你,也無法有效地重現出當時發生問題情況,問題是很難抓出來的。

這樣的多執行緒程式碼將會造成一個現象,那就是這個程式碼不具備 [執行緒安全 Thread Safety] 特性,因為,在多執行緒環境執行下,這組程式的執行結果將會是不可預期的。

最簡單的解法那就是當要寫入或者刪除共用資源,這裡將會是 list 這個集合物件,要進行鎖定的動作,因此,當執行 list.Clear(); 敘述的時候,要確保其他執行緒是不能夠來進行寫入動作的,當然,加入的鎖定或者其他同步機制,將會造成執行效能有所損失。

因此,只要是在多執行緒程式下,對於要存取共用的集合物件,可以參考這篇文章 安全執行緒集合。在這篇文章中,開宗明義提到了:.NET Framework 4 引進了 System.Collections.Concurrent 命名空間,其中包含數個兼具安全執行緒與調整能力的集合類別。因此,建議還是採用這樣的類別來進行多執行緒下的集合物件存取。

對於這裡提到的應用,可以先參考微軟建議的 如何:實作生產者-取用者資料流程模式 做法,或者可以在網路上搜尋其他人寫好的類似套件,直接使用這些套件來進行多執行程式設計,因為這些套件是具有執行緒安全的特性,所以,透過使用這些套件或者微軟建議的做法,你開發出來的程式碼,也會是具有執行緒安全的特性的。







2022年7月17日 星期日

C# : 在開發方案中,加入自訂的 NuGet 套件來源,無需透過 工具 > 選項 > NuGet 來設定

在開發方案中,加入自訂的 NuGet 套件來源,無需透過 工具 > 選項 > NuGet 來設定

在上一篇文章 C# : 將 EF Core 的資料模型類別庫,打包成為 NuGet 套件,並且上傳到公開 NuGet 伺服器上 中,說明到如何自己設計一個類別庫專案,並且將其打包成為一個 NuGet 套件,接著,將其發佈到私有的 NuGet 伺服器上,最後,透過在 Visual Studio 上加入這個私有的 NuGet 套件來源,成功的安裝這些套件到專案內來進行開發。

有些時候,在進行專案開發需要使用到一些 NuGet 套件,然而,有些時候需要參考到非 https://www.nuget.org/ 官方網站的套件,例如,許多套件是自己團隊或者公司開發的(或者,像是參加我的課程,也會用到我自行開發的 NuGet 套件,方便進行開發練習),並且發佈到其他非 NuGet 的網站上,此時,若要使用這些套件,或者要打開這類專案,都需要事先在 Visual Studio 上進行宣告與設定,否則,將無法建置此類型的專案,因為,無法將這些套件從網路下載下來。

在進行此篇文章動手練習之前

  • 請先打開 Visual Studio 2022

  • 點選功能表 [工具] > [選項]

  • 在 [選項] 對話窗中,展開 [NuGet 套件管理員] > [套件來源] 節點

  • 確認此時對話窗的右方套件來源清單中,是安裝 Visual Studio 2022 預設的選項,沒有其他自行額外追加的項目,若有這些額外新增的項目,請先刪除掉

    Visual Stdio 工具 選項 nuget

建立測試用的主控台應用程式專案

  • 打開 Visual Studio 2022

  • 點選右下方的 [建立新的專案] 按鈕

  • 選擇一個 [主控台應用程式] 的專案範本

  • 點選右下方的 [下一步] 按鈕

  • 在 [設定新的專案] 對話窗內,在 [專案名稱] 欄位中,輸入 CustomNuGet

  • 點選右下方的 [下一步] 按鈕

  • 在 [其他資訊] 對話窗中

  • 取消 [Do not use top-level statements] 這個 checkbox 檢查盒的勾選

  • 點選右下方的 [建立] 按鈕

  • 當這個專案建立完成後

  • 滑鼠右擊 [解決方案] 節點

  • 點選 [加入] > [新增項目]

  • 在 [新增項目 - 方案項目] 對話窗內

  • 在中間清單區域,選擇 [XML] 項目

  • 在下方名稱欄位內,輸入 NuGet.config

  • 點選右下方 [新增] 按鈕

  • 當 [NuGet.config] 編輯視窗出現之後,便可以將底下的宣告內容輸入到這個檔案內

<?xml version="1.0" encoding="utf-8"?>
<configuration>
	<packageSources>
		<add key="Vulcan 教學課程 NuGet 套件" 
        value="https://www.myget.org/F/course-lab/api/v3/index.json" />
	</packageSources>
</configuration>

為了要讓這些新加入的內容生效,建議你將這個專案關閉起來,又或者關閉 Visual Studio 2022`,重新開啟 Visual Studio 2022 與 這個練習專案

驗證可以使用專案特定的 NuGet 套件來源

專案重新開啟之後,請依序進行底下操作,確認剛剛的設定可以正確完成無誤。

在這裡將會要透過剛剛新加入的 [Vulcan 教學課程 NuGet 套件] 套件來源中的一個 Entity Framework Core 課程練習用的套件,這個套件提供了 Entity Framework Core 反向工程的模型與 Code First 代碼優先的模型,有了這些模型便可以快速、方便的進行 Entity Framework Core 課程中的各項練習。

  • 滑鼠右擊該專案的 [相依性] 節點

  • 點選 [管理 NuGet 套件] 選項

  • 當 [NuGet: CustomNuGet] 視窗出現後

  • 點選該視窗右上方的 [套件來源] 右邊下拉選單控制項

  • 從下拉選單清單中,選擇 [Vulcan 教學課程 NuGet 套件] 這個項目

  • 點選該視窗左上方的 [瀏覽] 標籤頁次

  • 沒意外的話,將會看到下面截圖的內容,在這個 NuGet 套件來源中,存在著底下的項目

  • 請選擇 [EFCore.Course] 這個套件,並且安裝起來

  • 底下是安裝這個套件的相關輸出文字

正在還原 C:\Vulcan\Projects\CustomNuGet\CustomNuGet\CustomNuGet.csproj 的封裝...
  GET https://www.myget.org/F/course-lab/api/v3/flatcontainer/efcore.course/index.json
  GET https://api.nuget.org/v3-flatcontainer/efcore.course/index.json
  OK https://www.myget.org/F/course-lab/api/v3/flatcontainer/efcore.course/index.json 295 毫秒
  GET https://www.myget.org/F/course-lab/api/v3/flatcontainer/efcore.course/1.0.0/efcore.course.1.0.0.nupkg
  NotFound https://api.nuget.org/v3-flatcontainer/efcore.course/index.json 810 毫秒
  OK https://www.myget.org/F/course-lab/api/v3/flatcontainer/efcore.course/1.0.0/efcore.course.1.0.0.nupkg 1268 毫秒
已從具有內容雜湊 UYMB5nZ2H2vdU53P7L/orsrojzPkdK6oYTDZltk3g/MpCmLhZeRw1Y6MCU5t/djYixEs91HZTXkCsogdi3PN2w== 的 https://www.myget.org/F/course-lab/api/v3/index.json 安裝 EFCore.Course 1.0.0。
正在安裝 NuGet 套件 EFCore.Course 1.0.0。
正在產生 MSBuild 檔案 C:\Vulcan\Projects\CustomNuGet\CustomNuGet\obj\CustomNuGet.csproj.nuget.g.props。
正在將資產檔案寫入磁碟。路徑: C:\Vulcan\Projects\CustomNuGet\CustomNuGet\obj\project.assets.json
已成功將 'EFCore.Course 1.0.0' 安裝到 CustomNuGet
已成功將 'Microsoft.CSharp 4.5.0' 安裝到 CustomNuGet
已成功將 'Microsoft.Data.SqlClient 2.1.4' 安裝到 CustomNuGet
已成功將 'Microsoft.Data.SqlClient.SNI.runtime 2.1.1' 安裝到 CustomNuGet
已成功將 'Microsoft.EntityFrameworkCore 6.0.6' 安裝到 CustomNuGet
已成功將 'Microsoft.EntityFrameworkCore.Abstractions 6.0.6' 安裝到 CustomNuGet
已成功將 'Microsoft.EntityFrameworkCore.Analyzers 6.0.6' 安裝到 CustomNuGet
已成功將 'Microsoft.EntityFrameworkCore.Relational 6.0.6' 安裝到 CustomNuGet
已成功將 'Microsoft.EntityFrameworkCore.SqlServer 6.0.6' 安裝到 CustomNuGet
已成功將 'Microsoft.Extensions.Caching.Abstractions 6.0.0' 安裝到 CustomNuGet
已成功將 'Microsoft.Extensions.Caching.Memory 6.0.1' 安裝到 CustomNuGet
已成功將 'Microsoft.Extensions.Configuration.Abstractions 6.0.0' 安裝到 CustomNuGet
已成功將 'Microsoft.Extensions.DependencyInjection 6.0.0' 安裝到 CustomNuGet
已成功將 'Microsoft.Extensions.DependencyInjection.Abstractions 6.0.0' 安裝到 CustomNuGet
已成功將 'Microsoft.Extensions.Logging 6.0.0' 安裝到 CustomNuGet
已成功將 'Microsoft.Extensions.Logging.Abstractions 6.0.0' 安裝到 CustomNuGet
已成功將 'Microsoft.Extensions.Options 6.0.0' 安裝到 CustomNuGet
已成功將 'Microsoft.Extensions.Primitives 6.0.0' 安裝到 CustomNuGet
已成功將 'Microsoft.Identity.Client 4.21.1' 安裝到 CustomNuGet
已成功將 'Microsoft.IdentityModel.JsonWebTokens 6.8.0' 安裝到 CustomNuGet
已成功將 'Microsoft.IdentityModel.Logging 6.8.0' 安裝到 CustomNuGet
已成功將 'Microsoft.IdentityModel.Protocols 6.8.0' 安裝到 CustomNuGet
已成功將 'Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0' 安裝到 CustomNuGet
已成功將 'Microsoft.IdentityModel.Tokens 6.8.0' 安裝到 CustomNuGet
已成功將 'Microsoft.NETCore.Platforms 3.1.0' 安裝到 CustomNuGet
已成功將 'Microsoft.Win32.Registry 4.7.0' 安裝到 CustomNuGet
已成功將 'Microsoft.Win32.SystemEvents 4.7.0' 安裝到 CustomNuGet
已成功將 'System.Collections.Immutable 6.0.0' 安裝到 CustomNuGet
已成功將 'System.Configuration.ConfigurationManager 4.7.0' 安裝到 CustomNuGet
已成功將 'System.Diagnostics.DiagnosticSource 6.0.0' 安裝到 CustomNuGet
已成功將 'System.Drawing.Common 4.7.0' 安裝到 CustomNuGet
已成功將 'System.IdentityModel.Tokens.Jwt 6.8.0' 安裝到 CustomNuGet
已成功將 'System.Runtime.Caching 4.7.0' 安裝到 CustomNuGet
已成功將 'System.Runtime.CompilerServices.Unsafe 6.0.0' 安裝到 CustomNuGet
已成功將 'System.Security.AccessControl 4.7.0' 安裝到 CustomNuGet
已成功將 'System.Security.Cryptography.Cng 4.5.0' 安裝到 CustomNuGet
已成功將 'System.Security.Cryptography.ProtectedData 4.7.0' 安裝到 CustomNuGet
已成功將 'System.Security.Permissions 4.7.0' 安裝到 CustomNuGet
已成功將 'System.Security.Principal.Windows 4.7.0' 安裝到 CustomNuGet
已成功將 'System.Text.Encoding.CodePages 4.7.0' 安裝到 CustomNuGet
已成功將 'System.Windows.Extensions 4.7.0' 安裝到 CustomNuGet
執行 NuGet 動作花費了 196 毫秒
經過時間: 00:00:03.3675770
========== 已完成 ==========

經過時間: 00:00:00.0296857
========== 已完成 ==========
  • 現在,這個主控台專案內,已經有可以存取後端資料庫的 Entity Framework Core Model 模型了

  • 打開 [Program.cs] 檔案,修改成為如下程式碼

using DBReverse;
using Microsoft.EntityFrameworkCore;

namespace ConsoleApp4
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            SchoolContext context = new SchoolContext();
            var people = await context.People
                .OrderBy(x => x.LastName)
                .ThenBy(x => x.FirstName)
                .Where(x => x.LastName == "Li")
                .ToListAsync();

            var foo = context.People
                .OrderBy(x => x.LastName);
            foo = foo.ThenBy(z => z.FirstName);
            var bar = foo.Where(x => x.LastName == "Li");
            var bar1 = bar.ToList();
            foreach (var item in people)
            {
                Console.WriteLine($"人員:{item.LastName} {item.FirstName}");
            }
        }
    }
}
  • 執行這個專案,並且查看結果是否為

人員:Li Yan 





2022年7月15日 星期五

C# : 將 EF Core 的資料模型類別庫,打包成為 NuGet 套件,並且上傳到公開 NuGet 伺服器上

 

C# : 將 EF Core 的資料模型類別庫,打包成為 NuGet 套件,並且上傳到公開 NuGet 伺服器上

在這篇文章,將會延續上一篇 EF Core : 使用資料庫反向工程,取得 EF Core 的資料模型 的開發結果,準備要將已經開發好的一個 Entity Framework Core 模型類別庫,打包成為 NuGet 套件,不過,在這裡將不會把這個 NuGet 套件上傳到 www.nuget.org 網站上,而是會上傳到另外一個方便管理的 MyGet 伺服器上

將類別庫專案設定可以產生 NuGet 套件

  • 再度開啟上篇文章所建立的 [DBReverse] 類別庫專案

  • 滑鼠右擊 [DBReverse] 專案節點

  • 從彈出功能表中,點選 [屬性] 選項

  • 此時,將會看到該專案的 [屬性] 視窗顯示在螢幕上

  • 請點選 [屬性] 視窗左方的清單選項 [套件] > [一般]

    Visual Studio 專案套件屬性

  • 請勾選 [在建置時產生 NuGet 套件] 下方的 Checkbox 檢查盒。

  • 對於下方的其他屬性設定值,可以依照自己的需要來做調整

  • 底下將會是這個類別庫專案的設定內容

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <Authors>Vulcan Lee</Authors>
    <Product>EF Core 動手實作課程</Product>
    <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.7" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.7">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

</Project>
  • 切換到 Release 模式下,建置這個類別庫專案

  • 現在,可以從 Visual Studio 2022 中,看到 [bin] > [Release] 資料夾下有個 [DBReverse.1.0.0.nupkg] 檔案產生出來

上ˇ傳到 MyGet 伺服器上

  • 打開 https://www.myget.org/ 網站,並且登入到這個網站內

  • 在首頁上,將會看到右方有底下的畫面

    MyGet Feed

  • 點選 [NEW FEED +] 這個按鈕

  • 當出現了 [Create your MyGet feed] 畫面後

  • 在 [Your feed URL] 欄位內輸入 efcore-reverse-engineering

  • 在 [Your feed description] 欄位中輸入 使用 Entity Framework Core 反向工程建立的模型與DbContext類別庫

  • 捲動該網頁到最下方,點選 [CREARE FEED] 按鈕

  • 當出現 [efcore-reverse-engineering - Packages] 網頁畫面

  • 請點選 [ADD PACKAGE] > [NuGet Package] 按鈕

  • 當 [Add Package] 對話窗出現的時候,切換到 [From an uploaded package] 標籤頁次

  • 點選 [選擇檔案] 按鈕,找到剛剛產生的 NuGet 檔案 - [DBReverse.1.0.0.nupkg]

  • 最後點選右下方的 [ADD] 綠色按鈕,上傳這個 NuGet 套件檔案

  • 點選左邊的 [FEED DETAILS] 連結

  • 找到 [Your NuGet V3 feed URL (Visual Studio 2015+)] 欄位

  • 將該欄位的內容值複製到剪貼簿內,此時的欄位值為 https://www.myget.org/F/efcore-reverse-engineering/api/v3/index.json

開始引用這個 MyGet 上的公開套件

  • 打開 Visual Studio 2022

  • 點選右下方的 [建立新的專案] 按鈕

  • 選擇一個 [主控台應用程式] 的專案範本

  • 點選右下方的 [下一步] 按鈕

  • 在 [設定新的專案] 對話窗內,使用預設值即可

  • 點選右下方的 [下一步] 按鈕

  • 在 [其他資訊] 對話窗中

  • 取消 [Do not use top-level statements] 這個 checkbox 檢查盒的勾選

  • 點選右下方的 [建立] 按鈕

  • 當這個專案建立完成後,接下來要設定飲用者個 NuGet 套件

  • 點選功能表 [工具] > [選項]

  • 此時, [選項] 對話窗將會出現

  • 點選左邊清單的 [NuGet 套件管理員] > [套件來源]

  • 點選右上方的綠色加號按鈕

  • 此時,中間的 [套件來源] 清單中將會多了一筆紀錄

  • 請在下方的 [名稱] 欄位輸入 EF Core 反向工程產生的模型

  • 請在下方的 [來源] 欄位輸入 https://www.myget.org/F/efcore-reverse-engineering/api/v3/index.json

  • 點選右下方的 [更新] 按鈕

  • 點選右下方的 [確定] 按鈕,完成加入一個 NuGet 來源設定

  • 滑鼠右擊該專案內的 [相依性] 節點

  • 從彈出功能表中,點選 [管理 NuGet 套件] 選項

  • 當 NuGet 套件管理視窗出現之後

  • 在該視窗的右上方,將會看到 [套件來源] 文字

  • 點選該文字右方的下拉選單,選擇 [EF Core 反向工程產生的模型] 這個 NuGet 套件來源

  • 現在可以點選這個 NuGet 套件管理視窗左上方的 [瀏覽] 標籤頁次

    若沒有看到下圖畫面,請再度切換 [套件來源] 為 [EF Core 反向工程產生的模型]

  • 在現在的視窗內,將會看到 [DBReverse] 套件出現

  • 點選這個套件,並且安裝到這個專案內

  • 底下是輸出文字內容

正在還原 C:\Vulcan\Projects\ConsoleApp4\ConsoleApp4\ConsoleApp4.csproj 的封裝...
  GET https://api.nuget.org/v3-flatcontainer/dbreverse/index.json
  NotFound https://api.nuget.org/v3-flatcontainer/dbreverse/index.json 218 毫秒
  GET https://www.myget.org/F/course-lab/api/v3/flatcontainer/dbreverse/index.json
  NotFound https://www.myget.org/F/course-lab/api/v3/flatcontainer/dbreverse/index.json 263 毫秒
  GET https://www.myget.org/F/efcore-reverse-engineering/api/v3/flatcontainer/dbreverse/index.json
  OK https://www.myget.org/F/efcore-reverse-engineering/api/v3/flatcontainer/dbreverse/index.json 256 毫秒
  GET https://www.myget.org/F/efcore-reverse-engineering/api/v3/flatcontainer/dbreverse/1.0.0/dbreverse.1.0.0.nupkg
  OK https://www.myget.org/F/efcore-reverse-engineering/api/v3/flatcontainer/dbreverse/1.0.0/dbreverse.1.0.0.nupkg 1230 毫秒
已從具有內容雜湊 skICCYIMQu3qIpAssDu4OdxIfgUwjM8Q9B/WJeNr2D22466yO/C9flV/R01aYnWYNQskpn7POfVwI1XZfkIlYA== 的 https://www.myget.org/F/efcore-reverse-engineering/api/v3/index.json 安裝 DBReverse 1.0.0。
正在安裝 NuGet 套件 DBReverse 1.0.0。
正在產生 MSBuild 檔案 C:\Vulcan\Projects\ConsoleApp4\ConsoleApp4\obj\ConsoleApp4.csproj.nuget.g.props。
正在將資產檔案寫入磁碟。路徑: C:\Vulcan\Projects\ConsoleApp4\ConsoleApp4\obj\project.assets.json
已成功將 'DBReverse 1.0.0' 安裝到 ConsoleApp4
已成功將 'Microsoft.CSharp 4.5.0' 安裝到 ConsoleApp4
已成功將 'Microsoft.Data.SqlClient 2.1.4' 安裝到 ConsoleApp4
已成功將 'Microsoft.Data.SqlClient.SNI.runtime 2.1.1' 安裝到 ConsoleApp4
已成功將 'Microsoft.EntityFrameworkCore 6.0.7' 安裝到 ConsoleApp4
已成功將 'Microsoft.EntityFrameworkCore.Abstractions 6.0.7' 安裝到 ConsoleApp4
已成功將 'Microsoft.EntityFrameworkCore.Analyzers 6.0.7' 安裝到 ConsoleApp4
已成功將 'Microsoft.EntityFrameworkCore.Relational 6.0.7' 安裝到 ConsoleApp4
已成功將 'Microsoft.EntityFrameworkCore.SqlServer 6.0.7' 安裝到 ConsoleApp4
已成功將 'Microsoft.Extensions.Caching.Abstractions 6.0.0' 安裝到 ConsoleApp4
已成功將 'Microsoft.Extensions.Caching.Memory 6.0.1' 安裝到 ConsoleApp4
已成功將 'Microsoft.Extensions.Configuration.Abstractions 6.0.0' 安裝到 ConsoleApp4
已成功將 'Microsoft.Extensions.DependencyInjection 6.0.0' 安裝到 ConsoleApp4
已成功將 'Microsoft.Extensions.DependencyInjection.Abstractions 6.0.0' 安裝到 ConsoleApp4
已成功將 'Microsoft.Extensions.Logging 6.0.0' 安裝到 ConsoleApp4
已成功將 'Microsoft.Extensions.Logging.Abstractions 6.0.0' 安裝到 ConsoleApp4
已成功將 'Microsoft.Extensions.Options 6.0.0' 安裝到 ConsoleApp4
已成功將 'Microsoft.Extensions.Primitives 6.0.0' 安裝到 ConsoleApp4
已成功將 'Microsoft.Identity.Client 4.21.1' 安裝到 ConsoleApp4
已成功將 'Microsoft.IdentityModel.JsonWebTokens 6.8.0' 安裝到 ConsoleApp4
已成功將 'Microsoft.IdentityModel.Logging 6.8.0' 安裝到 ConsoleApp4
已成功將 'Microsoft.IdentityModel.Protocols 6.8.0' 安裝到 ConsoleApp4
已成功將 'Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0' 安裝到 ConsoleApp4
已成功將 'Microsoft.IdentityModel.Tokens 6.8.0' 安裝到 ConsoleApp4
已成功將 'Microsoft.NETCore.Platforms 3.1.0' 安裝到 ConsoleApp4
已成功將 'Microsoft.Win32.Registry 4.7.0' 安裝到 ConsoleApp4
已成功將 'Microsoft.Win32.SystemEvents 4.7.0' 安裝到 ConsoleApp4
已成功將 'System.Collections.Immutable 6.0.0' 安裝到 ConsoleApp4
已成功將 'System.Configuration.ConfigurationManager 4.7.0' 安裝到 ConsoleApp4
已成功將 'System.Diagnostics.DiagnosticSource 6.0.0' 安裝到 ConsoleApp4
已成功將 'System.Drawing.Common 4.7.0' 安裝到 ConsoleApp4
已成功將 'System.IdentityModel.Tokens.Jwt 6.8.0' 安裝到 ConsoleApp4
已成功將 'System.Runtime.Caching 4.7.0' 安裝到 ConsoleApp4
已成功將 'System.Runtime.CompilerServices.Unsafe 6.0.0' 安裝到 ConsoleApp4
已成功將 'System.Security.AccessControl 4.7.0' 安裝到 ConsoleApp4
已成功將 'System.Security.Cryptography.Cng 4.5.0' 安裝到 ConsoleApp4
已成功將 'System.Security.Cryptography.ProtectedData 4.7.0' 安裝到 ConsoleApp4
已成功將 'System.Security.Permissions 4.7.0' 安裝到 ConsoleApp4
已成功將 'System.Security.Principal.Windows 4.7.0' 安裝到 ConsoleApp4
已成功將 'System.Text.Encoding.CodePages 4.7.0' 安裝到 ConsoleApp4
已成功將 'System.Windows.Extensions 4.7.0' 安裝到 ConsoleApp4
執行 NuGet 動作花費了 247 毫秒
經過時間: 00:00:04.0392165
========== 已完成 ==========

經過時間: 00:00:00.0572992
========== 已完成 ==========
  • 打開 [Program.cs] 檔案,修改成為如下程式碼
using DBReverse;
using Microsoft.EntityFrameworkCore;

namespace ConsoleApp4
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            SchoolContext context = new SchoolContext();
            var people = await context.People
                .OrderBy(x => x.LastName)
                .ThenBy(x => x.FirstName)
                .Where(x => x.LastName == "Li")
                .ToListAsync();

            var foo = context.People
                .OrderBy(x => x.LastName);
            foo = foo.ThenBy(z => z.FirstName);
            var bar = foo.Where(x => x.LastName == "Li");
            var bar1 = bar.ToList();
            foreach (var item in people)
            {
                Console.WriteLine($"人員:{item.LastName} {item.FirstName}");
            }
        }
    }
}
  • 執行這個專案,並且查看結果是否為
人員:Li Yan