2021年6月6日 星期日

Blazor SfGrid SfDataManager 的參數 Parameter 傳遞的異常設計分析 2

 

Blazor SfGrid SfDataManager 的參數 Parameter 傳遞的異常設計分析 2

現在終於可以來嘗試解決使用 Syncfusion SfGrid 元件可以使用 SfDataManager 來指定客製化轉接器,取得 Grid 元件所要顯示的紀錄,卻會有問題的情況。

這篇文章接續 Blazor SfGrid SfDataManager 的參數 Parameter 傳遞的異常設計分析 1

這個說明專案的原始碼位於 bzSfGridRefresh

使用各種方式,讓 Refresh 方法,取得最新的參數變更內容

打開 [Pages] 資料夾內的 Index.razor 檔案

確定該 Blazor 元件如底下內容

@page "/"

<h1>Hello, world!</h1>

@*<Index1View />*@
<Index2View />
@*<Index3View />*@

<SurveyPrompt Title="How is Blazor working for you?" />

在這裡將會使用自行設計的一個 Blazor 元件,這個 Index2View.razor 元件的程式碼如下

@using Syncfusion.Blazor.Grids;
@using Syncfusion.Blazor.Data;
@using Syncfusion.Blazor;

<div>
    <button class="btn btn-primary" @onclick="()=>OnSingleObjectClick(1)">Send A</button>
    <button class="btn btn-primary" @onclick="()=>OnSingleObjectClick(2)">Send B</button>
    <button class="btn btn-primary" @onclick="()=>OnSingleObjectClick(3)">Send C</button>
</div>
<div>
    <button class="btn btn-success" @onclick="()=>OnClassClick(1)">Send 1</button>
    <button class="btn btn-success" @onclick="()=>OnClassClick(2)">Send 2</button>
    <button class="btn btn-success" @onclick="()=>OnClassClick(2)">Send 3</button>
</div>
<div>
    <button class="btn btn-secondary" @onclick="()=>OnClassClick(3)">Send String 1</button>
    <button class="btn btn-secondary" @onclick="()=>OnClassStringClick(1)">Send String 2</button>
    <button class="btn btn-secondary" @onclick="()=>OnClassStringClick(2)">Send String 3</button>
</div>

<SfGrid @ref="Grid" TValue="Order" AllowPaging="true">
    <SfDataManager Adaptor="Adaptors.CustomAdaptor">
        <CustomAdaptorComponent CurrentTypeCondition="@CurrentTypeCondition"
                                FilterClass="@FilterObject" />
    </SfDataManager>
    <GridPageSettings PageSize="12" />
    <GridColumns>
        <GridColumn Field=@nameof(Order.OrderID) HeaderText="Order ID" IsIdentity="true" IsPrimaryKey="true" TextAlign="TextAlign.Right" Width="120">
        </GridColumn>
        <GridColumn Field=@nameof(Order.CustomerID) HeaderText="Customer Name" Width="150"></GridColumn>
    </GridColumns>
</SfGrid>

@code{
    SfGrid<Order> Grid { get; set; }
    public static List<Order> Orders { get; set; }
    public string CurrentTypeCondition { get; set; }
    public FilterClass FilterObject { get; set; } = new FilterClass();

    async Task OnSingleObjectClick(int i)
    {
        Console.WriteLine();
        Console.WriteLine();
        OutputHelper.Output("CurrentTypeCondition 準備要變更了");

        #region 使用同步方式來進行變更 CurrentTypeCondition
        if (i == 1)
        {
            CurrentTypeCondition = "A";
        }
        else if (i == 2)
        {
            CurrentTypeCondition = "B";
        }
        else if (i == 3)
        {
            CurrentTypeCondition = "C";
        }
        OutputHelper.Output("CurrentTypeCondition 已經變更了");
        #endregion

        #region 狀況 1 : 沒做任何事情
        OutputHelper.Output("沒做任何事情");
        #endregion

        #region 狀況 2 : 使用 Task.Yield()
        //OutputHelper.Output("Raise Task.Yield");
        //await Task.Yield();
        #endregion

        #region 狀況 3 : 使用 Task.Delay(100)
        //OutputHelper.Output("Raise Task.Delay");
        //await Task.Delay(100);
        #endregion

        #region 狀況 4 : 使用 StateHasChanged
        OutputHelper.Output("Raise StateHasChanged");
        //StateHasChanged();
        #endregion

        OutputHelper.Output("Raise Grid.Refresh");
        Grid.Refresh();
    }

    async Task OnClassClick(int i)
    {
        Console.WriteLine();
        Console.WriteLine();
        OutputHelper.Output("FilterObject.Id 準備要變更了");

        #region 使用同步方式來進行變更 FilterObject.Id
        FilterObject.Id = i;
        OutputHelper.Output("FilterObject.Id 已經變更了");
        #endregion

        #region 狀況 1 : 沒做任何事情
        OutputHelper.Output("沒做任何事情");
        #endregion

        #region 狀況 2 : 使用 Task.Yield()
        //OutputHelper.Output("Raise Task.Yield");
        //await Task.Yield();
        #endregion

        #region 狀況 3 : 使用 Task.Delay(100)
        //OutputHelper.Output("Raise Task.Delay");
        //await Task.Delay(100);
        #endregion

        #region 狀況 4 : 使用 StateHasChanged
        //OutputHelper.Output("Raise StateHasChanged");
        //StateHasChanged();
        #endregion

        OutputHelper.Output("Raise Grid.Refresh");
        Grid.Refresh();
    }

    async Task OnClassStringClick(int i)
    {
        Console.WriteLine();
        Console.WriteLine();
        OutputHelper.Output("FilterObject.Title 準備要變更了");

        #region 使用同步方式來進行變更 FilterObject.Title
        FilterObject.Title = i.ToString();
        OutputHelper.Output("FilterObject.Title 已經變更了");
        #endregion


        #region 狀況 1 : 沒做任何事情
        OutputHelper.Output("沒做任何事情");
        #endregion

        #region 狀況 2 : 使用 Task.Yield()
        //OutputHelper.Output("Raise Task.Yield");
        //await Task.Yield();
        #endregion

        #region 狀況 3 : 使用 Task.Delay(100)
        //OutputHelper.Output("Raise Task.Delay");
        //await Task.Delay(100);
        #endregion

        #region 狀況 4 : 使用 StateHasChanged
        //OutputHelper.Output("Raise StateHasChanged");
        //StateHasChanged();
        #endregion

        OutputHelper.Output("Raise Grid.Refresh");
        Grid.Refresh();
    }
}

