2018年8月22日 星期三

在 ASP.NET 使用 執行緒的同步內容 SynchronizationContext 進行非同步工作處理問題 HttpContext

在 ASP.NET 使用 執行緒的同步內容 SynchronizationContext 進行非同步工作處理問題 HttpContext

SynchronizationContext類別是一個基底類別,它提供執行緒內容的同步處理需求,它可以幫助我們在在不同的同步情況下,進行進行非同步/同步操作正確行為。若想要更深入去了解 SynchronizationContext 這個類別功能,可以參考 不可或缺的 SynchronizationContext 文章。
在這篇文章中,我們將要來說明在 ASP.NET 使用 執行緒的同步內容 SynchronizationContext 進行非同步工作處理問題,也就是我們在 ASP.NET 專案程式碼開發時候,當使用者發出一個 Http Request 請求的時候, IIS 將會從執行緒集區取出一個執行緒,用來處理這個使用者請求;此時,在這個執行緒中,當時的同步內容 SynchronizationContext 將會是一個 System.Web.AspNetSynchronizationContext 類別物件,這個類別物件將會用於處理 ASP.NET 的執行緒同步內容的處理工作。因此,若我們在這個 Http Request 請求執行緒中,另外進行一個非同步的工作,而該非同步的工作將會在另外一個新的執行緒中來執行,此時,在這個新的執行緒中,將無法取得 System.Web.HttpContext.Current 屬性值,此時,該屬性值將會是 null。
另外,當非同步工作完成之後,若沒有指定返回的執行緒要進行同步內容切換,則會造成呼叫非同步工作後的相關程式碼,也無法取得 System.Web.HttpContext.Current 屬性值,因此,若沒有遵守這樣的要求,將會造成您的應用程式發生例外異常的問題。這樣的問題將會發生在您的 ASP.NET 專案中有使用到 多執行緒 Multiple Thread 或者 非同步 Asynchronous 處理需求,若沒有特別注意,將會發生不可預期的例外異常問題。
我們來展示當您進行多執行緒或者非同步工作程式設計並且沒有遵循這樣要求,會發生甚麼樣的問題,以及要如何解決此一問題。在這裡您需要知道 ASP.NET 有繼承 SynchronizationContext 類別,實作出 System.Web.AspNetSynchronizationContext 類別,當您在 Http Request 請求執行緒下執行程式碼,此時,需要存取 System.Web.HttpContext.Current 屬性值,您可以透過 AspNetSynchronizationContext 物件幫助您做到同步內容,如此,也就是不會發生不明例外異常。
底下是我們要測試的 HomeController 程式碼。
首先,我們來看看當 continueOnCapturedContext = true 的執行結果
在這個測試中, Index 動作方法將會等候 Task.Run 這個非同步執行完成,而且,在這個非同步工作的等候前後,我們都可以看到當時的 Http Context 是都有值的,他的型別為 System.Web.AspNetSynchronizationContext。不過,在 Tas.Run 內的委派方法,在進行 Thread.Sleep 方法呼叫前後,我們是都無法捕捉到任何關於 System.Web.HttpContext.Current 的屬性值,這是因為要等候 Task.Run 這個非同步工作的時候,該非同步工作裡面會產生一個新的執行緒 (此執行緒 ID 為 9),而對於當使用者請求這個 Http Request 請求時候所用的執行緒 (此執行緒 ID 為 8) 是不同的,因此,在新的執行緒中,是無法讀取到任何 System.Web.HttpContext.Current 的屬性值。
不過,由於我們有在 Task.Run 這個非同步方法之後,又執行了 ConfigureAwait(true) 這個方法,所以,當非同步工作執行完成之後,將會透過執行緒同步內容,也就是 System.Web.AspNetSynchronizationContext 類別的物件,將原先 Http Request 請求的相關內容,複製到這個從非同步工作返回的執行緒中,故,我們可以還是可以看到與存取 System.Web.HttpContext.Current 的屬性值。在這裡您會看到,一開始使用者 Http 請求的執行緒為 ID=8,非同步工作的執行緒 ID=9,不過,這裡不像是 WPF 類型的應用程式,在 ASP.NET 專案程式中,在完成非同步工作之後,將會從執行緒集區 ThreadPool 內,取得一個新的執行緒,透過同步內容物件的幫助,還原 System.Web.HttpContext.Current ,因此,我們看到當完成非同步工作的執行緒,是與當初請求的執行緒不同,但是,還是可以繼續存取 System.Web.HttpContext.Current 的屬性值。
進行 System.Web.HttpContext.Current 多執行緒的同步內文測試
執行緒 Thread → 8
工作排程 Task scheduler → System.Threading.Tasks.ThreadPoolTaskScheduler
同步內文 SynchronizationContext → System.Web.AspNetSynchronizationContext
Http內文 Http Context → System.Web.HttpContext


