مطالب
پیاده سازی Full-Text Search با SQLite و EF Core - قسمت سوم - بهبود کیفیت جستجوهای FTS توسط یک غلط یاب املایی
فرض کنید کاربری برای جستجوی رکورد زیر:
context.Chapters.Add(new Chapter
{
    Title = "آزمایش متن فارسی",
    Text = "برای نمونه تهیه شده‌است",
    User = user1.Entity
});
بجای «فارسی»، واژه‌ی «فارشی» را وارد کند و یا بجای «آزمایش»، بنویسد «آزمایس». در هر دو حالت نتیجه‌ی جستجوی او خروجی را به همراه نخواهد داشت. برای بهبود تجربه‌ی کاربری جستجوی تمام متنی SQLite، افزونه‌ای به نام spell fix1 برای آن تهیه شده‌است که بر اساس توکن‌های ایندکس شده‌ی FTS، یک واژه‌نامه، تشکیل می‌شود و سپس بر اساس الگوریتم‌های غلط‌یابی املایی آن، از این توکن‌های از پیش موجود که واقعا در فیلدهای متنی بانک اطلاعاتی جاری وجود خارجی دارند، نزدیک‌ترین واژه‌های ممکن را پیشنهاد می‌کند تا بتوان بر اساس آن‌ها، جستجوی دقیق‌تری را ارائه کرد.


کامپایل افزونه‌ی spell fix1

افزونه‌ی spell fix، به همراه هیچکدام از توزیع‌های باینری SQLite ارائه نمی‌شود. ارائه‌ی آن فقط به صورت سورس کد است و باید خودتان آن‌را کامپایل کنید!


برای این منظور ابتدا به آدرس https://www.sqlite.org/src/dir?ci=99749d4fd4930ccf&name=ext/misc مراجعه کرده و فایل ext/misc/spellfix.c آن‌را دریافت کنید. اگر بر روی لینک spellfix.c کلیک کنید، در نوار ابزار بالای صفحه‌ی بعدی، لینک download آن هم وجود دارد.

سپس به صفحه‌ی دریافت اصلی SQLite یعنی https://www.sqlite.org/download.html مراجعه کرده و بسته‌ی amalgamation آن‌را دریافت کنید. این بسته به همراه کدهای اصلی SQLite است که باید در کنار افزونه‌های آن قرار گیرند تا بتوان این افزونه‌ها را کامپایل کرد. بنابراین پس از دریافت بسته‌ی amalgamation و گشودن آن، فایل spellfix.c را به داخل پوشه‌ی آن کپی کنید:


اکنون نوبت به کامپایل فایل spellfix.c و تبدیل آن به یک dll است تا بتوان آن‌را به صورت یک افزونه در برنامه بارگذاری کرد. برای این منظور از هر کامپایلر ++C ای می‌توانید استفاده کنید. برای نمونه به آدرس http://www.codeblocks.org/downloads/binaries مراجعه کرده و بسته‌ی codeblocks-20.03mingw-setup.exe را دریافت کنید (بسته‌ای که به همراه mingw است). پس از نصب آن، در مسیر C:\Program Files (x86)\CodeBlocks\MinGW\bin می‌توانید کامپایلر چندسکویی gcc را مشاهده کنید. توسط آن می‌توان با اجرای دستور زیر، سبب تولید فایل spellfix1.dll شد:
 "C:\Program Files (x86)\CodeBlocks\MinGW\bin\gcc.exe" -g -shared -fPIC -Wall D:\path\to\sqlite-amalgamation-3310100\spellfix.c -o spellfix1.dll


روش معرفی افزونه‌های SQLite به Microsoft.Data.Sqlite