在這個元件內,將會透過 CustomAdaptorComponent 這個轉接器元件來取得集合物件紀錄,其中這個元件 CustomAdaptorComponent 程式碼如下:

@using Syncfusion.Blazor;
@using Syncfusion.Blazor.Data;

@inherits DataAdaptor<Order>

<CascadingValue Value="@this">
    @ChildContent
</CascadingValue>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    [Parameter]
    public FilterClass FilterClass { get; set; }

    private string myVar;

    [Parameter]
    public string CurrentTypeCondition
    {
        get { return myVar; }
        set
        {
            myVar = value;
            OutputHelper.Output($"CustomAdaptorComponent CurrentTypeCondition has changed {myVar}");
        }
    }

    public override async Task<object> ReadAsync(DataManagerRequest dataManagerRequest, string key = null)
    {

        #region 發出查詢要求
        List<Order> orders = new List<Order>();
        List<Order> resut = new List<Order>();
        for (int i = 0; i < 3000; i++)
        {
            orders.Add(new Order() { OrderID = i, CustomerID = $"客戶{i}" });
        }

        #region 使用 CurrentTypeCondition
        if (CurrentTypeCondition == "A")
        {
            resut = orders.Where(x => x.OrderID % 7 == 0).ToList();
        }
        else if (CurrentTypeCondition == "B")
        {
            resut = orders.Where(x => x.OrderID % 11 == 0).ToList();
        }
        else if (CurrentTypeCondition == "C")
        {
            resut = orders.Where(x => x.OrderID % 29 == 0).ToList();
        }
        else
        {
            resut = orders;
        }
        #endregion

        #region 使用 FilterClass.Id
        //if (FilterClass.Id == 1)
        //{
        //    resut = orders.Where(x => x.OrderID % 7 == 0).ToList();
        //}
        //else if (FilterClass.Id == 2)
        //{
        //    resut = orders.Where(x => x.OrderID % 11 == 0).ToList();
        //}
        //else if (FilterClass.Id == 3)
        //{
        //    resut = orders.Where(x => x.OrderID % 29 == 0).ToList();
        //}
        //else
        //{
        //    resut = orders;
        //}
        #endregion

        #region 使用 FilterClass.Title
        //if (FilterClass.Title == "1")
        //{
        //    resut = orders.Where(x => x.OrderID % 7 == 0).ToList();
        //}
        //else if (FilterClass.Title == "2")
        //{
        //    resut = orders.Where(x => x.OrderID % 11 == 0).ToList();
        //}
        //else if (FilterClass.Title == "3")
        //{
        //    resut = orders.Where(x => x.OrderID % 29 == 0).ToList();
        //}
        //else
        //{
        //    resut = orders;
        //}
        #endregion

        var myresult = resut.Take(12);
        var item = dataManagerRequest.RequiresCounts
            ? new DataResult() { Result = myresult, Count = resut.Count }
            : (object)orders;
        await Task.Yield();
        return item;
        #endregion
    }
}

在這裡有許多 region C# 程式碼,這裡將會針對不同情況來進行測試。

狀況 1 : 沒做任何事情

這裡有7種模擬狀況,請先將其他 #region 狀況內的程式碼註解起來,將 狀況 1 內的程式碼解除註解

        #region 狀況 1 : 沒做任何事情
        OutputHelper.Output("沒做任何事情");
        #endregion

