SiamCafe.net Blog
Technology

TensorFlow Serving GreenOps Sustainability — ML Serving ที่ยั่งยืน

tensorflow serving greenops sustainability
TensorFlow Serving GreenOps Sustainability | SiamCafe Blog
2026-01-10· อ. บอม — SiamCafe.net· 1,475 คำ
C# Blazor กับ Low Code No Code — วิธีใช้ Blazor สร้าง Web App และ Low Code Platform | SiamCafe Blog เรียนรู้การใช้ C# Blazor สร้าง Web Application ร่วมกับแนวคิด Low Code/No Code ตั้งแต่ Component Design, Data Binding, Authentication ไปจนถึง Drag-and-Drop UI Builder FAQ_Q:Blazor คืออะไร FAQ_A:Blazor เป็น Web Framework จาก Microsoft ที่ให้เขียน Web App ด้วย C# แทน JavaScript มี 2 แบบ Blazor Server ประมวลผลบน Server ส่ง UI Updates ผ่าน SignalR และ Blazor WebAssembly (WASM) รันบน Browser โดยตรง ใช้ .NET Runtime ใน Browser ไม่ต้องเขียน JavaScript FAQ_Q:Low Code/No Code คืออะไร FAQ_A:Low Code คือ Platform ที่ให้สร้าง Application ด้วยการลาก Drop Components และเขียน Code น้อยที่สุด No Code ไม่ต้องเขียน Code เลย ใช้ Visual Builder ทั้งหมด เหมาะกับ Business Users ที่ไม่มีพื้นฐาน Programming ตัวอย่าง Power Apps, OutSystems, Mendix, Bubble FAQ_Q:Blazor Server กับ Blazor WASM ต่างกันอย่างไร FAQ_A:Blazor Server ประมวลผลบน Server ส่ง DOM Updates ผ่าน SignalR WebSocket โหลดเร็ว ใช้ Resources น้อยบน Client แต่ต้องมี Connection ตลอด Blazor WASM รันบน Browser ด้วย WebAssembly ทำงาน Offline ได้ แต่ Initial Load ช้ากว่า ไม่ต้องพึ่ง Server Connection FAQ_Q:Blazor เหมาะกับงานแบบไหน FAQ_A: เหมาะกับทีมที่ถนัด C#/.NET ไม่ต้องเรียน JavaScript Framework ใหม่ Enterprise Apps ที่ต้องการ Type Safety, Internal Tools, Dashboard, Admin Panel, Forms-heavy Applications ใช้ Component Library เช่น MudBlazor, Radzen, Syncfusion ได้ BODY_START

Blazor Web Framework

Blazor เป็น Web Framework จาก Microsoft ที่ให้เขียน Web App ด้วย C# แทน JavaScript มี Component-based Architecture เหมือน React/Vue แต่ใช้ C# และ Razor Syntax รองรับทั้ง Server-side และ Client-side (WebAssembly)

Low Code/No Code เป็นแนวคิดที่ให้สร้าง Application ด้วยการลาก Drop Components เขียน Code น้อยที่สุดหรือไม่ต้องเขียนเลย Blazor สามารถสร้าง Low Code Platform ได้ด้วย Dynamic Components และ Drag-and-Drop

Blazor Application Development

// === Blazor Web App ===
// dotnet new blazor -n MyBlazorApp --interactivity Auto
// cd MyBlazorApp

// === Components/UserList.razor ===
@page "/users"
@using MyBlazorApp.Models
@using MyBlazorApp.Services
@inject IUserService UserService
@inject NavigationManager Navigation

<PageTitle>Users</PageTitle>

<h3>User Management</h3>

@if (_loading)
{
    <div class="spinner-border" role="status">
        <span class="visually-hidden">Loading...</span>
    </div>
}
else
{
    <div class="mb-3">
        <input type="text" class="form-control"
               placeholder="Search users..."
               @bind-value="_searchText"
               @bind-value:event="oninput"
               @onkeyup="OnSearch" />
    </div>

    <table class="table table-striped">
        <thead>
            <tr>
                <th @onclick='() => SortBy("Name")'>Name</th>
                <th @onclick='() => SortBy("Email")'>Email</th>
                <th>Role</th>
                <th>Actions</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var user in _filteredUsers)
            {
                <tr>
                    <td>@user.Name</td>
                    <td>@user.Email</td>
                    <td><span class="badge bg-primary">@user.Role</span></td>
                    <td>
                        <button class="btn btn-sm btn-outline-primary"
                                @onclick="() => EditUser(user.Id)">Edit</button>
                        <button class="btn btn-sm btn-outline-danger"
                                @onclick="() => DeleteUser(user.Id)">Delete</button>
                    </td>
                </tr>
            }
        </tbody>
    </table>

    <p>Total: @_filteredUsers.Count users</p>
}