EF Core، از بسته‌ی Microsoft.Data.Sqlite در پشت صحنه برای کار با SQLite استفاده می‌کند و در اینجا هم برای معرفی افزونه‌ی کامپایل شده، باید ابتدا آن‌را به اتصال برقرار شده، معرفی کرد. خود Sqlite در ویندوز، افزونه‌هایش را بر اساس معرفی مستقیم مسیر فایل dll آن‌ها بارگذاری نمی‌کند. بلکه path ویندوز را برای جستجوی آن‌ها بررسی کرده و در صورتیکه فایل dll ای را افزونه تشخیص داد، آن‌را بارگذاری می‌کند. بنابراین یا باید به صورت دستی مسیر فایل dll تولید شده را به متغیر محیطی path ویندوز اضافه کرد و یا می‌توان توسط قطعه کد زیر، آن‌را به صورت پویایی معرفی کرد:
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;

namespace EFCoreSQLiteFTS.DataLayer
{
    public static class LoadSqliteExtensions
    {
        public static void AddToSystemPath(string extensionsDirectory)
        {
            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                throw new NotSupportedException("Modifying the path at runtime only works on Windows. On Linux and Mac, set LD_LIBRARY_PATH or DYLD_LIBRARY_PATH before running the app.");
            }

            var path = new HashSet<string>(Environment.GetEnvironmentVariable("PATH").Split(Path.PathSeparator));
            if (path.Add(extensionsDirectory))
            {
                Environment.SetEnvironmentVariable("PATH", string.Join(Path.PathSeparator, path));
            }
        }
    }
}
در این متد extensionsDirectory، همان پوشه‌ای است که فایل dll کامپایل شده، در آن قرار دارد. مابقی آن، معرفی این مسیر به صورت پویا به PATH سیستم عامل است.

در ادامه پیش از معرفی services.AddDbContext، باید مسیر پوشه‌ی افزونه‌ها را ثبت کرد و سپس UseSqlite را به همراه اتصالی استفاده کرد که توسط متد LoadExtension آن، افزونه‌ی spellfix1 به آن معرفی شده‌است:
LoadSqliteExtensions.AddToSystemPath("path to .dll file");
services.AddDbContext<ApplicationDbContext>((serviceProvider, optionsBuilder) =>
    {
        var connection = new SqliteConnection(connectionString);
        connection.Open();

        connection.LoadExtension("spellfix1");
        // Passing in an already open connection will keep the connection open between requests.
        optionsBuilder.UseSqlite(connection);
    });
همانطور که عنوان شد، متد LoadExtension، مسیری را دریافت نمی‌کند. این متد فقط نام افزونه را دریافت می‌کند و مسیر آن‌را از PATH سیستم عامل می‌خواند.


ایجاد جداول ویژه‌ی spell fix در برنامه