現在,執行這個專案,執行結果如下圖

接著點選 [Send A] 按鈕,從 Console 視窗內將會看到底下的輸出內容

CurrentTypeCondition 準備要變更了 (Thread:6) [SC:Yes]
CurrentTypeCondition 已經變更了 (Thread:6) [SC:Yes]
沒做任何事情 (Thread:6) [SC:Yes]
Raise Grid.Refresh (Thread:6) [SC:Yes]
CustomAdaptorComponent CurrentTypeCondition has changed  (Thread:6) [SC:Yes]
CustomAdaptorComponent CurrentTypeCondition has changed A (Thread:6) [SC:Yes]
CustomAdaptorComponent CurrentTypeCondition has changed A (Thread:17) [SC:Yes]
CustomAdaptorComponent CurrentTypeCondition has changed A (Thread:17) [SC:Yes]

在 Console 輸出內容可以看出, CurrentTypeCondition 變數值變動之後,再呼叫 Grid.Refesh() 方法之前,這個要傳遞到元件內的參數,是沒有產生,因為可以看到 Raise Grid.Refresh (Thread:6) [SC:Yes] 訊息出現之後,可以看到 CustomAdaptorComponent CurrentTypeCondition has changed A (Thread:6) [SC:Yes] 出現在 Console 視窗內。

可以看看瀏覽器顯示的內容,原則上,當按下 [Send A] 這個按鈕之後, 僅會顯示 OrderId 有 7 的質數的紀錄才會顯示出來,不過,卻沒有看到任何變化,這是因為當呼救 Grid.Refresh 這個方法的時候,所傳遞過去的參數沒有啟用作用。

現在,請再點選一次 [Send A] 這個按鈕

從瀏覽器畫面可以看出,現在的 Order ID 就僅會出現具有 7 這個質數的訂單紀錄

CurrentTypeCondition 準備要變更了 (Thread:6) [SC:Yes]
CurrentTypeCondition 已經變更了 (Thread:6) [SC:Yes]
沒做任何事情 (Thread:6) [SC:Yes]
Raise Grid.Refresh (Thread:6) [SC:Yes]
CustomAdaptorComponent CurrentTypeCondition has changed A (Thread:6) [SC:Yes]
CustomAdaptorComponent CurrentTypeCondition has changed A (Thread:6) [SC:Yes]
CustomAdaptorComponent CurrentTypeCondition has changed A (Thread:10) [SC:Yes]
CustomAdaptorComponent CurrentTypeCondition has changed A (Thread:10) [SC:Yes]

再度觀察 Console 視窗的輸出內容,其實已經明白了,因為在第一次按下按鈕之後,當該按鈕委派事件方法執行完畢之後,在同步內容訊息佇列內還有一個委派方法,當這個方法執行完畢之後,就已經將變更的物件,傳遞到元件內的參數變數內。

而第二次按下同一個按鈕之後,就直接使用這個內容來顯示到瀏覽器上。

可是,當點選 Send B 按鈕,應該要出現 Order ID 具有 11 的質數紀錄,這些紀錄要顯示在網頁上,可是卻沒有,然而,再次點選一次,就會看到預期的內容,如下圖

狀況 2 : 使用 Task.Yield()

這裡有7種模擬狀況,請先將其他 #region 狀況內的程式碼註解起來,將 狀況 2 內的程式碼解除註解

        #region 狀況 2 : 使用 Task.Yield()
        OutputHelper.Output("Raise Task.Yield");
        await Task.Yield();
        #endregion

現在,執行這個專案,執行結果如下圖

接著點選 [Send A] 按鈕,從 Console 視窗內將會看到底下的輸出內容

CurrentTypeCondition 準備要變更了 (Thread:16) [SC:Yes]
CurrentTypeCondition 已經變更了 (Thread:16) [SC:Yes]
Raise Task.Yield (Thread:16) [SC:Yes]
CustomAdaptorComponent CurrentTypeCondition has changed  (Thread:16) [SC:Yes]
CustomAdaptorComponent CurrentTypeCondition has changed A (Thread:16) [SC:Yes]
Raise Grid.Refresh (Thread:11) [SC:Yes]
CustomAdaptorComponent CurrentTypeCondition has changed A (Thread:11) [SC:Yes]
CustomAdaptorComponent CurrentTypeCondition has changed A (Thread:11) [SC:Yes]
CustomAdaptorComponent CurrentTypeCondition has changed A (Thread:11) [SC:Yes]
CustomAdaptorComponent CurrentTypeCondition has changed A (Thread:11) [SC:Yes]

在 Console 輸出內容可以看出, CurrentTypeCondition 變數值變動之後,再呼叫 Grid.Refesh() 方法之前,有看到這個訊息 CustomAdaptorComponent CurrentTypeCondition has changed A (Thread:16) [SC:Yes] ,這表示了元件內的參數綁定已經生效了,此時可以從網頁上看到,這裡出現了正確且與其的結果。






沒有留言:

張貼留言