等候 非同步工作 完成
執行緒 Thread → 8
工作排程 Task scheduler → System.Threading.Tasks.ThreadPoolTaskScheduler
同步內文 SynchronizationContext → System.Web.AspNetSynchronizationContext
Http內文 Http Context → System.Web.HttpContext


正在非同步工作中 / Sleep 方法將會被呼叫
執行緒 Thread → 9
工作排程 Task scheduler → System.Threading.Tasks.ThreadPoolTaskScheduler
同步內文 SynchronizationContext →
Http內文 Http Context →

準備結束非同步工作 / 是否要回到原先執行緒的同步內文中 True
執行緒 Thread → 9
工作排程 Task scheduler → System.Threading.Tasks.ThreadPoolTaskScheduler
同步內文 SynchronizationContext →
Http內文 Http Context →

非同步工作 已經完成
執行緒 Thread → 9
工作排程 Task scheduler → System.Threading.Tasks.ThreadPoolTaskScheduler
同步內文 SynchronizationContext → System.Web.AspNetSynchronizationContext
Http內文 Http Context → System.Web.HttpContext
ASP.NET SynchronizationContext
現在,我們來看看當 continueOnCapturedContext = false 的執行結果
在這個測試中, Index 動作方法將會等候 Task.Run 這個非同步執行完成,而且,在這個非同步工作的等候前後,我們都可以看到當時的 Http Context 是都有值的,他的型別為 System.Web.AspNetSynchronizationContext。不過,在 Tas.Run 內的委派方法,在進行 Thread.Sleep 方法呼叫前後,我們是都無法捕捉到任何關於 System.Web.HttpContext.Current 的屬性值,這是因為要等候 Task.Run 這個非同步工作的時候,該非同步工作裡面會產生一個新的執行緒 (此執行緒 ID 為 9),而對於當使用者請求這個 Http Request 請求時候所用的執行緒 (此執行緒 ID 為 8) 是不同的,因此,在新的執行緒中,是無法讀取到任何 System.Web.HttpContext.Current 的屬性值。
不過,由於我們有在 Task.Run 這個非同步方法之後,又執行了 ConfigureAwait(false) 這個方法,因此,當非同步工作執行完成之後,將會不會執行執行緒同步內容工作,將原先 Http Request 請求的相關內容,複製到這個從非同步工作返回的執行緒中,故,我們是無法看到與存取 System.Web.HttpContext.Current 的屬性值。在這裡您會看到,一開始使用者 Http 請求的執行緒為 ID=8,非同步工作的執行緒 ID=9,不過,這裡不像是 WPF 類型的應用程式,在 ASP.NET 專案程式中,在完成非同步工作之後,將會從執行緒集區 ThreadPool 內,取得一個新的執行緒,透過同步內容物件的幫助,還原 System.Web.HttpContext.Current ,因此,我們看到當完成非同步工作的執行緒,是與當初請求的執行緒不同,但是,還是可以繼續存取 System.Web.HttpContext.Current 的屬性值。
進行 System.Web.HttpContext.Current 多執行緒的同步內文測試
執行緒 Thread → 8
工作排程 Task scheduler → System.Threading.Tasks.ThreadPoolTaskScheduler
同步內文 SynchronizationContext → System.Web.AspNetSynchronizationContext
Http內文 Http Context → System.Web.HttpContext


等候 非同步工作 完成
執行緒 Thread → 8
工作排程 Task scheduler → System.Threading.Tasks.ThreadPoolTaskScheduler
同步內文 SynchronizationContext → System.Web.AspNetSynchronizationContext
Http內文 Http Context → System.Web.HttpContext