در قسمت اول، با متد createFtsTables آشنا شدیم. اکنون این متد را برای ایجاد جداول کمکی مرتبط با افزونه‌ی spell fix به صورت زیر تکمیل می‌کنیم:
        private static void createFtsTables(ApplicationDbContext context)
        {
            // For SQLite FTS
            // Note: This can be added to the `protected override void Up(MigrationBuilder migrationBuilder)` method too.
            context.Database.ExecuteSqlRaw(@"CREATE VIRTUAL TABLE IF NOT EXISTS ""Chapters_FTS""
                                    USING fts5(""Text"", ""Title"", content=""Chapters"", content_rowid=""Id"");");

            // 'SQLite Error 1: 'no such module: spellfix1'.' --> must be loaded ...
            // EditCost for unicode support
            context.Database.ExecuteSqlRaw("CREATE VIRTUAL TABLE IF NOT EXISTS Chapters_FTS_Vocab USING fts5vocab('Chapters_FTS', 'row');");
            context.Database.ExecuteSqlRaw("CREATE TABLE IF NOT EXISTS Chapters_FTS_SpellFix_EditCost(iLang INT, cFrom TEXT, cTo TEXT, iCost INT);");
            context.Database.ExecuteSqlRaw("CREATE VIRTUAL TABLE IF NOT EXISTS Chapters_FTS_SpellFix USING spellfix1(edit_cost_table=Chapters_FTS_SpellFix_EditCost);");
        }
- اگر در حین اجرای این دستورات خطای «no such module: spellfix1» را دریافت کردید، یعنی متد LoadExtension را به درستی فراخوانی نکرده‌اید.
- همانطور که مشاهده می‌کنید، ابتدا بر اساس Chapters_FTS یا همان جدول مجازی FTS برنامه، یک جدول مجازی از نوع fts5vocab ایجاد می‌شود. کار آن استخراج توکن‌های FTS و آماده سازی آن‌ها برای استفاده در غلط یاب املایی هستند.
- سپس جدول ویژه‌ی EditCost را مشاهده می‌کنید. نام آن مهم نیست، اما ساختار آن باید دقیقا به همین صورت باشد. اگر این جدول اختیاری را تهیه کنیم، الگوریتم spellfix1 به utf8 سوئیچ خواهد کرد و برای پردازش متون یونیکد، بدون مشکل کار می‌کند. بدون آن، جستجوهای فارسی نتایج مطلوبی را به همراه نخواهند داشت.
- در آخر جدول مجازی مرتبط با spellfix1 که از جدول cost_table معرفی شده استفاده می‌کند، ایجاد شده‌است.

اجرای این دستورات، جداول زیر را ایجاد می‌کنند (که ساختار آن‌ها استاندارد است و باید مطابق فرمول‌های مستندات آن‌ها باشد):



به روز رسانی جدول واژه نامه‌ی غلط یابی برنامه

آخرین جدولی را که ایجاد کردیم، Chapters_FTS_SpellFix است که اطلاعات خودش را از Chapters_FTS_Vocab دریافت می‌کند:


  هر بار که بانک اطلاعاتی را به روز می‌کنیم، نیاز است اطلاعات این جدول را نیز توسط دستور زیر به روز کرد:
database.ExecuteSqlRaw(@"INSERT INTO Chapters_FTS_SpellFix(word, rank)
    SELECT term, cnt FROM Chapters_FTS_Vocab
    WHERE term not in (SELECT word from Chapters_FTS_SpellFix_vocab)");
البته خود SQLite اطلاعات این جدول را فقط یکبار بارگذاری می‌کند. برای اجبار آن به بارگذاری مجدد، می‌توان دستور reset زیر را صادر کرد:
database.ExecuteSqlRaw("INSERT INTO Chapters_FTS_SpellFix(command) VALUES(\"reset\");");


کوئری گرفتن از جدول مجازی Chapters_FTS_SpellFix

تا اینجا افزونه‌ی spellfix1 را کامپایل و به سیستم معرفی کردیم. سپس جداول واژه نامه‌ی آن‌را نیز تشکیل دادیم، اکنون نوبت به کوئری گرفتن از آن است. به همین جهت یک موجودیت بدون کلید دیگر را بر اساس ساختار خروجی کوئری‌های آن ایجاد کرده:
namespace EFCoreSQLiteFTS.Entities
{
    public class SpellCheck
    {
        public string Word { get; set; }
        public decimal Rank { get; set; }
        public decimal Distance { get; set; }
        public decimal Score { get; set; }
        public decimal Matchlen { get; set; }
    }
}
و آن‌را توسط متد HasNoKey به EF Core معرفی می‌کنیم:
namespace EFCoreSQLiteFTS.DataLayer
{
    public class ApplicationDbContext : DbContext
    {
        //...

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);

            builder.Entity<SpellCheck>().HasNoKey().ToView(null);
        }

        //...
    }
}
در اینجا SpellCheck تهیه شده با متد HasNoKey علامتگذاری می‌شود تا آن‌را بتوان بدون مشکل در کوئری‌های EF استفاده کرد. همچنین فراخوانی ToView(null) سبب می‌شود تا EF Core جدولی را در حین Migration از روی این موجودیت ایجاد نکند و آن‌را به همین حال رها کند.

