第三單元 管道與中間件

来源:https://www.cnblogs.com/xuyubing/archive/2023/12/06/17878998.html
-Advertisement-
Play Games

1. 什麼是中間件 在ASP.NET Core中,中間件(Middleware)是一個可以處理HTTP請求或響應的軟體管道。 ASP.NET Core中給中間件組件的定位是具有非常特定的用途。例如,我們可能有需要一個中間件組件驗證用戶,另一個中間件來處理錯誤,另一個中間件來提供靜態文件,如JavaS ...


1. 什麼是中間件

在ASP.NET Core中,中間件(Middleware)是一個可以處理HTTP請求或響應的軟體管道。 ASP.NET Core中給中間件組件的定位是具有非常特定的用途。例如,我們可能有需要一個中間件組件驗證用戶,另一個中間件來處理錯誤,另一個中間件來提供靜態文件,如JavaScript文件,CSS文件,圖片等等。

中間件就是用於組成應用程式管道來處理請求和響應的組件 。

中間件可以認為有兩個基本的職責:

  1. 選擇是否將請求傳遞給管道中的下一個中間件。

  2. 可以在管道中的下一個中間件前後執行一些工作。

我們使用這些中間件組件在ASP.NET Core中設置請求處理管道,而正是這管道決定瞭如何處理請求。 而請求管道是由Startup.cs文件中的Configure()方法進行配置,它也是應用程式啟動的一個重要部分。

// 配置http 請求管道,由運行時調用
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage(); // 渲染錯誤頁中間件
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }
    app.UseStaticFiles(); // 使用靜態文件中間件
    
    app.UseRouting(); // 使用路由中間件
​
    app.UseAuthorization(); // 使用授權中間件
    // 終端節點中間件
    app.UseEndpoints(endpoints =>
                     {
                         endpoints.MapControllerRoute(
                             name: "default",
                             pattern: "{controller=Home}/{action=Index}/{id?}");
                     });
}
 

 

2. 中間件處理流程-請求管道

.Net Core管道(pipeline)是什麼?

簡單來說,就是從發起請求到返回結果的一個過程,在.Net Core中這裡面的處理是由中間件(middleware)來完成。 管道機制解釋 用戶在發起請求後,系統會自動生成一個請求管道(request pipeline),在這個請求管道中,可以通過run、map和use方法來配置請求委托(RequestDelegate),而在單獨的請求委托中定義的可重用的類和並行的匿名方法即為中間件,也叫做中間件組件。當發起請求後,系統會創建一個請求管道,在這個管道中,每一個中間件都會按順序處理(可能會執行,也可能不會被執行,取決於具體的業務邏輯),等最後一個中間件處理完後,又會按照相反的方向返回最終的處理結果。

例如,如果您有一個日誌記錄中間件,它可能只是記錄請求的時間,它處理完畢後將請求傳遞給下一個中間件以進行進一步處理。

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(); // 添加控制器服務
}


public void Configure(IApplicationBuilder app, IWebHostEnvironment env,ILogger<Startup> logger)
{
    app.UseRouting();// 添加路由服務

    app.Use(next =>
            {
                logger.LogInformation("第1個中間件"); // 啟動時執行,只執行一次
                
                // 每次請求都會執行一次
                return async context =>
                {
                    logger.LogInformation("第1個中間件1-before");
                    await next(context);
                    logger.LogInformation("第1個中間件1-after");
                };
            });

    app.Use(next =>
            {
                logger.LogInformation("第2個中間件");// 啟動時執行,只執行一次

                 // 每次請求都會執行一次
                return async context =>
                {
                    logger.LogInformation("第2個中間件2-before");
                    await next(context);
                    logger.LogInformation("第2個中間件2-after");
                };
            });

    app.Use(next =>
            {
                logger.LogInformation("第3個中間件");// 啟動時執行,只執行一次

                 // 每次請求都會執行一次
                return async context =>
                {
                    logger.LogInformation("第3個中間件3-before");
                    await next(context);
                    logger.LogInformation("第3個中間件3-after");
                };
            });
    
    // 使用終端節點中間件會短路後面的中件間,所以,這個中間件最好放在最後
    app.UseEndpoints(endpoints =>
                     {
                         endpoints.MapControllerRoute(
                             name: "default",
                             pattern: "{controller=Home}/{action=Index}/{id?}");
                     });
}
info: Step3.Empty.Startup[0]
      第3個中間件