正在非同步工作中 / Sleep 方法將會被呼叫
執行緒 Thread → 9
工作排程 Task scheduler → System.Threading.Tasks.ThreadPoolTaskScheduler
同步內文 SynchronizationContext →
Http內文 Http Context →

準備結束非同步工作 / 是否要回到原先執行緒的同步內文中 False
執行緒 Thread → 9
工作排程 Task scheduler → System.Threading.Tasks.ThreadPoolTaskScheduler
同步內文 SynchronizationContext →
Http內文 Http Context →

非同步工作 已經完成
執行緒 Thread → 9
工作排程 Task scheduler → System.Threading.Tasks.ThreadPoolTaskScheduler
同步內文 SynchronizationContext →
Http內文 Http Context →
ASP.NET SynchronizationContext
C Sharp / C#
public class HomeController : Controller
{
    public async Task<ActionResult> Index()
    {
        // 是否要回到原先執行緒的同步內文中
        // https://msdn.microsoft.com/zh-tw/library/system.threading.tasks.task.configureawait(v=vs.110).aspx
        bool continueOnCapturedContext = true;
        Response.Write($"{Log("進行 System.Web.HttpContext.Current 多執行緒的同步內文測試")}<br/><hr/><br/>");
        try
        {
            Response.Write($"{Log("等候 非同步工作 完成")}<br/><hr/><br/>");

            // 使用我們指定的條件,進行呼叫非同步方法
            await Task.Run(async () =>
            {
                Response.Write($"{Log($"正在非同步工作中 / Sleep 方法將會被呼叫")}<br/>");
                // 要休息 1000 毫秒數
                System.Threading.Thread.Sleep(1000);
                Response.Write($"{Log($"準備結束非同步工作 / 是否要回到原先執行緒的同步內文中 <strong>{continueOnCapturedContext.ToString()}</strong>")}<br/>");
            }).ConfigureAwait(continueOnCapturedContext);

            Response.Write($"{Log("非同步工作 已經完成")}<br/><hr/>");
        }
        catch (Exception e)
        {
            Response.Write($"{Log($"Error {e.Message}")}<br/>");
        }

        return new HttpStatusCodeResult(200);
    }

    string Log(string msg)
    {
        var synchronizationContext = System.Threading.SynchronizationContext.Current;
        var taskScheduler = TaskScheduler.Current;
        var httpContextCurrent = System.Web.HttpContext.Current;
        return $"{msg} <ul><li>" +
            $"執行緒 Thread → {System.Threading.Thread.CurrentThread.ManagedThreadId} </li><li>" +
            $"工作排程 Task scheduler → {taskScheduler} </li><li>" +
            $"同步內文 SynchronizationContext → {synchronizationContext} </li><li>" +
            $"Http內文 Http Context → {httpContextCurrent} </li></ul>";
    }

    public ActionResult About()
    {
        ViewBag.Message = "Your application description page.";

        return View();
    }

    public ActionResult Contact()
    {
        ViewBag.Message = "Your contact page.";

        return View();
    }
}

關於 Xamarin 在台灣的學習技術資源

Xamarin 實驗室 粉絲團
歡迎加入 Xamarin 實驗室 粉絲團,在這裡,將會經常性的貼出各種關於 Xamarin / Visual Studio / .NET 的相關消息、文章、技術開發等文件,讓您可以隨時掌握第一手的 Xamarin 方面消息。
Xamarin.Forms @ Taiwan
歡迎加入 Xamarin.Forms @ Taiwan,這是台灣的 Xamarin User Group,若您有任何關於 Xamarin / Visual Studio / .NET 上的問題,都可以在這裡來與各方高手來進行討論、交流。
Xamarin 實驗室 部落格
Xamarin 實驗室 部落格 是作者本身的部落格,這個部落格將會專注於 Xamarin 之跨平台 (Android / iOS / UWP) 方面的各類開技術探討、研究與分享的文章,最重要的是,它是全繁體中文。
Xamarin.Forms 系列課程
Xamarin.Forms 系列課程 想要快速進入到 Xamarin.Forms 的開發領域,學會各種 Xamarin.Forms 跨平台開發技術,例如:MVVM、Prism、Data Binding、各種 頁面 Page / 版面配置 Layout / 控制項 Control 的用法等等,千萬不要錯過這些 Xamarin.Forms 課程



2018年8月21日 星期二