در آخر، کوئری گرفتن از این جدول، ساختار زیر را دارد:
foreach (var item in context.Set<SpellCheck>().FromSqlRaw(
          @"SELECT word, rank, distance, score, matchlen FROM Chapters_FTS_SpellFix
            WHERE word MATCH {0} and top=6", "فارشی"))
{
    Console.WriteLine($"Word: {item.Word}");
    Console.WriteLine($"Distance: {item.Distance}");
}
با این خروجی:


top=6 در این کوئری خاص یعنی 6 رکورد را بازگشت بده.

یک نکته: اگر می‌خواهید کوئری فوق را توسط برنامه‌ی «DB Browser for SQLite» اجرا کنید، باید از منوی tools آن، گزینه‌ی load extension را انتخاب کرده و فایل dll افزونه را به برنامه معرفی کنید.


کدهای کامل این سری را از اینجا می‌توانید دریافت کنید.
مطالب
روش استفاده از Async&Await در Portable Class Library دات نت 4
چند وقت پیش زمانی که قصد داشتم از یک Portable Class Library  که تحت دات نت 4 بود توی پروژه ام استفاده کنم متوجه شدم که این نوع Class Library از فضای نام System.Threading.Task پشتیبانی نمی‌کنه. قصد داشتم که از این فضای نام برای بحث TPL توی پروژه ام استفاده کنم و چند تا متد Async بنویسم. زمانی که سعی کردم  با استفاده از Nuget؛ AsyncAwaitCTP رو نصب کنم با خطای زیر روبرو شدم.

دستور مورد نظر در Nuget :
PM> install-package AsyncAwaitCtp
و اما خطا
Install failed. Rolling back...
install-package : Could not install package 'AsyncAwaitCTP 1.0'. You are trying to install this package into a project that targets '.NETPortable,Version=v4.0,Profile=Profile3', 
but the package does not contain any assembly references that are compatible with that framework. For more information, contact the package author.
At line:1 char:1
دستور بالا برای نصب AsyncAwaitCTP در Class Library تحت دات نت 4.5 استفاده می‌شه.
خلاصه بعد از یکم جستجو متوجه شدم که باید از دستور زیر برای نصب TPL توی Portable Class Library تحت دات نت 4 استفاده کنم.
PM > install-Package Microsoft-BCL-Async -Pre
که بازم با خطای زیر مواجه شدم.
Install failed. Rolling back...
install-package : Could not install package 'Microsoft.Bcl 1.0.16-rc'. You are trying to install this package into a project that targets 
'.NETPortable,Version=v4.0,Profile=Profile3', but the package does not contain any assembly references that are compatible with that framework. For more information, contact the 
package author.
At line:1 char:1
+ install-package Microsoft.BCL.Async -Pre
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Install-Package], InvalidOperationException
    + FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PowerShell.Commands.InstallPackageCommand
از اونجا که از دستور بالا مطمئن بودم و می‌دونستم که باید درست کار کنه فهمیدم اشکال کار از یه جای دیگه است. خلاصه بعد از یه جستجوی 25 دقیقه ای متوجه شدم که
Nuget نصب شده روی سیستم من Update نیست. برای همین به روش زیر عمل کردم .
از منوی Tools گزینه Extension And Updates رو انتخاب کردم. بعد از صفحه مورد نظر گزینه Updates روز از منوی سمت چپ انتخاب کردم و درنهایت گزینه Nuget Package Manager. مثل شکل زیر:


بعد از اتمام عملیات Update دوباره دستورات مورد نظر رو وارد کردم که به خوبی عملیات نصب CTP به اتمام رسید.





اشتراک‌ها
افزونه Funnel
Funnel  به معنی قیف
 برای مدیریت لود کردن پروژه‌ها در یک Solution بزرگ
با این افزونه میتوانید تعداد کمی پروژه که الان می‌خواهید بر روی آن کار کنید را فقط لود کنید ! مزیت در پروژه هایی با 20 تا پروژه کاملا مشهود می‌باشد
و موجب بالا رفتن سرعت لود و  کار  VSبا ویژوال استودیو خواهد شد  مخصوصا اگر معتاد RSharper  هم باشید
افزونه Funnel
مطالب
سایت‌های مهمی که از ASP.NET MVC استفاده می‌کنند

عموما استفاده وسیع از نگارش‌های مختلف ASP.NET مربوط به اینترانت‌های شرکت‌های خصوصی و دولتی است. برنامه‌هایی که هیچ وقت رنگ آسمان را هم نخواهند دید و کسی از آمار یا وجود آن‌ها مطلع نخواهد شد. اما در این بین هستند سایت‌های عمومی که از این فناوری‌ها استفاده می‌کنند. مهم‌ترین و پرترافیک‌ترین سایت‌هایی که در حال حاضر از ASP.NET MVC کمک می‌گیرند شامل موارد زیر هستند:
جالب اینجا است که اخیرا سایت msnbc استفاده وسیعی از RavenDB را هم شروع کرده است.

سایر منابع:

سؤال: چگونه تشخیص دهیم یک سایت از ASP.NET MVC استفاده می‌کند؟

ابتدا افزونه Server Spy را نصب کنید. این افزونه می‌تواند وب سروری را که یک سایت هم اکنون مورد استفاده قرار داده، تشخیص دهد. اگر IIS بود، یعنی این سایت از یکی از مشتقات ASP یا ASP.NET استفاده می‌کند. اگر پسوند صفحات به asp ختم شده بود، ASP‌ کلاسیک دهه نود است. در غیراینصورت یا Web forms است یا MVC. در این حالت به سورس صفحه مراجعه کنید. اگر از ViewState خبری نبود یعنی ASP.NET MVC است.
البته این روش در 90 درصد موارد جواب می‌دهد. می‌شود هدر ارسالی وب سرور را کلا تغییر داد. یعنی ضرورتی ندارد که یک سایت استفاده کننده از IIS حتما اعلام کند که از این وب سرور خاص استفاده می‌کند. یا در ASP.NET Web forms می‌شود ViewState را با ترفندهایی حذف کرد. اما ... این مسایل همه گیر نیست و روش‌ ذکر شده شناسایی، در اکثر موارد جواب می‌دهد.

مطالب
معرفی افزونه‌های مفید VSCode جهت کار با Angular
VSCode یکی از بهترین ادیتورهایی است که از آن می‌توان برای توسعه‌ی برنامه‌های Angular استفاده کرد و در این بین افزونه‌های ویژه‌ای جهت کار با Angular برای آن تدارک دیده شده‌اند که در ادامه تعدادی از مهم‌ترین‌های آن‌ها را بررسی می‌کنیم.


Angular Essentials

این افزونه گروهی از مهم‌ترین افزونه‌های موجود را به صورت بسته بندی شده ارائه می‌دهد و با نصب آن، تعدادی از افزونه‌هایی را که در ادامه نامبرده خواهند شد، به صورت یکجا و خودکار دریافت خواهید کرد.


Angular Language Service

نگارش‌های اخیر Angular به همراه یک سرویس زبان نیز می‌باشند که به ادیتورهای مختلف این امکان را می‌دهد تا توسط این ویژگی بتوانند قابلیت‌های ویرایشی بهتری را جهت برنامه‌های Angular ارائه کنند. برای مثال ویرایش مطلوب قالب‌های کامپوننت‌های Angular و استفاده‌ی از Syntax خاص آن، موردی است که توسط هیچکدام از HTML ادیتورهای موجود پشتیبانی نمی‌شود. اکنون به کمک سرویس زبان Angular و افزونه‌ی ویژه‌ی آن برای VSCode که توسط تیم اصلی Angular توسعه یافته‌است، امکان ویرایش غنی قالب‌های HTML ایی آن فراهم شده‌است. این افزونه یک چنین قابلیت‌هایی را فراهم می‌کند:
الف) AOT Diagnostic messages
اگر قالب HTML ایی مورد استفاده (چه به صورت inline و چه در یک فایل html مجزا) به خاصیتی تعریف نشده اشاره کند، بلافاصله خطای مرتبطی ظاهر خواهد شد:


ب) Completions lists یا همان Intellisense
ج) امکان Go to definition با کلیک راست بر روی خواص و متدهای ذکر شده‌ی در قالب.
د) Quick info که با نزدیک کردن اشاره‌گر ماوس به خاصیت یا متدی در صفحه، اطلاعات بیشتری را در مورد آن نمایش می‌دهد.


angular2-inline

علاوه بر افزونه‌ی سرویس زبان‌های Angular، این افزونه نیز قابلیت درک قالب‌های inline کامپوننت‌ها را داشته و به همراه syntax highlighting و همچنین Intellisense است.


Auto Import

حین کار با TypeScript، هر ماژولی که در صفحه ارجاعی داشته باشد، باید در ابتدای فایل جاری import شود. افزونه‌ی Auto Import با بررسی ماژول‌های موجود و فراهم آوردن Intellisense ایی بر اساس آن‌ها، این‌کار را ساده‌تر می‌کند:


بنابراین این افزونه صرفا مختص به Angular نیست و برای کارهای متداول TypeScript نیز بسیار مفید است.


TSLint

این افزونه ابزار TSLint را با VSCode یکپارچه می‌کند. بنابراین نیاز است پیش از نصب این افزونه، وابستگی‌های ذیل را نیز به صورت سراسری نصب کرد:
 > npm install -g tslint typescript
