C# در مرورگر با Blazor
Blazor is the new Microsoft experimental framework that brings C# into any browser without a plug-in. It holds the promise of modern single-page applications, combined with the ability to use C# and its vast base-class library. Blazor takes C# development to a new level. It’s the final piece necessary to make the language a full-stack development tool. It will have all the power of the popular JavaScript frameworks, but based on the familiar languages, APIs and tooling of the Microsoft .NET Framework.
نصب و راه اندازی مقدماتی Full Text Search
بررسیهای مقدماتی
ابتدای کار نیاز است بررسی کنیم آیا افزونهی Full Text Search، به همراه SQL Server نصب شدهاست یا خیر. برای این منظور کوئری ذیل را اجرا کنید:
select SERVERPROPERTY('IsFullTextInstalled');
instance features -> database engine services -> Full Text
راه اندازی سرویس Full Text Search
پیش از ادامهی بحث، به کنسول سرویسهای ویندوز مراجعه کرده و مطمئن شوید که سرویس SQL Full-text Filter Daemon Launcher MSSQLSERVER نیز در حال اجرا است. در غیراینصورت با خطای ذیل مواجه خواهید شد:
SQL Server encountered error 0x80070422 while communicating with full-text filter daemon host (FDHost) process.
sp_fulltext_service 'restart_all_fdhosts'
چه نوع دادههایی را میتوان توسط FTS ایندکس کرد؟
با استفاده از امکانات FTS میتوان کلیه ستونهایی را که دارای نوعهای ذیل باشند، ایندکس کرد:
char, nchar, varchar, nvarchar, text, ntext, image, xml, varbinary(max)
همچنین FTS برای پردازش این فایلهای باینری و ایندکس کردن اطلاعات آنها، نیاز به افزونههایی به نام IFilters دارد. کار این فیلترها استخراج متن بدون فرمت، از فایلهای باینری مرتبط و ارائهی آنها به موتور FTS میباشد.
نصب فیلترهای مخصوص FTS آفیس
اگر علاقمند هستید که بدانید در حال حاضر چه تعداد فیلترهای FTS بر روی سیستم شما نصب شدهاست، کوئری ذیل را اجرا نمائید:
exec sys.sp_help_fulltext_system_components 'filter';
فیلترهای آفیس را جداگانه نیز میتوانید دریافت و نصب کنید (بدون نیاز به نصب کامل آفیس بر روی سرور):
این فیلترها تا نگارش 2013 آفیس را نیز پشتیبانی میکنند و آگر آپدیت ویندوز نیز روشن باشد، سرویس پک 2 آن را نیز دریافت خواهید کرد.
پس از اینکه فیلترها را نصب کردید، باید آنها را در وهلهی جاری SQL Server ثبت کرد:
exec sys.sp_fulltext_service 'load_os_resources', 1; EXEC sp_fulltext_service 'update_languages'; EXEC sp_fulltext_service 'restart_all_fdhosts';
select * from sys.fulltext_document_types;
فیلترهای فوق علاوه بر اینکه امکان FTS را بر روی کلیه فایلهای مجموعه آفیس میسر میکنند، امکان جستجو FTS را بر روی خواص ویژه اضافی آنها، مانند نام نویسنده، واژههای کلیدی، تاریخ ایجاد و امثال آن نیز به همراه دارند.
FTS چگونه کار میکند؟
زبانهای پشتیبانی شده توسط FTS را توسط کوئری ذیل میتوانید مشاهده کنید:
select lcid, name from sys.fulltext_languages order by name;
word-breakers تک تک کلمات را (که به آنها token نیز گفته میشود) تشخیص داده و سپس FTS آنها را با فرمتی فشرده شده، درون ایندکسهای مخصوص خود ذخیره میکند.کار stemmers تولید حالات inflectional (صرفی) یک کلمه بر اساس دستور زبانی مشخص است.
اهمیت آنالیز inflectional، در اینجا است که برای مثال اگر در متنی واژهی jumps وجود داشت و کاربر در حین جستجو، jumped را وارد کرد، FTS بر اساس دستور زبان مورد استفاده، پیشتر، حالات مختلف صرفی jump را ذخیره کردهاست و امکان انجام یک چنین کوئری پیشرفتهای را پیدا میکند.
نصب و فعال سازی Semantic Language Database
کار TFS تنها به خرد کردن واژهها و آنالیز صرفی آنها خلاصه نمیشود. در مرحلهی بعد، انجام Statistical semantic search میسر میشود. در اینجا SQL Server بر اساس آمار واژههای کلیدی استخراج شده، توانایی یافتن متونی مشابه و یا مرتبط را پیدا میکند. Semantic Search جزو تازههای SQL Server 2012 است.
برای اینکار نیاز است بانک اطلاعاتی Semantic language statistics نیز نصب شود. برای اطمینان از نصب بودن آن، کوئری ذیل را اجرا کنید:
select * from sys.fulltext_semantic_language_statistics_database;
x64\Setup\SemanticLanguageDatabase.msi x86\Setup\SemanticLanguageDatabase.msi
پس از آن نیاز است این بانک اطلاعاتی را Attach و همچنین ثبت کرد:
CREATE DATABASE semanticsdb ON ( FILENAME = 'D:\SQL_Data\SemanticLanguageDatabase\semanticsdb.mdf' ) LOG ON ( FILENAME = 'D:\SQL_Data\SemanticLanguageDatabase\semanticsdb_log.ldf' ) FOR ATTACH GO EXEC sp_fulltext_semantic_register_language_statistics_db @dbname = N'semanticsdb' GO
پس از مراحل فوق، اگر مجددا کوئری یاد شده بر روی sys.fulltext_semantic_language_statistics_database را اجرا کنید، یک سطر خروجی خواهد داشت.
ایجاد یک پروژهی جدید Blazor WASM
برای تکمیل پیاده سازی قسمت سمت کلاینت پروژهی این سری، نیاز به یک پروژهی جدید Blazor WASM را داریم که میتوان آنرا با اجرای دستور dotnet new blazorwasm در یک پوشهی خالی، ایجاد کرد. کدهای این پروژه را میتوانید در پوشهی HotelManagement\BlazorWasm\BlazorWasm.Client فایل پیوستی انتهای بحث مشاهده کنید.
افزودن فایلهای جاوااسکریپتی مورد نیاز
شبیه به کاری که در مطلب «Blazor 5x - قسمت یازدهم - مبانی Blazor - بخش 8 - کار با جاوا اسکریپت» انجام دادیم، در اینجا هم قصد افزودن یکسری کتابخانهی جاوااسکریپتی و CSS ای را داریم که توسط LibMan آنها را مدیریت خواهیم کرد.
- بنابراین در ابتدا به پوشهی BlazorWasm.Client\wwwroot\css وارد شده و پوشههای پیشفرض bootstrap و open-iconic آنرا حذف میکنیم؛ چون تحت مدیریت هیچ package manager ای نیستند و در این حالت، مدیریت به روز رسانی و یا بازیابی آنها به صورت خودکار میسر نیست.
- سپس فایل wwwroot\css\app.css را هم ویرایش کرده و سطر زیر را از ابتدای آن حذف میکنیم:
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
dotnet tool update -g Microsoft.Web.LibraryManager.Cli libman init libman install bootstrap --provider unpkg --destination wwwroot/lib/bootstrap libman install open-iconic --provider unpkg --destination wwwroot/lib/open-iconic libman install jquery --provider unpkg --destination wwwroot/lib/jquery libman install toastr --provider unpkg --destination wwwroot/lib/toastr
- بعد از نصب بستههای ذکر شده، فایل wwwroot\index.html را به صورت زیر به روز رسانی میکنیم تا به مسیرهای جدید بستههای CSS و JS نصب شده، اشاره کند:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <title>BlazorWasm.Client</title> <base href="/" /> <link href="lib/toastr/build/toastr.min.css" rel="stylesheet" /> <link href="lib/open-iconic/font/css/open-iconic-bootstrap.min.css" rel="stylesheet" /> <link href="lib/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" /> <link href="css/app.css" rel="stylesheet" /> <link href="BlazorWasm.Client.styles.css" rel="stylesheet" /> </head> <body> <div id="app">Loading...</div> <div id="blazor-error-ui"> An unhandled error has occurred. <a href="" class="reload">Reload</a> <a class="dismiss">🗙</a> </div> <script src="lib/jquery/dist/jquery.min.js"></script> <script src="lib/toastr/build/toastr.min.js"></script> <script src="js/common.js"></script> <script src="_framework/blazor.webassembly.js"></script> </body> </html>
- محتویات فایل wwwroot\css\app.css را هم به صورت زیر تغییر میدهیم تا یک spinner و شیوه نامههای نمایش تصاویر، به آن اضافه شوند:
.valid.modified:not([type="checkbox"]) { outline: 1px solid #26b050; } .invalid { outline: 1px solid red; } .validation-message { color: red; } #blazor-error-ui { background: lightyellow; bottom: 0; box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); display: none; left: 0; padding: 0.6rem 1.25rem 0.7rem 1.25rem; position: fixed; width: 100%; z-index: 1000; } #blazor-error-ui .dismiss { cursor: pointer; position: absolute; right: 0.75rem; top: 0.5rem; } .spinner { border: 16px solid silver !important; border-top: 16px solid #337ab7 !important; border-radius: 50% !important; width: 80px !important; height: 80px !important; animation: spin 700ms linear infinite !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%); position: absolute !important; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .room-image { display: block; width: 100%; height: 150px; background-size: cover !important; border: 3px solid green; position: relative; } .room-image-title { position: absolute; top: 0; right: 0; background-color: green; color: white; padding: 0px 6px; display: inline-block; }
window.ShowToastr = (type, message) => { if (type === "success") { toastr.success(message, "Operation Successful", { timeOut: 10000 }); } if (type === "error") { toastr.error(message, "Operation Failed", { timeOut: 10000 }); } };
- در قسمت 11، در بخش «کاهش کدهای تکراری فراخوانی متدهای جاوا اسکریپتی با تعریف متدهای الحاقی» آن، کلاس JSRuntimeExtensions را تعریف کردیم که سبب کاهش تکرار کدهای استفاده از تابع ShowToastr میشود. این فایلرا در پروژهی BlazorServer.App\Utils\JSRuntimeExtensions.cs این سری نیز استفاده کردیم. یا میتوان مجددا آنرا به پروژهی جاری کپی کرد؛ یا آنرا در یک پروژهی اشتراکی قرار داد. برای مثال اگر آنرا به پوشهی BlazorWasm.Client\Utils کپی کردیم، نیاز است فضای نام آنرا اصلاح کرده و سپس آنرا به انتهای فایل BlazorWasm.Client\_Imports.razor نیز اضافه کنیم تا در تمام کامپوننتهای برنامه قابل استفاده شود:
@using BlazorWasm.Client.Utils
تغییر و ساده سازی منوی برنامهی کلاینت
در برنامهی کلاینت جاری دیگر نمیخواهیم منوی پیشفرض سمت چپ صفحه را شاهد باشیم. به همین جهت ابتدا فایل Shared\MainLayout.razor را به صورت زیر ساده میکنیم:
@inherits LayoutComponentBase <NavMenu /> <div> @Body </div>
<nav class="navbar navbar-expand-sm navbar-dark bg-dark p-0"> <a class="navbar-brand mx-4" href="#">Navbar</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse pr-2" id="navbarSupportedContent"> <ul class="navbar-nav mr-auto"></ul> <ul class="my-0 navbar-nav"> <li class="nav-item p-0"> <NavLink class="nav-link" href="registration"> <span class="p-2"> Register </span> </NavLink> </li> <li class="nav-item p-0"> <NavLink class="nav-link" href="login"> <span class="p-2"> Login </span> </NavLink> </li> </ul> </div> </nav>
تغییر محتوای صفحهی آغازین برنامه
صفحهی ابتدایی برنامه، یعنی کامپوننت Pages\Index.razor را نیز به صورت زیر تغییر میدهیم:
@page "/" <form> <div class="row p-0 mx-0 mt-4"> <div class="col-12 col-md-5 offset-md-1 pl-2 pr-2 pr-md-0"> <div class="form-group"> <label>Check In Date</label> <input type="text" class="form-control" /> </div> </div> <div class="col-8 col-md-3 pl-2 pr-2"> <div class="form-group"> <label>No. of nights</label> <select class="form-control"> @for (var i = 1; i <= 10; i++) { <option value="@i">@i</option> } </select> </div> </div> <div class="col-4 col-md-2 p-0 pr-2"> <div class="form-group"> <label> </label> <input type="submit" value="Go" class="btn btn-success btn-block" /> </div> </div> </div> </form>
تعریف View Model رابط کاربری Pages\Index.razor
پس از تعریف محتوای ثابت برنامه، اکنون نوبت به پویا سازی آن است. به همین جهت نیاز است مدلی را برای صفحهی آغازین برنامه تعریف کرد تا بتوان فرم آنرا به این مدل متصل کرد. این مدل چون مختص به برنامهی کلاینت است، آنرا در پوشهی جدید Models\ViewModels ایجاد میکنیم:
using System; namespace BlazorWasm.Client.Models.ViewModels { public class HomeVM { public DateTime StartDate { get; set; } = DateTime.Now; public DateTime EndDate { get; set; } public int NoOfNights { get; set; } = 1; } }
پس از این تعریف، بهتر است فضای نام آنرا نیز به فایل BlazorWasm.Client\_Imports.razor افزود، تا کار با آن در کامپوننتهای برنامه، سادهتر شود:
using BlazorWasm.Client.Models.ViewModels
- ابتدا فیلدی که ارائه کنندهی شیء ViewModel فرم است را تعریف میکنیم:
@code{ HomeVM HomeModel = new HomeVM(); }
<EditForm Model="HomeModel"> // ... </EditForm>
<InputDate min="@DateTime.Now.ToString("yyyy-MM-dd")" @bind-Value="HomeModel.StartDate" type="text" class="form-control" />
<select @bind="HomeModel.NoOfNights">
تعریف Local Storage سمت کلاینت
در ادامه میخواهیم اگر کاربری زمان شروع رزرو اتاقی را به همراه تعداد شب مدنظر، انتخاب کرد، با کلیک بر روی دکمهی Go، به یک صفحهی مشاهدهی جزئیات منتقل شود. بنابراین نیاز داریم تا اطلاعات انتخابی کاربر را به نحوی ذخیره سازی کنیم. برای یک چنین سناریوی سمت کلاینتی، میتوان از local storage استاندارد مرورگرها استفاده کرد که امکان کار آفلاین با برنامه را نیز فراهم میکند.
برای این منظور کتابخانهای به نام Blazored.LocalStorage طراحی شدهاست که پس از نصب آن توسط دستور زیر:
dotnet add package Blazored.LocalStorage
namespace BlazorWasm.Client { public class Program { public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); // ... builder.Services.AddBlazoredLocalStorage(); // ... } } }
@using Blazored.LocalStorage
@page "/" @inject ILocalStorageService LocalStorage @inject IJSRuntime JsRuntime <EditForm Model="HomeModel" OnValidSubmit="SaveInitialData">
@code{ HomeVM HomeModel = new HomeVM(); private async Task SaveInitialData() { try { HomeModel.EndDate = HomeModel.StartDate.AddDays(HomeModel.NoOfNights); await LocalStorage.SetItemAsync("InitialRoomBookingInfo", HomeModel); } catch (Exception e) { await JsRuntime.ToastrError(e.Message); } } }
برای مثال اگر تاریخ و عددی را انتخاب کنیم، نتیجهی حاصل از کلیک بر روی دکمهی Go را میتوان در قسمت Local storage مرورگر جاری مشاهده کرد:
البته با توجه به اینکه میخواهیم از کلید InitialRoomBookingInfo در سایر کامپوننتهای برنامه نیز استفاده کنیم، بهتر است آنرا به یک پروژهی مشترک مانند BlazorServer.Common که پیشتر نام نقشهایی مانند Admin را در آن تعریف کردیم، منتقل کنیم:
namespace BlazorServer.Common { public static class ConstantKeys { public const string LocalInitialBooking = "InitialRoomBookingInfo"; } }
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"> <ItemGroup> <ProjectReference Include="..\..\BlazorServer\BlazorServer.Common\BlazorServer.Common.csproj" /> </ItemGroup> </Project>
@using BlazorServer.Common
await LocalStorage.SetItemAsync(ConstantKeys.LocalInitialBooking, HomeModel);
در آخر قصد داریم با کلیک بر روی Go، به یک صفحهی جدید مانند نمایش لیست اتاقها هدایت شویم. به همین جهت کامپوننت جدید Pages\HotelRooms\HotelRooms.razor را ایجاد میکنیم:
@page "/hotel/rooms" <h3>HotelRooms</h3> @code { }
@page "/" @inject ILocalStorageService LocalStorage @inject IJSRuntime JsRuntime @inject NavigationManager NavigationManager @code{ HomeVM HomeModel = new HomeVM(); private async Task SaveInitialData() { try { HomeModel.EndDate = HomeModel.StartDate.AddDays(HomeModel.NoOfNights); await LocalStorage.SetItemAsync(ConstantKeys.LocalInitialBooking, HomeModel); NavigationManager.NavigateTo("hotel/rooms"); } catch (Exception e) { await JsRuntime.ToastrError(e.Message); } } }
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-26.zip
ChatGPT خودتان را با #C درست کنید
امکانات جدید در فایرفاکس کوانتم
Firefox Quantum is over twice as fast as Firefox from 6 months ago, built on a completely overhauled core engine with brand new technology stolen from our advanced research group, and graced with a beautiful new look designed to get out of the way and let you do what you do best: surf a ton of pages, open a zillion tabs, all guilt free because Firefox Quantum uses less memory than the competition