在 WPF 使用 執行緒的同步內容 SynchronizationContext 進行非同步工作處理問題

在 WPF 使用 執行緒的同步內容 SynchronizationContext 進行非同步工作處理問題

SynchronizationContext類別是一個基底類別,它提供執行緒內容的同步處理需求,它可以幫助我們在在不同的同步情況下,進行進行非同步/同步操作正確行為。若想要更深入去了解 SynchronizationContext 這個類別功能,可以參考 不可或缺的 SynchronizationContext 文章。

了解更多關於 [使用 async 和 await 進行非同步程式設計] 的使用方式
了解更多關於 [Thread Class] 的使用方式
了解更多關於 [SynchronizationContext Class] 的使用方式


了解更多關於 [同步處理原始物件概觀] 的使用方式

在這篇文章中,我們將要來說明在 WPF 使用 執行緒的同步內容 SynchronizationContext 進行非同步工作處理問題,也就是我們在 WPF 專案程式碼開發時候,必須要注意一個很重要的需求,任何想要變更 UI 相關物件或其屬性的程式碼,需要在 主執行緒 ( Main Thread ) 或稱 UI執行緒 ( UI Thread ) 下來執行,若沒有遵守這樣的要求,將會造成您的應用程式發生例外異常的問題。這樣的問題將會發生在您的 WPF 專案中有使用到 多執行緒 Multiple Thread 或者 非同步 Asynchronous 處理需求,若沒有特別注意,將會發生不可預期的例外異常問題。
我們來展示當您進行多執行緒或者非同步工作程式設計並且沒有遵循這樣要求,會發生甚麼樣的問題,以及要如何解決此一問題。在這裡您需要知道 WPF 有繼承 SynchronizationContext 類別,實作出 DispatcherSynchronizationContext 類別,當您在非主執行緒或者UI執行緒下執行程式碼,此時,需要在別的執行緒 Thread 下進行更新與變動 UI 相關物件屬性,您可以透過 DispatcherSynchronizationContext 物件幫助您做到同步內容,如此,相關 UI 異動程式碼就會在主執行緒下執行,也就是不會發生不明例外異常。
底下是我們要測試的 WPF XAML 頁面宣告,在這裡,我們宣告了兩個按鈕, [btnRunAsyncWithoutSynchronizationContext] / [btnRunAsyncWithSynchronizationContext] ,分別要進行沒有使用同步內容與有使用同步內容的測試。底下為執行後的畫面。
WPF SynchronizationContext
XAML
<Window x:Class="執行緒的同步處理內容WPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:執行緒的同步處理內容WPF"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="60"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <TextBlock
            Grid.Row="0" Grid.ColumnSpan="2"
            x:Name="tbkResult"
            Text="執行結果"
            TextWrapping="Wrap"
            HorizontalAlignment="Center" VerticalAlignment="Center"
            FontSize="30"
            />

        <Button
            Grid.Row="1"
            x:Name="btnRunAsyncWithoutSynchronizationContext"
            Content="執行非同步工作,沒有同步內容" Click="btnRunAsyncWithoutSynchronizationContext_Click"/>

        <Button
            Grid.Row="1" Grid.Column="1"
            x:Name="btnRunAsyncWithSynchronizationContext"
            Content="執行非同步工作,有同步內容" Click="btnRunAsyncWithSynchronizationContext_Click"/>
    </Grid>