info: Step3.Empty.Startup[0]
      第2個中間件
info: Step3.Empty.Startup[0]
      第1個中間件

// 發起請求之後
info: Step3.Empty.Startup[0]
      第1個中間件1-before
info: Step3.Empty.Startup[0]
      第2個中間件2-before
info: Step3.Empty.Startup[0]
      第3個中間件3-before
info: Step3.Empty.Startup[0]
      第3個中間件3-after
info: Step3.Empty.Startup[0]
      第2個中間件2-after
info: Step3.Empty.Startup[0]
      第1個中間件1-after

 

中間件順序

下圖顯示了 ASP.NET Core MVC 和 Razor Pages 應用的完整請求處理管道。 你可以在典型應用中瞭解現有中間件的順序,以及在哪裡添加自定義中間件。 你可以完全控制如何重新排列現有中間件,或根據場景需要註入新的自定義中間件。

 

3. 什麼是短路

中間件組件可以處理請求, 並決定不調用管道中的下一個中間件,從而使管道短路,官方微軟給了一個英文的名字叫“terminal middleware ”,翻譯為“終端中間件”。短路通常是被允許的,因為它可以避免一些不必要的工作。 例如, 如果請求的是像圖像或 css 文件這樣的靜態文件, 則 StaticFiles 中間件可以處理和服務該請求並使管道中的其餘部分短路。這個意思就是說,在我們的示例中, 如果請求是針對靜態文件, 則 Staticile 中間件不會調用 MVC 中間件,避免一些無謂的操作。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env,ILogger<Startup> logger)
{
    app.UseRouting();

    app.Use(next =>
            {
                logger.LogInformation("第1個中間件");

                return async context =>
                {
                    logger.LogInformation("第1個中間件1-before");
                    await next(context);
                    logger.LogInformation("第1個中間件1-after");
                };
            });

    // 後面的中件間將不會再執行了
    app.Use(next =>
            {
                logger.LogInformation("第2個中間件");

                return async context =>
                {
                    logger.LogInformation("短路了");
                    // await next(context); // 沒有調用即表示短路了
                };
            });
    
    app.Use(next =>
            {
                logger.LogInformation("第3個中間件");

                return async context =>
                {
                    logger.LogInformation("第3個中間件3-before");
                    await next(context);
                    logger.LogInformation("第3個中間件3-after");
                };
            });

    app.UseEndpoints(endpoints =>
                     {
                         endpoints.MapControllerRoute(
                             name: "default",
                             pattern: "{controller=Home}/{action=Index}/{id?}");
                     });
}
輸出結果:

info: Step3.Empty.Startup[0]
      第3個中間件
info: Step3.Empty.Startup[0]
      第2個中間件
info: Step3.Empty.Startup[0]
      第1個中間件

// 發起請求之後
info: Step3.Empty.Startup[0]
      第1個中間件1-before
info: Step3.Empty.Startup[0]
      短路了
info: Step3.Empty.Startup[0]
      第1個中間件1-after

 

app.Use 與 app.Run 的區別

它倆都可以添加一個中間件至請求管道中。

  1. Use 有權決定是否執行下一個中間件,如果不執行,則出現短路情況

  2. Run 是直接短路,不會執行後面的中間件。

 

4. 常用的系統中間件

1. 路由中間件

ASP.NET Core 控制器使用路由中間件來匹配傳入請求的 URL 並將它們映射到操作。 若要設置路由模板,則必須執行添加如下中間件至執行管道.

// 添加控制器與視圖服務
builder.Services.AddControllersWithViews();