@code {
    private List<User> _users = new();
    private List<User> _filteredUsers = new();
    private string _searchText = "";
    private bool _loading = true;
    private string _sortField = "Name";
    private bool _sortAsc = true;

    protected override async Task OnInitializedAsync()
    {
        _users = await UserService.GetUsersAsync();
        _filteredUsers = _users;
        _loading = false;
    }

    private void OnSearch()
    {
        _filteredUsers = string.IsNullOrEmpty(_searchText)
            ? _users
            : _users.Where(u =>
                u.Name.Contains(_searchText, StringComparison.OrdinalIgnoreCase) ||
                u.Email.Contains(_searchText, StringComparison.OrdinalIgnoreCase))
              .ToList();
    }

    private void SortBy(string field)
    {
        if (_sortField == field) _sortAsc = !_sortAsc;
        else { _sortField = field; _sortAsc = true; }

        _filteredUsers = _sortField switch
        {
            "Name" => _sortAsc
                ? _filteredUsers.OrderBy(u => u.Name).ToList()
                : _filteredUsers.OrderByDescending(u => u.Name).ToList(),
            "Email" => _sortAsc
                ? _filteredUsers.OrderBy(u => u.Email).ToList()
                : _filteredUsers.OrderByDescending(u => u.Email).ToList(),
            _ => _filteredUsers,
        };
    }

    private void EditUser(int id) => Navigation.NavigateTo($"/users/edit/{id}");

    private async Task DeleteUser(int id)
    {
        await UserService.DeleteUserAsync(id);
        _users = await UserService.GetUsersAsync();
        OnSearch();
    }
}

Low Code Component Builder

// === Low Code Dynamic Form Builder ===
// Components/FormBuilder.razor

// DynamicField.cs — Field Definition
namespace MyBlazorApp.LowCode;

public class DynamicField
{
    public string Name { get; set; } = "";
    public string Label { get; set; } = "";
    public string Type { get; set; } = "text";  // text, number, email, select, checkbox, textarea
    public bool Required { get; set; }
    public string Placeholder { get; set; } = "";
    public List<string> Options { get; set; } = new();  // สำหรับ Select
    public string DefaultValue { get; set; } = "";
    public int? MinLength { get; set; }
    public int? MaxLength { get; set; }
    public string ValidationMessage { get; set; } = "";
}

public class FormDefinition
{
    public string Title { get; set; } = "";
    public string Description { get; set; } = "";
    public List<DynamicField> Fields { get; set; } = new();
    public string SubmitUrl { get; set; } = "";
    public string SubmitMethod { get; set; } = "POST";
}

// FormRenderer.razor — Render Dynamic Form
// @using MyBlazorApp.LowCode
//
// <EditForm Model="_formData" OnValidSubmit="OnSubmit">
//     <h4>@Definition.Title</h4>
//     <p>@Definition.Description</p>
//
//     @foreach (var field in Definition.Fields)
//     {
//         <div class="mb-3">
//             <label class="form-label">@field.Label</label>
//
//             @switch (field.Type)
//             {
//                 case "text":
//                 case "email":
//                 case "number":
//                     <input type="@field.Type"
//                            class="form-control"
//                            placeholder="@field.Placeholder"
//                            @bind="_formData[field.Name]"
//                            required="@field.Required" />
//                     break;
//
//                 case "textarea":
//                     <textarea class="form-control"
//                               rows="4"
//                               @bind="_formData[field.Name]"></textarea>
//                     break;
//
//                 case "select":
//                     <select class="form-select" @bind="_formData[field.Name]">
//                         <option value="">-- Select --</option>
//                         @foreach (var opt in field.Options)
//                         {
//                             <option value="@opt">@opt</option>
//                         }
//                     </select>
//                     break;
//
//                 case "checkbox":
//                     <input type="checkbox" class="form-check-input"
//                            @bind="_formData[field.Name]" />
//                     break;
//             }
//         </div>
//     }
//
//     <button type="submit" class="btn btn-primary">Submit</button>
// </EditForm>

// ตัวอย่าง Form Definition (JSON)
var contactForm = new FormDefinition
{
    Title = "Contact Form",
    Description = "กรอกข้อมูลติดต่อ",
    SubmitUrl = "/api/contacts",
    Fields = new List<DynamicField>
    {
        new() { Name = "name", Label = "ชื่อ", Type = "text", Required = true },
        new() { Name = "email", Label = "Email", Type = "email", Required = true },
        new() { Name = "phone", Label = "โทรศัพท์", Type = "text" },
        new() { Name = "department", Label = "แผนก", Type = "select",
                 Options = new() { "Sales", "Support", "Engineering" } },
        new() { Name = "message", Label = "ข้อความ", Type = "textarea", Required = true },
        new() { Name = "subscribe", Label = "รับข่าวสาร", Type = "checkbox" },
    },
};

Console.WriteLine($"Form: {contactForm.Title}");
Console.WriteLine($"Fields: {contactForm.Fields.Count}");
foreach (var f in contactForm.Fields)
    Console.WriteLine($"  {f.Name} ({f.Type}) {(f.Required ? "*" : "")}");

Blazor Component Library

// === Reusable Blazor Components ===
// Components/Shared/DataTable.razor