</Window>
現在,我們來看看當點選這兩個按鈕後,會發生甚麼問題呢?
  • btnRunAsyncWithoutSynchronizationContext
    當點選這個按鈕之後,將會進行執行非同步工作,沒有同步內容,我們可以從程式碼中看出,當這按鈕點選之後,將會執行這個敘述
    await new System.Net.Http.HttpClient().GetStringAsync("http://www.google.com").ConfigureAwait(false)
    這表示將會讀取 Google 網站上的內容,並且傳回該網頁字串內容,不過,在這裡 我們有呼叫 ConfigureAwait(false),這表示當這個非同步工作執行完成之後,並不會回到現在這個執行緒,而是會從執行池 ThreadPool 內取得一個執行緒,繼續執行剩下的程式碼。關於 ConfigureAwait 說明,可以參考 ConfigureAwait 網頁
    請在 tbkResult.Text = foo; 敘述上設定中斷點,並且執行這個專案,接著點選 [執行非同步工作,沒有同步內容] 按鈕,這個時候將會停留在這個中斷點上。請在監看式視窗中,加入觀察 BeforeSynchronizationContext , BeforeThreadID , AfterSynchronizationContext , AfterThreadID ,結果該會類似下面截圖。
    在這裡我們看到在進行非同步工作方法執行之前,當時是執行緒 ID 是 1 (也就是在主執行緒上,我們可以從下面的執行緒視窗看的出來) ,而且當時 SynchronizationContext.Current 屬性中,是有 System.Windows.Threading.DispatcherSynchronizationContext 這個型別的物件;不過,當非同步工作執行完成後,繼續回到這個事件方法內,我們發現到了,這個時候的執行緒ID變成了 7,並且是一個背景工作執行緒,而且這個時候 SynchronizationContext.Current 屬性內已經變成空值。
    WPF Thread
    請打開 [除錯] > [視窗] > [執行緒] 視窗,我們可以看到這個應用程式有用到的執行緒相關資訊,在這個視窗中,我們看到了 主執行緒 Main Thread / UI 執行緒 UI Thread 是編號為 1 的執行緒,而我們停留在中斷點上的程式碼,則是在執行緒為編號 7 的執行緒上執行。
    WPF Thread
    若在此時我們想要修改任何 UI 控制項的屬性,就會發生問題。您將會得到這個錯誤訊息,這也表示了我們的應用程式將會中止執行了。
    System.InvalidOperationException: '呼叫執行緒無法存取此物件,因為此物件屬於另一個執行緒。'
    System.InvalidOperationException
  • btnRunAsyncWithSynchronizationContext
    當點選這個按鈕之後,將會進行執行非同步工作,有同步內容,我們可以從程式碼中看出,當這按鈕點選之後,將會執行這個敘述
    await new System.Net.Http.HttpClient().GetStringAsync("http://www.google.com");
    這表示將會讀取 Google 網站上的內容,並且傳回該網頁字串內容,不過,在這裡 我沒有呼叫 ConfigureAwait(false),這表示當這個非同步工作執行完成之後,會回到現在這個執行緒,也就是主執行緒或者UI執行緒。
    請在 [btnRunAsyncWithSynchronizationContext_Click] 事件方法內的 tbkResult.Text = foo; 敘述上設定中斷點,並且執行這個專案,接著點選 [執行非同步工作,有同步內容] 按鈕,這個時候將會停留在這個中斷點上。請在監看式視窗中,加入觀察 BeforeSynchronizationContext , BeforeThreadID , AfterSynchronizationContext , AfterThreadID ,結果該會類似下面截圖。
    在這裡我們看到在進行非同步工作方法執行前、後,都是位於執行緒 ID 是 1 (也就是在主執行緒上,我們可以從下面的執行緒視窗看的出來) ,=而且當時 SynchronizationContext.Current 屬性中,都是有 System.Windows.Threading.DispatcherSynchronizationContext 這個型別的物件。
    WPF Thread
    請打開 [除錯] > [視窗] > [執行緒] 視窗,我們可以看到這個應用程式有用到的執行緒相關資訊,在這個視窗中,我們看到了 主執行緒 Main Thread / UI 執行緒 UI Thread 是編號為 1 的執行緒,而我們停留在中斷點上的程式碼,則是在執行緒為編號 1 的執行緒上執行。
    WPF Thread
    若在此時我們想要修改任何 UI 控制項的屬性,就不會發生問題。為什麼呢?
    因為這些程式碼都是在主執行緒上執行的呀
    底下是執行後的程式畫面截圖
    SynchronizationContext