// ....上面省略一些代碼
app.UseRouting();

 

路由模板:

  • 在啟動時 Program.cs 或在屬性中定義。

  • 描述 URL 路徑如何與操作相匹配。

  • 用於生成鏈接的 URL。 生成的鏈接通常在響應中返回。

操作既支持傳統路由,也支持屬性路由。 通過在控制器或操作上放置路由可實現屬性路由。 有關詳細信息,請參閱混合路由

路由模板示例匹配 URI請求 URI…
hello /hello 僅匹配單個路徑 /hello
{Page=Home} / 匹配並將 Page 設置為 Home
{Page=Home} /Contact 匹配並將 Page 設置為 Contact
{controller}/{action}/{id?} /Products/List 映射到 Products 控制器和 List 操作。
{controller}/{action}/{id?} /Products/Details/123 映射到 Products 控制器和 Details 操作,並將 id 設置為 123。
{controller=Home}/{action=Index}/{id?} / 映射到 Home 控制器和 Index 方法。 id 將被忽略。
{controller=Home}/{action=Index}/{id?} /Products 映射到 Products 控制器和 Index 方法。 id 將被忽略。

設置傳統路由

app.UseRouting();
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run(); // 必須添加一個終節點
路由模板 "{controller=Home}/{action=Index}/{id?}":

匹配 URL 路徑,例如 /Products/Details/5

通過標記路徑來提取路由值 { controller = Products, action = Details, id = 5 }。 如果應用有一個名為 ProductsController 的控制器和一個 Details 操作,則提取路由值會導致匹配:

public class ProductsController : Controller
{
    public IActionResult Details(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}
MyDisplayRouteInfo 由 Rick.Docs.Samples.RouteInfo NuGet 包提供,會顯示路由信息。

/Products/Details/5 模型綁定 id = 5 的值,以將 id 參數設置為 5。 有關更多詳細信息,請參閱模型綁定。

{controller=Home} 將 Home 定義為預設 controller。

{action=Index} 將 Index 定義為預設 action。

{id?} 中的 ? 字元將 id 定義為可選。

預設路由參數和可選路由參數不必包含在 URL 路徑中進行匹配。 有關路由模板語法的詳細說明,請參閱路由模板參考。

匹配 URL 路徑 /。

生成路由值 { controller = Home, action = Index }。

controller 和 action 的值使用預設值。 id 不會生成值,因為 URL 路徑中沒有相應的段。 / 僅在存在 HomeController 和 Index 操作時匹配:

public class HomeController : Controller
{
    public IActionResult Index() { ... }
}
使用前面的控制器定義和路由模板,為以下 URL 路徑運行 HomeController.Index 操作:

/Home/Index/17

/Home/Index

/Home

/

URL 路徑 / 使用路由模板預設 Home 控制器和 Index 操作。 URL 路徑 /Home 使用路由模板預設 Index 操作。

簡便方法 MapDefaultControllerRoute:

app.MapDefaultControllerRoute();
替代:

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
 

屬性路由
// 添加控制器與視圖服務
builder.Services.AddControllersWithViews();


// ....上面省略一些代碼 
// app.UseRouting(); // 此行代碼已經不需要了
app.MapControllers(); // 映射控制器
MapControllers 調用它來映射屬性路由控制器。

在以下示例中:

HomeController 匹配一組類似於預設傳統路由 {controller=Home}/{action=Index}/{id?} 匹配的 URL。

public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult Index(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult About(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}
將針對任意 URL 路徑 /、/Home、/Home/Index 或 /Home/Index/3 執行 HomeController.Index 操作。

此示例重點介紹屬性路由與傳統路由之間的主要編程差異。 屬性路由需要更多輸入才能指定路由。 傳統預設路由會更簡潔地處理路由。 但是,屬性路由允許並需要精確控制應用於每項操作的路由模板。

對於屬性路由,控制器和操作名稱在操作匹配中不起作用,除非使用標記替換。 以下示例匹配與上一個示例相同的 URL:

public class MyDemoController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult MyIndex(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult MyAbout(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}
以下代碼對 action 和 controller 使用標記替換:

public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("[controller]/[action]")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [Route("[controller]/[action]")]
    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}
以下代碼將 [Route("[controller]/[action]")] 應用於控制器:

[Route("[controller]/[action]")]
public class HomeController : Controller
{
    [Route("~/")]
    [Route("/Home")]
    [Route("~/Home/Index")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

 

在前面的代碼中,Index 方法模板必須將 /~/ 預置到路由模板。 應用於操作的以 /~/ 開頭的路由模板不與應用於控制器的路由模板合併。

有關路由模板選擇的信息,請參閱路由模板優先順序

 

路由約束(可選)

路由約束在傳入 URL 發生匹配時執行,URL 路徑標記為路由值。 路徑約束通常檢查通過路徑模板關聯的路徑值,並對該值是否為可接受做出對/錯決定。 某些路由約束使用路由值以外的數據來考慮是否可以路由請求。 例如,HttpMethodRouteConstraint 可以根據其 HTTP 謂詞接受或拒絕請求。 約束用於路由請求和鏈接生成。

警告

請勿將約束用於輸入驗證。 如果約束用於輸入驗證,則無效的輸入將導致 404(找不到頁面)響應。 無效輸入可能生成包含相應錯誤消息的 400 錯誤請求。 路由約束用於消除類似路由的歧義,而不是驗證特定路由的輸入。

下表演示示例路由約束及其預期行為:

約束示例匹配項示例說明
int {id:int} 123456789, -123456789 匹配任何整數
bool {active:bool} true, FALSE 匹配 truefalse。 不區分大小寫
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm 在固定區域性中匹配有效的 DateTime 值。 請參閱前面的警告。
decimal {price:decimal} 49.99, -1,000.01 在固定區域性中匹配有效的 decimal 值。 請參閱前面的警告。
double {weight:double} 1.234, -1,001.01e8 在固定區域性中匹配有效的 double 值。 請參閱前面的警告。
float {weight:float} 1.234, -1,001.01e8 在固定區域性中匹配有效的 float 值。 請參閱前面的警告。
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638 匹配有效的 Guid
long {ticks:long} 123456789, -123456789 匹配有效的 long
minlength(value) {username:minlength(4)} Rick 字元串必須至少為 4 個字元
maxlength(value) {filename:maxlength(8)} MyFile 字元串不得超過 8 個字元
length(length) {filename:length(12)} somefile.txt 字元串必須正好為 12 個字元
length(min,max) {filename:length(8,16)} somefile.txt 字元串必須至少為 8 個字元,且不得超過 16 個字元
min(value) {age:min(18)} 19 整數值必須至少為 18
max(value) {age:max(120)} 91 整數值不得超過 120
range(min,max) {age:range(18,120)} 91 整數值必須至少為 18,且不得超過 120
alpha {name:alpha} Rick 字元串必須由一個或多個字母字元組成,a-z,並區分大小寫。
regex(expression) {ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} 123-45-6789 字元串必須與正則表達式匹配。 請參閱有關定義正則表達式的提示。
required {name:required} Rick 用於強制在 URL 生成過程中存在非參數值

警告

如果使用 System.Text.RegularExpressions 處理不受信任的輸入,則傳遞一個超時。 惡意用戶可能會向 RegularExpressions 提供輸入,從而導致拒絕服務攻擊。 使用 RegularExpressions 的 ASP.NET Core 框架 API 會傳遞一個超時。

 

可向單個參數應用多個用冒號分隔的約束。 例如,以下約束將參數限製為大於或等於 1 的整數值:

[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { }

 

警告

驗證 URL 的路由約束並將轉換為始終使用固定區域性的 CLR 類型。 例如,轉換為 CLR 類型 intDateTime。 這些約束假定 URL 不可本地化。 框架提供的路由約束不會修改存儲於路由值中的值。 從 URL 中分析的所有路由值都將存儲為字元串。 例如,float 約束會嘗試將路由值轉換為浮點數,但轉換後的值僅用來驗證其是否可轉換為浮點數。

 

疑惑解答:

1. 當訪問一個Web 應用地址時,Asp.Net Core 是怎麼執行到ControllerAction的呢?

答:程式啟動的時候會把所有的Controller 中的Action 映射存儲到routeOptions 的集合中,Action 映射成Endpoint終結者 的RequestDelegate 委托屬性,最後通過UseEndPoints 添加EndpointMiddleware 中間件進行執行,同時這個中間件中的Endpoint 終結者路由已經是通過Rouing匹配後的路由。

2. EndPoint 跟普通路由又存在著什麼樣的關係?

答:Ednpoint 終結者路由是普通路由map 轉換後的委托路由,裡面包含了路由方法的所有元素信息EndpointMetadataCollectionRequestDelegate 委托。

3. UseRouing()UseAuthorization()UseEndpoints() 這三個中間件的關係是什麼呢?

答:UseRouing 中間件主要是路由匹配,找到匹配的終結者路由EndpointUseEndpoints 中間件主要針對UseRouing 中間件匹配到的路由進行 委托方法的執行等操作。 UseAuthorization 中間件主要針對 UseRouing 中間件中匹配到的路由進行攔截 做授權驗證操作等,通過則執行下一個中間件UseEndpoints(),具體的關係可以看下麵的流程圖:

 

上面流程圖中省略了一些部分,主要是把UseRouing 、UseAuthorization 、UseEndpoint 這三個中間件的關係突顯出來。

 

 

2. 異常中間件

UseExceptionHandler : 將中間件添加到管道,該中間件將捕獲異常,記錄異常,併在備用管道中重新執行請求。如果響應已啟動,則不會重新執行請求。

UseDeveloperExceptionPage: 從管道捕獲同步和非同步異常實例,並生成 HTML 錯誤響應。

if (!app.Environment.IsDevelopment()) // 非開發環境下,可以顯示自定義錯誤頁
{
    app.UseExceptionHandler("/Home/Error");
}
else
{
    app.UseDeveloperExceptionPage(); // 開發人員錯誤頁
}

 

 

3. 靜態資源中間件

預設情況下,靜態文件(如 HTML、CSS、圖像和 JavaScript)是 ASP.NET Core 應用直接提供給客戶端的資產。

靜態文件存儲在項目的 Web 根目錄中。 預設目錄為 {content root}/wwwroot,但可通過 UseWebRoot 方法更改目錄。 有關詳細信息,請參閱內容根目錄Web 根目錄

Web 應用程式項目模板包含 wwwroot 文件夾中的多個文件夾:

  • wwwroot

    • css 樣式文件

    • js 腳本文件

    • lib 第三方前端庫

    • images 圖片文件

 

預設 Web 應用模板在 Program.cs 中調用

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 作者:Lxlxxx 鏈接:https://juejin.cn/post/7221461552343072828 前言 繼上次線上CPU出現了報警,這次服務又開始整活了,風平浪靜了沒幾天,看生產日誌服務的運行的時候,頻繁的出現OutOfMemoryError,就是我們俗稱的OOM,這可還行! 頻繁的O ...
  • 上一篇介紹了scikit-learn中的幾個玩具數據集,本篇介紹scikit-learn提供的一些真實的數據集。玩具數據集:scikit-learn 基礎(01)--『數據載入』之玩具數據集 1. 獲取數據集 與玩具數據集不同,真實的數據集的數據不僅數據特征多,而且數據量也比較大,所以沒有直接包含在 ...
  • 在今天的課上,我們深入討論了封裝、反射和單例模式這幾個重要的概念。我不想過多地贅述它們的細節,但是請大家務必記住它們的基本語法規則,因為這也是面向對象章節的結束。我希望大家能夠牢牢掌握這些知識點,為未來的學習打下堅實的基礎。 ...
  • 外接矩形、外接圓: 1 import cv2 2 import numpy 3 4 img = cv2.imread('../img/img.png', -1) 5 ret, thresh = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) 6 con ...
  • 1. _Layout.cshtml 佈局頁 佈局視圖和我們在Asp.Net MVC一樣,佈局視圖_Layout.cshtml使得所有視圖保持一致的外觀變得更加容易,因為我們只有一個要修改的佈局視圖文件,更改後將立即反映在整個應用程式的所有視圖中。 在 ASP.NET Core MVC 中,有一些視圖 ...
  • 如果是首次安裝Dev只需要下麵兩步流程就可以 第一步安裝試用的最新版 Devexpress 22.2.4這步看直接去官網,安裝官方試用的就可以 第二步安裝破解補丁關閉防火牆或360 然後打開 DevExpress.Universal.Patch 選擇22.2 版本 和對應的visual studio ...
  • create database MvcUnit4; go use MvcUnit4; go create table Product ( Id bigint primary key, ProductName varchar(30), CategoryName varchar(30), Price d ...
  • 前言 上一篇,我們實現了基於 DotNetty 的通信基礎模塊的搭建,本篇,主要實現待發佈 Web 項目的集成。 創建待發佈項目 為了測試, 我創建了一個基於 .NET 4.8 的 Web 項目 OpenDeploy.TestWebProject 我本機的代碼倉儲路徑是: D:\Projects\B ...
一周排行
    -Advertisement-
    Play Games
  • 1、預覽地址:http://139.155.137.144:9012 2、qq群:801913255 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕 ...
  • 前言 上一篇文章寫瞭如何使用RabbitMQ做個簡單的發送郵件項目,然後評論也是比較多,也是準備去學習一下如何確保RabbitMQ的消息可靠性,但是由於時間原因,先來說說設計模式中的簡單工廠模式吧! 在瞭解簡單工廠模式之前,我們要知道C#是一款面向對象的高級程式語言。它有3大特性,封裝、繼承、多態。 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 介紹 Nodify是一個WPF基於節點的編輯器控制項,其中包含一系列節點、連接和連接器組件,旨在簡化構建基於節點的工具的過程 ...
  • 創建一個webapi項目做測試使用。 創建新控制器,搭建一個基礎框架,包括獲取當天日期、wiki的請求地址等 創建一個Http請求幫助類以及方法,用於獲取指定URL的信息 使用http請求訪問指定url,先運行一下,看看返回的內容。內容如圖右邊所示,實際上是一個Json數據。我們主要解析 大事記 部 ...
  • 最近在不少自媒體上看到有關.NET與C#的資訊與評價,感覺大家對.NET與C#還是不太瞭解,尤其是對2016年6月發佈的跨平臺.NET Core 1.0,更是知之甚少。在考慮一番之後,還是決定寫點東西總結一下,也回顧一下.NET的發展歷史。 首先,你沒看錯,.NET是跨平臺的,可以在Windows、 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 添加節點(nodes) 通過上一篇我們已經創建好了編輯器實例現在我們為編輯器添加一個節點 添加model和viewmode ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...
  • 類型檢查和轉換:當你需要檢查對象是否為特定類型,並且希望在同一時間內將其轉換為那個類型時,模式匹配提供了一種更簡潔的方式來完成這一任務,避免了使用傳統的as和is操作符後還需要進行額外的null檢查。 複雜條件邏輯:在處理複雜的條件邏輯時,特別是涉及到多個條件和類型的情況下,使用模式匹配可以使代碼更 ...
  • 在日常開發中,我們經常需要和文件打交道,特別是桌面開發,有時候就會需要載入大批量的文件,而且可能還會存在部分文件缺失的情況,那麼如何才能快速的判斷文件是否存在呢?如果處理不當的,且文件數量比較多的時候,可能會造成卡頓等情況,進而影響程式的使用體驗。今天就以一個簡單的小例子,簡述兩種不同的判斷文件是否... ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...