آیا تنوع در محل کار مؤثر است؟
خیر - به کلی مخالفم؛ باعث انحراف از موضوع کاری می شود
بله - تا حدودی موافقم
خیر - تاثیر چندانی ندارد
برای آشنایی بیشتر با این helper توصیه میکنم ابتدا این مقاله را مطالعه نمایید.
به صورت پیش فرش WbebGrid صفحه بندی را به صورت خیلی ساده فقط با نمایش اعداد و جهت نماهای جلو و عقب، نشان میدهد که برای پروژههای رسمی تا حدودی جالب نیست.
در این مطلب قصد داریم از کتابخانهی bootstrap جهت صفحه بندی استفاده کنیم و در نهایت به صفحه بندی زیر برسیم:
برای اینکار، ابتدا قبل از هر چیزی به یک متد الحاقی برای انجام صفحه بندی سفارشی سازی شده، نیاز داریم که کدهای این متد به صورت زیر خواهد بود:
public static class WebGridExtensions { public static HelperResult PagerList( this WebGrid webGrid, WebGridPagerModes mode = WebGridPagerModes.NextPrevious | WebGridPagerModes.Numeric, string firstText = null, string previousText = null, string nextText = null, string lastText = null, int numericLinksCount = 5) { return PagerList(webGrid, mode, firstText, previousText, nextText, lastText, numericLinksCount, explicitlyCalled: true); } private static HelperResult PagerList( WebGrid webGrid, WebGridPagerModes mode, string firstText, string previousText, string nextText, string lastText, int numericLinksCount, bool explicitlyCalled) { int currentPage = webGrid.PageIndex; int totalPages = webGrid.PageCount; int lastPage = totalPages - 1; var ul = new TagBuilder("ul"); var li = new List<TagBuilder>(); if (ModeEnabled(mode, WebGridPagerModes.FirstLast)) { if (String.IsNullOrEmpty(firstText)) { firstText = "اولین"; } var part = new TagBuilder("li") { InnerHtml = GridLink(webGrid, webGrid.GetPageUrl(0), firstText) }; if (currentPage == 0) { part.MergeAttribute("class", "disabled"); } li.Add(part); } if (ModeEnabled(mode, WebGridPagerModes.NextPrevious)) { if (String.IsNullOrEmpty(previousText)) { previousText = "قبلی"; } int page = currentPage == 0 ? 0: currentPage - 1; var part = new TagBuilder("li") { InnerHtml = GridLink(webGrid, webGrid.GetPageUrl(page), previousText) }; if (currentPage == 0) { part.MergeAttribute("class", "disabled"); } li.Add(part); } if (ModeEnabled(mode, WebGridPagerModes.Numeric) && (totalPages > 1)) { int last = currentPage + (numericLinksCount / 2); int first = last - numericLinksCount + 1; if (last > lastPage) { first -= last - lastPage; last = lastPage; } if (first < 0) { last = Math.Min(last + (0 - first), lastPage); first = 0; } for (int i = first; i <= last; i++) { var pageText = (i + 1).ToString(CultureInfo.InvariantCulture); var part = new TagBuilder("li") { InnerHtml = GridLink(webGrid, webGrid.GetPageUrl(i), pageText) }; if (i == currentPage) { part.MergeAttribute("class", "active"); } li.Add(part); } } if (ModeEnabled(mode, WebGridPagerModes.NextPrevious)) { if (String.IsNullOrEmpty(nextText)) { nextText = "بعدی"; } int page = currentPage == lastPage ? lastPage: currentPage + 1; var part = new TagBuilder("li") { InnerHtml = GridLink(webGrid, webGrid.GetPageUrl(page), nextText) }; if (currentPage == lastPage) { part.MergeAttribute("class", "disabled"); } li.Add(part); } if (ModeEnabled(mode, WebGridPagerModes.FirstLast)) { if (String.IsNullOrEmpty(lastText)) { lastText = "آخرین"; } var part = new TagBuilder("li") { InnerHtml = GridLink(webGrid, webGrid.GetPageUrl(lastPage), lastText) }; if (currentPage == lastPage) { part.MergeAttribute("class", "disabled"); } li.Add(part); } ul.InnerHtml = string.Join("", li); var html = ""; if (explicitlyCalled && webGrid.IsAjaxEnabled) { var span = new TagBuilder("span"); span.MergeAttribute("data-swhgajax", "true"); span.MergeAttribute("data-swhgcontainer", webGrid.AjaxUpdateContainerId); span.MergeAttribute("data-swhgcallback", webGrid.AjaxUpdateCallback); span.InnerHtml = ul.ToString(); html = span.ToString(); } else { html = ul.ToString(); } return new HelperResult(writer => { writer.Write(html); }); } private static String GridLink(WebGrid webGrid, string url, string text) { TagBuilder builder = new TagBuilder("a"); builder.SetInnerText(text); builder.MergeAttribute("href", url); if (webGrid.IsAjaxEnabled) { builder.MergeAttribute("data-swhglnk", "true"); } return builder.ToString(TagRenderMode.Normal); } private static bool ModeEnabled(WebGridPagerModes mode, WebGridPagerModes modeCheck) { return (mode & modeCheck) == modeCheck; } }
پس از آن در View یی که اطلاعات را نمایش میدهید، فقط لازم است کد زیر را اضافه نمایید:
<div> @grid.PagerList(mode: WebGridPagerModes.All) </div>
در ادامه برای تکمیل بحث مثالی را از نحوهی نمایش اطلاعات و صفحه بندی سفارشی نشان خواهیم داد:
PartialView لازم برای نمایش اطلاعات
تنظیمات لازم گرید :
@{ WebGrid grid = new WebGrid(Model, rowsPerPage: 10, ajaxUpdateContainerId: "grid"); var rowIndex = ((grid.PageIndex + 1) * grid.RowsPerPage) - (grid.RowsPerPage - 1); }
@grid.Table( tableStyle: "table table-striped table-hover", headerStyle: "webgrid-header", alternatingRowStyle: "webgrid-alternating-row", selectedRowStyle: "webgrid-selected-row", rowStyle: "webgrid-row-style", columns: grid.Columns( grid.Column(columnName: "Name", header: "نام استان", style: "myfont"), grid.Column(columnName: "NameEn", header: "نام استان ( انگلیسی )", style: "myfont"), grid.Column(header: "", format: item => @Html.ActionLink("مدیریت شهرها", actionName: MVC.Admin.City.ActionNames.Index, controllerName: MVC.Admin.City.Name, routeValues: new {Code=item.Code },htmlAttributes:null)), grid.Column(header: "", style: "text-align-center-col smallcell", format: item => @Html.ActionLink(linkText: "ویرایش", actionName: "Edit", controllerName: "Province", routeValues: new { area = "Admin", code = item.Code }, htmlAttributes: new { @class = "btn-sm btn-info vertical-center" })) ) ) <div> @grid.PagerList(mode: WebGridPagerModes.All) </div>
خروجی حاصل به صورت زیر خواهد بود :
اگر طبق توضیحات بالا عمل کرده باشید، در نهایت صفحه بندی شما به صورت عمودی نمایش داده میشود؛ یعنی هر کدام از شماره صفحات در یک سطر. دلیل آن هم این است که تگ ul، کلاس .pagination را ندارد. در کدهای بوت استراپ تعریف شده است که تمام li هایی که به صورت مستقیم داخل کلاس .pagination هستند خصوصیات مورد نظر را بگیرند.
برای این کار دو راه حل وجود دارد :
راه حل اول: تغییر کدهای css
کدهای نوشته شده برای صفحه بندی در بوت استراپ را از حالت زیر:
.pagination > li
.pagination li
یادآوری : علامت < در CSS یعنی به صورت مستقیم و در شاخهی اول.
راه حل دوم - افزودن کلاس .pagination به تگ ul:
ابتدا کلاس .pagination را از تگ div حذف نمایید:
<div > @grid.PagerList(mode: WebGridPagerModes.All) </div>
و در کدهایی کلاس WebGridExtensions، در قسمتی که تگ ul اصافه میشود، کلاس مورد نظر را به آن اضافه میکنیم:
var ul = new TagBuilder("ul"); ul.AddCssClass("pagination");
دانلود کدهای این مثال
نکتهای در مورد Webgrid
اگر نیاز داشتید به یکباره تمام اطلاعات را در گرید لود نکنید و به صورت n تاn تا رکوردها را نمایش دهید، در این حالت پس از پاس دادن لیستی از اطلاعات به View مورد نظر لازم است تعداد کل رکوردها را در یک متغییر به سمت View بفرستید. این کار به این دلیل میباشد که بتوان صفحه بندی را تولید کرد. برای این کار در بخش تنظیمات Webgrid مقدار source را برابر null قرار دهید و از قطعه کد زیر جهت بایند کردن گرید، بعد از کدهای تنظیمات WebGrid استفاده نمایید:
grid.Bind(Model, rowCount: (int)ViewBag.PageCount);
Roslyn #7
Workspace، در حقیقت نمایش اجزای یک Solution در ویژوال استودیو است و یک Solution متشکل است از تعدادی پروژه به همراه وابستگیهای بین آنها. هدف از وجود Workspace API در Roslyn، دسترسی به اطلاعات لازم جهت انجام امور Refactoring در سطح یک Solution است. برای مثال اگر قرار است نام خاصیتی تغییر کند و این خاصیت در چندین پروژهی دیگر در حال استفاده است، این نام باید در سراسر Solution جاری یافت شده و تغییر یابد. همچنین برفراز Workspace API تعدادی سرویس زبان مانند فرمت کنندههای کدها، تغییرنام دهندههای سیمبلها و توصیه کنندهها نیز تهیه شدهاند.
همچنین این سرویسها و API تهیه شده، منحصر به ویژوال استودیو نیستند و VS 2015 تنها از آنها استفاده میکند. برای مثال نگارشهای جدیدتر mono-develop لینوکسی نیز شروع به استفادهی از Roslyn کردهاند.
نمایش اجزای یک Solution
در ادامه مثالی را مشاهده میکنید که توسط آن نام Solution و سپس تمام پروژههای موجود در آنها به همراه نام فایلهای مرتبط و همچنین ارجاعات آنها در صفحه نمایش داده میشوند:
var ws = MSBuildWorkspace.Create(); var sln = ws.OpenSolutionAsync(@"..\..\..\Roslyn.sln").Result; // Print the root of the solution. Console.WriteLine(Path.GetFileName(sln.FilePath)); // Get dependency graph to perform a sort. var g = sln.GetProjectDependencyGraph(); var ps = g.GetTopologicallySortedProjects(); // Print all projects, their documents, and references. foreach (var p in ps) { var proj = sln.GetProject(p); Console.WriteLine("> " + proj.Name); Console.WriteLine(" > References"); foreach (var r in proj.ProjectReferences) { Console.WriteLine(" - " + sln.GetProject(r.ProjectId).Name); } foreach (var d in proj.Documents) { Console.WriteLine(" - " + d.Name); } }
Roslyn.sln > Roslyn01 > References - Program.cs - AssemblyInfo.cs - .NETFramework,Version=v4.6.AssemblyAttributes.cs
ایجاد یک Syntax highlighter با استفاده از Classification service
هدف از Classification service، رندر کردن فایلها در ادیتور جاری است. برای این منظور نیاز است بتوان واژههای کلیدی، کامنتها، نامهای نوعها و امثال آنها را به صورت کلاسه شده در اختیار داشت و سپس برای مثال هرکدام را با رنگی مجزا نمایش داد و رندر کرد.
در ادامه مثالی از آنرا ملاحظه میکنید:
var ws = MSBuildWorkspace.Create(); var sln = ws.OpenSolutionAsync(@"..\..\..\Roslyn.sln").Result; // Get the Tests\Bar.cs document. var proj = sln.Projects.Single(p => p.Name == "Roslyn04.Tests"); var test = proj.Documents.Single(d => d.Name == "Bar.cs"); var tree = test.GetSyntaxTreeAsync().Result; var root = tree.GetRootAsync().Result; // Get all the spans in the document that are classified as language elements. var spans = Classifier.GetClassifiedSpansAsync(test, root.FullSpan).Result.ToDictionary(c => c.TextSpan.Start, c => c); // Print the source text with appropriate colorization. var txt = tree.GetText().ToString(); var i = 0; foreach (var c in txt) { var span = default(ClassifiedSpan); if (spans.TryGetValue(i, out span)) { var color = ConsoleColor.Gray; switch (span.ClassificationType) { case ClassificationTypeNames.Keyword: color = ConsoleColor.Cyan; break; case ClassificationTypeNames.StringLiteral: case ClassificationTypeNames.VerbatimStringLiteral: color = ConsoleColor.Red; break; case ClassificationTypeNames.Comment: color = ConsoleColor.Green; break; case ClassificationTypeNames.ClassName: case ClassificationTypeNames.InterfaceName: case ClassificationTypeNames.StructName: case ClassificationTypeNames.EnumName: case ClassificationTypeNames.TypeParameterName: case ClassificationTypeNames.DelegateName: color = ConsoleColor.Yellow; break; case ClassificationTypeNames.Identifier: color = ConsoleColor.DarkGray; break; } Console.ForegroundColor = color; } Console.Write(c); i++; }
توضیحات:
در اینجا نیز کار با ایجاد یک Workspace و سپس گشودن Solution ایی مشخص در آن آغاز میشود. سپس در آن به دنبال پروژهای به نام Roslyn04.Tests میگردیم. این پروژه حاوی تعدادی کلاس، جهت بررسی و آزمایش هستند. برای مثال در اینجا فایل Bar.cs آن قرار است آنالیز شود. پس از یافتن آن، ابتدا syntax tree آن دریافت میگردد و سپس به سرویس Classifier.GetClassifiedSpansAsync ارسال خواهد شد. خروجی آن شامل لیستی از Classified Spans است؛ مانند کلمات کلیدی، رشتهها، کامنتها و غیره. در ادامه این لیست تبدیل به یک دیکشنری میشود که کلید آن محل آغاز این span و مقدار آن، مقدار span است. سپس متن syntax tree دریافت شده و حرف به حرف آن در طی یک حلقه بررسی میشود. در این حلقه، مقدار i به محل حروف جاری مورد آنالیز اشاره میکند. اگر این محل در دیکشنری Classified Spans وجود داشت، یعنی یک span جدید شروع شدهاست و بر این اساس، نوع آن span را میتوان استخراج کرد و سپس بر اساس این نوع، رنگ متفاوتی را در صفحه نمایش داد.
سرویس فرمت کردن کدها
این سرویس کار فرمت خودکار کدهای بهم ریخته را انجام میدهد؛ مانند تنظیم فاصلههای خالی و یا ایجاد indentation و امثال آن. در حقیقت Ctlr K+D در ویژوال استودیو، دقیقا از همین سرویس زبان استفاده میکند.
کار کردن با این سرویس از طریق برنامه نویسی به نحو ذیل است:
var ws = MSBuildWorkspace.Create(); var sln = ws.OpenSolutionAsync(@"..\..\..\Roslyn.sln").Result; // Get the Tests\Qux.cs document. var proj = sln.Projects.Single(p => p.Name == "Roslyn04.Tests"); var qux = proj.Documents.Single(d => d.Name == "Qux.cs"); Console.WriteLine("Before:"); Console.WriteLine(); Console.WriteLine(qux.GetSyntaxTreeAsync().Result.GetText()); Console.WriteLine(); Console.WriteLine(); // Apply formatting and print the result. var res = Formatter.FormatAsync(qux).Result; Console.WriteLine("After:"); Console.WriteLine(); Console.WriteLine(res.GetSyntaxTreeAsync().Result.GetText()); Console.WriteLine();
Before: using System; namespace Roslyn04.Tests { class Qux { public void Baz() { Console.WriteLine(42); return; } } } After: using System; namespace Roslyn04.Tests { class Qux { public void Baz() { Console.WriteLine(42); return; } } }
سرویس یافتن سیمبلها
یکی دیگر از قابلیتهایی که در ویژوال استودیو وجود دارد، امکان یافتن سیمبلها است. برای مثال این نوع یا کلاس خاص، در کجاها استفاده شدهاست و به آن ارجاعاتی وجود دارد. مواردی مانند Find all references، Go to definition و نمایش Call hierarchy از این سرویس استفاده میکنند.
var ws = MSBuildWorkspace.Create(); var sln = ws.OpenSolutionAsync(@"..\..\..\Roslyn.sln").Result; // Get the Tests project. var proj = sln.Projects.Single(p => p.Name == "Roslyn04.Tests"); // Locate the symbol for the Bar.Foo method and the Bar.Qux property. var comp = proj.GetCompilationAsync().Result; var barType = comp.GetTypeByMetadataName("Roslyn04.Tests.Bar"); var fooMethod = barType.GetMembers().Single(m => m.Name == "Foo"); var quxProp = barType.GetMembers().Single(m => m.Name == "Qux"); // Find callers across the solution. Console.WriteLine("Find callers of Foo"); Console.WriteLine(); var callers = SymbolFinder.FindCallersAsync(fooMethod, sln).Result; foreach (var caller in callers) { Console.WriteLine(caller.CallingSymbol); foreach (var location in caller.Locations) { Console.WriteLine(" " + location); } } Console.WriteLine(); Console.WriteLine(); // Find all references across the solution. Console.WriteLine("Find all references to Qux"); Console.WriteLine(); var references = SymbolFinder.FindReferencesAsync(quxProp, sln).Result; foreach (var reference in references) { Console.WriteLine(reference.Definition); foreach (var location in reference.Locations) { Console.WriteLine(" " + location.Location); } }
اکنون با استفاده از سرویس SymbolFinder.FindCallersAsync تمام فراخوانهای متد Foo را در سراسر Solution جاری مییابیم.
سپس با استفاده از سرویس SymbolFinder.FindReferencesAsync تمام ارجاعات به خاصیت Qux را در Solution جاری نمایش میدهیم.
سرویس توصیه کننده
Intellisense در ویژوال استودیو از سرویس توصیه کنندهی Roslyn استفاده میکند.
var ws = MSBuildWorkspace.Create(); var sln = ws.OpenSolutionAsync(@"..\..\..\Roslyn.sln").Result; // Get the Tests\Foo.cs document. var proj = sln.Projects.Single(p => p.Name == "Roslyn04.Tests"); var foo = proj.Documents.Single(d => d.Name == "Foo.cs"); // Find the 'dot' token in the first Console.WriteLine member access expression. var tree = foo.GetSyntaxTreeAsync().Result; var model = proj.GetCompilationAsync().Result.GetSemanticModel(tree); var consoleDot = tree.GetRoot().DescendantNodes().OfType<MemberAccessExpressionSyntax>().First().OperatorToken; // Get recommendations at the indicated cursor position. // // Console.WriteLine // ^ var res = Recommender.GetRecommendedSymbolsAtPosition( model, consoleDot.GetLocation().SourceSpan.Start + 1, ws).ToList(); foreach (var rec in res) { Console.WriteLine(rec); }
تعدادی از خروجیهای مثال فوق به صورت زیر هستند:
System.Console.Beep() System.Console.Beep(int, int) System.Console.Clear()
سرویس تغییر نام دادن
هدف از سرویس Renamer.RenameSymbolAsync، تغییر نام یک identifier در کل Solution است. نمونهای از نحوهی کاربرد آنرا در مثال ذیل مشاهده میکنید:
var ws = MSBuildWorkspace.Create(); var sln = ws.OpenSolutionAsync(@"..\..\..\Roslyn.sln").Result; // Get Tests\Bar.cs before making changes. var oldProj = sln.Projects.Single(p => p.Name == "Roslyn04.Tests"); var oldDoc = oldProj.Documents.Single(d => d.Name == "Bar.cs"); Console.WriteLine("Before:"); Console.WriteLine(); var oldTxt = oldDoc.GetTextAsync().Result; Console.WriteLine(oldTxt); Console.WriteLine(); Console.WriteLine(); // Get the symbol for the Bar.Foo method. var comp = oldProj.GetCompilationAsync().Result; var barType = comp.GetTypeByMetadataName("Roslyn04.Tests.Bar"); var fooMethod = barType.GetMembers().Single(m => m.Name == "Foo"); // Perform the rename. var newSln = Renamer.RenameSymbolAsync(sln, fooMethod, "Foo2", ws.Options).Result; // Get Tests\Bar.cs after making changes. var newProj = newSln.Projects.Single(p => p.Name == "Roslyn04.Tests"); var newDoc = newProj.Documents.Single(d => d.Name == "Bar.cs"); Console.WriteLine("After:"); Console.WriteLine(); var newTxt = newDoc.GetTextAsync().Result; Console.WriteLine(newTxt);
سرویس ساده کننده
هدف از سرویس ساده کننده، سادهکردن و کاهش کدهای ارائه شده، از دید Semantics است. برای مثال اگر فضای نامی در قسمت using ذکر شدهاست، دیگر نیازی نیست تا این فضای نام به ابتدای فراخوانی یک متد آن اضافه شود و میتوان این قطعه از کد را سادهتر کرد و کاهش داد.
var ws = MSBuildWorkspace.Create(); var sln = ws.OpenSolutionAsync(@"..\..\..\Roslyn.sln").Result; // Get the Tests\Baz.cs document. var proj = sln.Projects.Single(p => p.Name == "Roslyn04.Tests"); var baz = proj.Documents.Single(d => d.Name == "Baz.cs"); Console.WriteLine("Before:"); Console.WriteLine(); Console.WriteLine(baz.GetSyntaxTreeAsync().Result.GetText()); Console.WriteLine(); Console.WriteLine(); var oldRoot = baz.GetSyntaxRootAsync().Result; var memberAccesses = oldRoot.DescendantNodes().OfType<CastExpressionSyntax>(); var newRoot = oldRoot.ReplaceNodes(memberAccesses, (_, m) => m.WithAdditionalAnnotations(Simplifier.Annotation)); var newDoc = baz.WithSyntaxRoot(newRoot); // Invoke the simplifier and print the result. var res = Simplifier.ReduceAsync(newDoc).Result; Console.WriteLine("After:"); Console.WriteLine(); Console.WriteLine(res.GetSyntaxTreeAsync().Result.GetText()); Console.WriteLine();
کدهای کامل این سری را از اینجا میتوانید دریافت کنید:
Roslyn-Samples.zip
مدرسه هوش مصنوعی
Dive in and learn how to start building intelligence into your solutions with the Microsoft AI platform, including pre-trained AI services like Cognitive Services and Bot Framework, as well as deep learning tools like Azure Machine Learning, Visual Studio Code Tools for AI, and Cognitive Toolkit. Our platform enables any developer to code in any language and infuse AI into your apps. Whether your solutions are existing or new, this is the intelligence platform to build on.
5 ویژگی برتر احتمالی C# 10
context.Chapters.Add(new Chapter { Title = "آزمایش متن فارسی", Text = "برای نمونه تهیه شدهاست", User = user1.Entity });
کامپایل افزونهی 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)); } } } }
در ادامه پیش از معرفی 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); });
ایجاد جداول ویژهی 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);"); }
- همانطور که مشاهده میکنید، ابتدا بر اساس 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)");
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; } } }
namespace EFCoreSQLiteFTS.DataLayer { public class ApplicationDbContext : DbContext { //... protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.Entity<SpellCheck>().HasNoKey().ToView(null); } //... } }
در آخر، کوئری گرفتن از این جدول، ساختار زیر را دارد:
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 افزونه را به برنامه معرفی کنید.
کدهای کامل این سری را از اینجا میتوانید دریافت کنید.
تنظیمات JSON در ASP.NET Web API
public string RemoveControlCharacters1(string input) { string output = Regex.Replace(input, @"[\u0000-\u001F]", string.Empty); return output; } public string RemoveControlCharacters2(string input) { string output = new string(input.Where(c => !char.IsControl(c)).ToArray()); return output; } public string RemoveControlCharacters3(string inString) { if (inString == null) return null; StringBuilder newString = new StringBuilder(); char ch; for (int i = 0; i < inString.Length; i++) { ch = inString[i]; if (!char.IsControl(ch)) { newString.Append(ch); } } return newString.ToString(); }
اگر با زبان C و مشتقات آن آشنایی داشته باشید، حتما با عملگرهای ترکیبی آنها که جهت خلاصه نویسی بکار میروند، نیز کار کردهاید. برای مثال:
int i =5;
i += 15; // i = i + 15;
اس کیوال سرور 2008 نیز از اینگونه عملگرها پشتیبانی به عمل میآورد. برای نمونه:
DECLARE @x1 int = 27;
SET @x1 += 2 ;
SELECT @x1 AS Added_2;
الف) امکان تعریف و مقدار دهی همزمان یک متغیر (مقدار دهی همزمان با تعریف، تا قبل از اس کیوال سرور 2008 پشتیبانی نمیشد)
ب) امکان استفاده از عملگرهای C مانند در عبارات T-SQL
لیست این عملگرهای جدید به شرح زیر است:
+= (Add EQUALS) (Transact-SQL)
-= (Subtract EQUALS) (Transact-SQL)
*= (Multiply EQUALS) (Transact-SQL)
/= (Divide EQUALS) (Transact-SQL)
%= (Modulo EQUALS) (Transact-SQL)
&= (Bitwise AND EQUALS) (Transact-SQL)
^= (Bitwise Exclusive OR EQUALS) (Transact-SQL)
|= (Bitwise OR EQUALS) (Transact-SQL)
string greeting = "Hello, C#";
که در این حالت مجموعهای از کاراکترها را ایجاد خواهد کرد:
- خانههای آن یک ضرب پر نمیشوند بلکه به ترتیب، خانه به خانه پر میشوند.
- قبل از انتساب متن باید باید از طول متن مطمئن شویم تا بتوانیم تعداد خانهها را بر اساس آن ایجاد کنیم.
- همه عملیات آرایهها از پر کردن ابتدای کار گرفته تا هر عملی، نیاز است به صورت دستی صورت بگیرد و تعداد خطوط کد برای هر کاری هم بالا میرود.
string str = "abcde"; char ch = str[1]; // ch == 'b' str[1] = 'a'; // Compilation error! ch = str[50]; // IndexOutOfRangeException
string a="Hello \"C#\""; string b="Hello \r\n C#"; //مساوی با اینتر string c="C:\\a.jpg"; //چاپ خود علامت \ -مسیردهی
string c=@"C:\a.jpg";// == "C:\\a.jpg"
string source = "Some source"; string assigned = source;
string hel = "Hel"; string hello = "Hello"; string copy = hel + "lo";
string hello = "Hello"; string same = "Hello";
برای اطلاعات بیشتر در این زمینه این لینک را مطالعه نمایید.
Console.WriteLine(word1.Equals(word2, StringComparison.CurrentCultureIgnoreCase));
string score = "sCore"; string scary = "scary"; Console.WriteLine(score.CompareTo(scary)); Console.WriteLine(scary.CompareTo(score)); Console.WriteLine(scary.CompareTo(scary)); // Console output: // 1 // -1 // 0
string alpha = "alpha"; string score1 = "sCorE"; string score2 = "score"; Console.WriteLine(string.Compare(alpha, score1, false)); Console.WriteLine(string.Compare(score1, score2, false)); Console.WriteLine(string.Compare(score1, score2, true)); Console.WriteLine(string.Compare(score1, score2, StringComparison.CurrentCultureIgnoreCase)); // Console output: // -1 // 1 // 0 // 0
نکته : برای مقایسه برابری دو رشته از متد Equals یا == استفاده کنید و فقط برای تعیین کوچک یا بزرگ بودن از compareها استفاده نمایید. دلیل آن هم این است که برای مقایسه از فرهنگ culture فعلی سیستم استفاده میشود و نظم جدول یونیکد را رعایت نمیکنند و ممکن است بعضی رشتههای نابرابر با یکدیگر برابر باشند. برای مثال در زبان آلمانی دو رشته "SS" و "ß " با یکدیگر برابر هستند.
عبارات با قاعده Regular Expression
string doc = "Smith's number: 0898880022\nFranky can be " + "found at 0888445566.\nSteven's mobile number: 0887654321"; string replacedDoc = Regex.Replace( doc, "(08)[0-9]{8}", "$1********"); Console.WriteLine(replacedDoc); // Console output: // Smith's number: 08******** // Franky can be found at 08********. // Steven' mobile number: 08********
اتصال رشتهها در Loop
DateTime dt = DateTime.Now; string s = ""; for (int index = 1; index <= 20000; index++) { s += index.ToString(); } Console.WriteLine(s); Console.WriteLine(dt); Console.WriteLine(DateTime.Now); Console.ReadKey();
- قسمتی از حافظه به طور موقت برای این دور جدید حلقه، گرفته میشود که به آن بافر میگوییم.
- رشته قبلی به بافر انتقال میابد که بسته به مقدار آن زمان بر و کند است؛ 5 کیلو یا 5 مگابایت یا 50 مگابایت و ...
- شماره تولید شده جدید به بافر چسبانده میشود.
- بافر به یک رشته تبدیل میشود وجایی برای خود در حافظه Heap میگیرد.
- حافظه رشته قدیمی و بافر دیگر بلا استفاده شدهاند و توسط GC پاکسازی میشوند که ممکن است عملیاتی زمان بر باشد.
String Builder
string declared = "Intern pool"; string built = new StringBuilder("Intern pool").ToString();
StringBuilder sb = new StringBuilder(); sb.Append("Numbers: "); DateTime dt = DateTime.Now; for (int index = 1; index <= 200000; index++) { sb.Append(index); } Console.WriteLine(sb.ToString()); Console.WriteLine(dt); Console.WriteLine(DateTime.Now); Console.ReadKey();
StringBuilder sb = new StringBuilder(15); sb.Append("Hello, C#!");
استفاده از متد ایستای string.Format
DateTime date = DateTime.Now; string name = "David Scott"; string task = "Introduction to C# book"; string location = "his office"; string formattedText = String.Format( "Today is {0:MM/dd/yyyy} and {1} is working on {2} in {3}.", date, name, task, location); Console.WriteLine(formattedText);