C Sharp / C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace 執行緒的同步處理內容WPF
{
    /// <summary>
    /// MainWindow.xaml 的互動邏輯
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void btnRunAsyncWithoutSynchronizationContext_Click(object sender, RoutedEventArgs e)
        {
            SynchronizationContext BeforeSynchronizationContext = SynchronizationContext.Current;
            int BeforeThreadID = Thread.CurrentThread.ManagedThreadId;
            var foo = await new System.Net.Http.HttpClient().GetStringAsync("http://www.google.com").ConfigureAwait(false);
            SynchronizationContext AfterSynchronizationContext = SynchronizationContext.Current;
            int AfterThreadID = Thread.CurrentThread.ManagedThreadId;
            tbkResult.Text = foo;
        }

        private async void btnRunAsyncWithSynchronizationContext_Click(object sender, RoutedEventArgs e)
        {
            SynchronizationContext BeforeSynchronizationContext = SynchronizationContext.Current;
            int BeforeThreadID = Thread.CurrentThread.ManagedThreadId;
            var foo = await new System.Net.Http.HttpClient().GetStringAsync("http://www.google.com");
            SynchronizationContext AfterSynchronizationContext = SynchronizationContext.Current;
            int AfterThreadID = Thread.CurrentThread.ManagedThreadId;
            tbkResult.Text = foo;
        }
    }
}


了解更多關於 [使用 async 和 await 進行非同步程式設計] 的使用方式
了解更多關於 [Thread Class] 的使用方式
了解更多關於 [SynchronizationContext Class] 的使用方式


了解更多關於 [同步處理原始物件概觀] 的使用方式



關於 Xamarin 在台灣的學習技術資源

Xamarin 實驗室 粉絲團
歡迎加入 Xamarin 實驗室 粉絲團,在這裡,將會經常性的貼出各種關於 Xamarin / Visual Studio / .NET 的相關消息、文章、技術開發等文件,讓您可以隨時掌握第一手的 Xamarin 方面消息。
Xamarin.Forms @ Taiwan
歡迎加入 Xamarin.Forms @ Taiwan,這是台灣的 Xamarin User Group,若您有任何關於 Xamarin / Visual Studio / .NET 上的問題,都可以在這裡來與各方高手來進行討論、交流。
Xamarin 實驗室 部落格
Xamarin 實驗室 部落格 是作者本身的部落格,這個部落格將會專注於 Xamarin 之跨平台 (Android / iOS / UWP) 方面的各類開技術探討、研究與分享的文章,最重要的是,它是全繁體中文。
Xamarin.Forms 系列課程
Xamarin.Forms 系列課程 想要快速進入到 Xamarin.Forms 的開發領域,學會各種 Xamarin.Forms 跨平台開發技術,例如:MVVM、Prism、Data Binding、各種 頁面 Page / 版面配置 Layout / 控制項 Control 的用法等等,千萬不要錯過這些 Xamarin.Forms 課程



2018年8月20日 星期一

使用 Autofac 進行建構式注入並且數值或者字串與解析時候,需要覆蓋當初註冊數值或者字串用法

使用 Autofac 進行建構式注入並且數值或者字串與解析時候,需要覆蓋當初註冊數值或者字串用法