// DataTable<T> — Generic Data Table Component
// @typeparam T
//
// <div class="table-responsive">
//     @if (ShowSearch)
//     {
//         <input type="text" class="form-control mb-3"
//                placeholder="Search..."
//                @bind-value="SearchText"
//                @bind-value:event="oninput" />
//     }
//
//     <table class="table table-hover">
//         <thead>
//             <tr>
//                 @foreach (var col in Columns)
//                 {
//                     <th style="cursor:pointer"
//                         @onclick="() => Sort(col.Field)">
//                         @col.Title
//                         @if (_sortField == col.Field)
//                         {
//                             <span>@(_sortAsc ? "▲" : "▼")</span>
//                         }
//                     </th>
//                 }
//             </tr>
//         </thead>
//         <tbody>
//             @foreach (var item in PagedItems)
//             {
//                 <tr @onclick="() => OnRowClick.InvokeAsync(item)"
//                     style="cursor:pointer">
//                     @RowTemplate(item)
//                 </tr>
//             }
//         </tbody>
//     </table>
//
//     <nav>
//         <ul class="pagination">
//             @for (var i = 1; i <= TotalPages; i++)
//             {
//                 var page = i;
//                 <li class="page-item @(page == CurrentPage ? "active" : "")">
//                     <a class="page-link" @onclick="() => GoToPage(page)">@page</a>
//                 </li>
//             }
//         </ul>
//     </nav>
// </div>
//
// @code {
//     [Parameter] public List<T> Items { get; set; } = new();
//     [Parameter] public List<ColumnDef> Columns { get; set; } = new();
//     [Parameter] public RenderFragment<T> RowTemplate { get; set; }
//     [Parameter] public EventCallback<T> OnRowClick { get; set; }
//     [Parameter] public bool ShowSearch { get; set; } = true;
//     [Parameter] public int PageSize { get; set; } = 10;
//
//     private string SearchText = "";
//     private int CurrentPage = 1;
//     private string _sortField = "";
//     private bool _sortAsc = true;
//
//     private int TotalPages => (int)Math.Ceiling(Items.Count / (double)PageSize);
//     private List<T> PagedItems => Items
//         .Skip((CurrentPage - 1) * PageSize)
//         .Take(PageSize).ToList();
//
//     private void GoToPage(int page) => CurrentPage = page;
//     private void Sort(string field) { /* sorting logic */ }
// }

// ColumnDef.cs
public class ColumnDef
{
    public string Field { get; set; } = "";
    public string Title { get; set; } = "";
    public string Width { get; set; } = "";
    public bool Sortable { get; set; } = true;
}

// ใช้งาน
// <DataTable Items="users" Columns="columns" PageSize="20">
//     <RowTemplate Context="user">
//         <td>@user.Name</td>
//         <td>@user.Email</td>
//         <td>@user.Role</td>
//     </RowTemplate>
// </DataTable>

Console.WriteLine("Blazor Component Library:");
Console.WriteLine("  DataTable<T> — Generic sortable, searchable, paginated table");
Console.WriteLine("  FormBuilder — Dynamic form from JSON definition");
Console.WriteLine("  DragDrop — Drag and drop UI builder");
Console.WriteLine("  ChartWidget — Chart.js wrapper for Blazor");

Best Practices

Blazor คืออะไร

Web Framework จาก Microsoft เขียน Web App ด้วย C# แทน JavaScript มี Blazor Server ประมวลผลบน Server SignalR และ Blazor WASM รันบน Browser WebAssembly ไม่ต้องเขียน JavaScript

Low Code/No Code คืออะไร

Platform สร้าง Application ลาก Drop Components เขียน Code น้อย No Code ไม่ต้องเขียนเลย Visual Builder เหมาะ Business Users ตัวอย่าง Power Apps OutSystems Mendix Bubble

Blazor Server กับ Blazor WASM ต่างกันอย่างไร

Server ประมวลผลบน Server DOM Updates SignalR WebSocket โหลดเร็ว ต้อง Connection WASM รันบน Browser WebAssembly Offline ได้ Initial Load ช้ากว่า ไม่ต้องพึ่ง Server

Blazor เหมาะกับงานแบบไหน

ทีมถนัด C#/.NET ไม่ต้องเรียน JavaScript Enterprise Apps Type Safety Internal Tools Dashboard Admin Panel Forms-heavy ใช้ MudBlazor Radzen Syncfusion

สรุป

Blazor ให้เขียน Web App ด้วย C# ใช้ Component-based Architecture เหมือน React/Vue เมื่อรวมกับ Low Code สร้าง Dynamic Forms จาก JSON Definition Drag-and-Drop UI Builder Generic Components ที่ใช้ซ้ำได้ เหมาะกับ Enterprise Apps ที่ทีมถนัด .NET

📖 บทความที่เกี่ยวข้อง

TensorFlow Serving Hexagonal Architectureอ่านบทความ → Htmx Alpine.js GreenOps Sustainabilityอ่านบทความ → TensorFlow Serving Network Segmentationอ่านบทความ → TensorFlow Serving Multi-cloud Strategyอ่านบทความ → TensorFlow Serving Domain Driven Design DDDอ่านบทความ →

📚 ดูบทความทั้งหมด →