کار TSLint انجام static code analysis است؛ چیزی شبیه به افزونه‌هایی مانند ری‌شارپر در ویژوال استودیو که راهنماهایی را در مورد بهتر کردن کیفیت کدهای نوشته شده ارائه می‌دهد.
تعدادی از امکانات آن‌را پس از نصب، با فشردن دکمه‌ی F1 می‌توان مشاهده کرد:


برای مثال تولید فایل tslint.json، امکان سفارشی سازی موارد بررسی شونده‌ی توسط این افزونه را فراهم می‌کند و اگر برنامه‌ی خود را توسط Angular CLI ایجاد کرده‌اید، این فایل هم اکنون در ریشه‌ی پروژه قرار دارد.
در مورد TSLint در مطلب «Angular CLI - قسمت دوم - ایجاد یک برنامه‌ی جدید» بیشتر توضیح داده شده‌است و اینبار به کمک این افزونه، خطاهای یاد شده را دقیقا درون محیط ادیتور و به صورت خودکار و یکپارچه‌ای مشاهده خواهید کرد.



Angular v4 TypeScript Snippets

سیستم کار VSCode مبتنی بر ایجاد فایل‌های خالی است و مفهوم قالب‌های از پیش آماده‌ی فایل‌ها در آن وجود ندارد. اما با کمک Code Snippets می‌توان این خلاء را پر کرد. افزونه‌ی Angular v4 TypeScript Snippets دقیقا به همین منظور طراحی شده‌است و زمانیکه حروف -a یا -rx را در صفحه تایپ می‌کنید، منویی ظاهر خواهد شد که توسط آن می‌توان قالب ابتدایی شروع به کار با انواع و اقسام جزئیات پروژه‌های Angular را تهیه کرد.



Path Intellisense

این افزونه مسیر فایل‌های موجود را به صورت یک Intellisense ارائه می‌کند و به این صورت به سادگی می‌توان مسیرهای اسکریپت‌ها و یا شیوه‌نامه‌ها را در ادیتور انتخاب و وارد کرد.

دوره‌ها
استفاده از افزونه‌ها و امکانات jQuery در ASP.NET MVC
هدف از این دوره، ارائه مثال‌هایی کاربردی از نحوه استفاده از امکانات مهیا در جی‌کوئری و یا تهیه شده توسط اشخاص ثالث، به صورت افزونه‌های موجود، در ASP.NET MVC است. برای مثال چگونه یک فرم ASP.NET MVC را توسط jQuery Ajax به همراه اعتبار سنجی کامل سمت کاربر به سرور ارسال کنیم، یا نحوه استفاده از افزونه تغییر ترتیب آیتم‌های تعریف شده در ASP.NET MVC چگونه است و یا Infinite Scroll ایی که در سایت جاری مورد استفاده قرار گرفته، چگونه پیاده سازی گردیده است. لیست کامل سرفصل‌های این دوره در ادامه قابل مشاهده است.