當我們在使用 Autofac IoC Container 的時候,我們具體實作的類別內建構式需要傳入數值或者字串的時候,我們可以使用這三種方法來進行宣告 : NamedParameter , TypedParameter , ResolvedParamet ;另外,當我們進行解析的時候,我們也想要覆蓋之前註冊所定義的數值或字串,上面所述的三種方式來進行覆蓋。
關於這兩項需求更詳盡的使用說明,可以查看官方 Passing Parameters to Register / Passing Parameters to Resolve 文件。
在這篇文章中,我們將要來說明如何再進行 DI 容器註冊與解析的時候,如何透過 NamedParameter 來解決需要注入的數值與字串的需求。這篇文章的範例原始碼,可以從 https://github.com/vulcanlee/CSharpNotes2018/tree/master/AutofacResolveParameter 取得
我們首先宣告一個 IMessage 介面,並且設計一個 ConsoleMessage 類別來實作這個介面,不過,在這個 ConsoleMessage 具體實作類別中,僅有設計一個建構式,這個建構式需要傳入一個字串與一個整數數值。
所以,當我們使用 ContainerBuilder.RegisterType 方法進行抽象介面與具體實作類別的註冊需求的時候,可以使用延伸方法 WithParameter 來指定建構式參數的明確數值是甚麼?其中延伸方法 WithParameter 的第一個參數將會是建構函式的參數名稱,第二個參數將會為指定參數名稱的實際數值。在我們這個範例中,我們具體實作類別的建構函式 public ConsoleMessage(string name, int age) ,他需要傳入一個字串與一個整數數值,因此,我們使用了 WithParameter("name", "Awesome") / WithParameter("age", 18) 來指定使用這兩個數值傳入到要注入物件的建構式內。因此,當我們透過 Autofac IoC Container 要注入 IMessage 這個介面的時候 (或者進行 container.Resolve<IMessage>() 手動解析時候),Autofac DI Container,將會把上面定義的兩個數值,傳入到建構式 public ConsoleMessage(string name, int age)
現在,我們要進行手動解析 IMessage 介面時候,不希望使用當初註冊宣告的數值,而希望使用我們另外提供的數值,讓 Autofac IoC 容器使用指定的數值,傳入到建構式內,建立一個具體實作類別的物件。此時,當我們呼叫 container.Resolve<IMessage> 方法的時候,可以在 Resolve 方法內,傳入 NamedParameter 物件,指定哪個參數名稱需要使用我們指定的數值來傳入到建構函式內,類似這樣的敘述 container.Resolve<IMessage>(new NamedParameter("name", "Oh..No"), new NamedParameter("age", 77));
C Sharp / C#
class Program
{
    static void Main(string[] args)
    {
        // 準備建立型別對應註冊的工作
        var builder = new ContainerBuilder();
        // 進行抽象型別與具體實作類別的註冊
        builder.RegisterType<ConsoleMessage>().As<IMessage>()
            .WithParameter("name", "Awesome")
            .WithParameter("age", 18);


        // 這裡將會建立 DI 容器
        IContainer container = builder.Build();

        // 進行抽象型別的具體實作物件的解析
        IMessage message = ;

        // 執行取得物件的方法
        message.Write("Hi Vulcan");

        // 進行抽象型別的具體實作物件的解析
        IMessage messageParameter = container.Resolve<IMessage>(
            new NamedParameter("name", "Oh..No"),
            new NamedParameter("age", 77));

        // 執行取得物件的方法
        messageParameter.Write("Hi Vulcan");

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

public interface IMessage
{
    void Write(string message);
}

public class ConsoleMessage : IMessage
{
    string _Name;
    int _Age;
    public ConsoleMessage(string name, int age)
    {
        _Name = name;
        _Age = age;
    }
    public void Write(string message)
    {
        Console.WriteLine($"[{_Name}, {_Age}] 訊息: {message} 已經寫入到螢幕上了");
    }
}
現在,我們可以執行我們的專案,此時,將會出現底下的輸出結果,第一行是我們使用了 container.Resolve<IMessage>() 進行手動解析出來 IMessage 的具體實作類別的物件;而第二行將會為使用了 container.Resolve<IMessage>(new NamedParameter("name", "Oh..No"), new NamedParameter("age", 77)); 敘述,取得了另外一個物件,我們可以從這兩個輸出內容中看出,這兩個物件確實使用了不同的數值與字串,傳入到該具體實作類別的建構式。
[Awesome, 18] 訊息: Hi Vulcan 已經寫入到螢幕上了
[Oh..No, 77] 訊息: Hi Vulcan 已經寫入到螢幕上了
Press any key for continuing...

關於 Xamarin 在台灣的學習技術資源

Xamarin 實驗室 粉絲團
歡迎加入 Xamarin 實驗室 粉絲團,在這裡,將會經常性的貼出各種關於 Xamarin / Visual Studio / .NET 的相關消息、文章、技術開發等文件,讓您可以隨時掌握第一手的 Xamarin 方面消息。
Xamarin.Forms @ Taiwan
歡迎加入 Xamarin.Forms @ Taiwan,這是台灣的 Xamarin User Group,若您有任何關於 Xamarin / Visual Studio / .NET 上的問題,都可以在這裡來與各方高手來進行討論、交流。
Xamarin 實驗室 部落格
Xamarin 實驗室 部落格 是作者本身的部落格,這個部落格將會專注於 Xamarin 之跨平台 (Android / iOS / UWP) 方面的各類開技術探討、研究與分享的文章,最重要的是,它是全繁體中文。
Xamarin.Forms 系列課程
Xamarin.Forms 系列課程 想要快速進入到 Xamarin.Forms 的開發領域,學會各種 Xamarin.Forms 跨平台開發技術,例如:MVVM、Prism、Data Binding、各種 頁面 Page / 版面配置 Layout / 控制項 Control 的用法等等,千萬不要錯過這些 Xamarin.Forms 課程