مدل برنامه
در ابتدای کار نیاز است تا ساختاری را جهت ارائه لیستی از مطالب که دارای گزینه امتیاز دهی میباشند، تهیه کنیم:
namespace jQueryMvcSample03.Models { public class BlogPost { public int Id { set; get; } public string Title { set; get; } public string Body { set; get; } /// <summary> /// اطلاعات رای گیری یک مطلب به صورت یک خاصیت تو در تو یا پیچیده /// </summary> public Rating Rating { set; get; } public BlogPost() { Rating = new Rating(); } } }
namespace jQueryMvcSample03.Models { //[ComplexType] public class Rating { public double? TotalRating { get; set; } public int? TotalRaters { get; set; } public double? AverageRating { get; set; } } }
منبع داده فرضی برنامه
using System.Collections.Generic; using System.Linq; using jQueryMvcSample03.Models; namespace jQueryMvcSample03.DataSource { /// <summary> /// منبع داده فرضی /// </summary> public static class BlogPostDataSource { private static IList<BlogPost> _cachedItems; /// <summary> /// با توجه به استاتیک بودن سازنده کلاس، تهیه کش، پیش از سایر فراخوانیها صورت خواهد گرفت /// باید دقت داشت که این فقط یک مثال است و چنین کشی به معنای /// تهیه یک لیست برای تمام کاربران سایت است /// </summary> static BlogPostDataSource() { _cachedItems = createBlogPostsInMemoryDataSource(); } /// <summary> /// هدف صرفا تهیه یک منبع داده آزمایشی ساده تشکیل شده در حافظه است /// </summary> private static IList<BlogPost> createBlogPostsInMemoryDataSource() { var results = new List<BlogPost>(); for (int i = 1; i < 30; i++) { results.Add(new BlogPost { Id = i, Title = "عنوان " + i, Body = "متن ... متن ... متن " + i, Rating = new Rating { TotalRaters = i + 1, AverageRating = 3.5 } }); } return results; } /// <summary> /// پارامترهای شماره صفحه و تعداد رکورد به ازای یک صفحه برای صفحه بندی نیاز هستند /// شماره صفحه از یک شروع میشود /// </summary> public static IList<BlogPost> GetLatestBlogPosts(int pageNumber, int recordsPerPage = 4) { var skipRecords = pageNumber * recordsPerPage; return _cachedItems .OrderByDescending(x => x.Id) .Skip(skipRecords) .Take(recordsPerPage) .ToList(); } } }
در این منبع داده ابتدا لیستی از مطالب تهیه شده و سپس کش میشوند. در ادامه توسط متد GetLatestBlogPosts بازهای از این اطلاعات قابل بازیابی خواهند بود که برای استفاده در حالات صفحه بندی اطلاعات بهینه سازی شده است.
آشنایی با طراحی افزونه jQuery Star Rating
افزودن CSS نمایش امتیازها در ذیل هر مطلب
/* star rating system */ .post_rating { direction: ltr; } .rating { text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; display: inline-block; width: 8px; height: 16px; } .rating.stars { background-image: url('Images/star_rating.png'); } .rating.stars.active { cursor: pointer; } .star-left_off { background-position: -0px -0px; } .star-left_on { background-position: -16px -0px; } .star-right_off { background-position: -8px -0px; } .star-right_on { background-position: -24px -0px; }
افزودن افزونه jQuery Star rating
// <![CDATA[ (function ($) { $.fn.StarRating = function (options) { var defaults = { ratingStarsSpan: '.rating.stars', postInfoUrl: '/', loginUrl: '/login', errorHandler: null, completeHandler: null, onlyOneTimeHandler: null }; var options = $.extend(defaults, options); return this.each(function () { var ratingStars = $(this); $(ratingStars).unbind('mouseover'); $(ratingStars).mouseover(function () { var span = $(this).parent("span"); var newRating = $(this).attr("value"); setRating(span, newRating); }); $(ratingStars).unbind('mouseout'); $(ratingStars).mouseout(function () { var span = $(this).parent("span"); var rating = span.attr("rating"); setRating(span, rating); }); $(ratingStars).unbind('click'); $(ratingStars).click(function () { var span = $(this).parent("span"); var newRating = $(this).attr("value"); var text = span.children("span"); var pID = span.attr("post"); var type = span.attr("sectiontype"); postData({ postID: pID, rating: newRating, sectionType: type }); span.attr("rating", newRating); setRating(span, newRating); }); function setRating(span, rating) { span.find(options.ratingStarsSpan).each(function () { var value = parseFloat($(this).attr("value")); var imgSrc = $(this).attr("class"); if (value <= rating) $(this).attr("class", imgSrc.replace("_off", "_on")); else $(this).attr("class", imgSrc.replace("_on", "_off")); }); } function postData(dataJsonArray) { $.ajax({ type: "POST", url: options.postInfoUrl, data: JSON.stringify(dataJsonArray), contentType: "application/json; charset=utf-8", dataType: "json", complete: function (xhr, status) { var data = xhr.responseText; if (xhr.status == 403) { window.location = options.loginUrl; } else if (status === 'error' || !data) { if (options.errorHandler) options.errorHandler(this); } else if (data == "nok") { if (options.onlyOneTimeHandler) options.onlyOneTimeHandler(this); } else { if (options.completeHandler) options.completeHandler(this); } } }); } }); }; })(jQuery); // ]]>
کاری که این افزونه انجام میدهد ردیابی حرکت ماوس بر روی ستارههای نمایش داده شده و سپس ارسال سه پارامتر ذیل به اکشن متدی که توسط پارامتر postInfoUrl مشخص میگردد، پس از کلیک کاربر میباشد:
{ postID: pID, rating: newRating, sectionType: type }
در اینجا از errorHandler برای نمایش خطاها، از completeHandler برای نمایش تشکر به کاربر و از onlyOneTimeHandler برای نمایش اخطار مثلا «یکبار بیشتر مجاز نیستید به ازای یک مطلب رای دهید»، میتوان استفاده کرد.
بنابراین تا اینجا فایل layout برنامه تقریبا چنین مداخلی را خواهد داشت:
<head> <title>@ViewBag.Title</title> <link href="@Url.Content("Content/starRating.css")" rel="stylesheet" type="text/css" /> <link href="@Url.Content("Content/Site.css")" rel="stylesheet" type="text/css" /> <script src="@Url.Content("~/Scripts/jquery-1.9.1.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.StarRating.js")" type="text/javascript"></script> @RenderSection("JavaScript", required: false) </head>
طراحی یک HTML helper برای نمایش ستارههای امتیاز دهی
ابتدا پوشه استاندارد app_code را به پروژه اضافه کرده و سپس فایلی را به نام StarRatingHelper.cshtml، با محتوای ذیل به آن اضافه نمائید:
@using System.Globalization @helper AddStarRating(int postId, double? average = 0, int? postRatingsCount = 0, string type = "BlogPost", string tooltip = "لطفا جهت رای دادن کلیک نمائید") { string actIt = "active "; if (!average.HasValue) { average = 0; } if (!postRatingsCount.HasValue) { postRatingsCount = 0; } <span class='postRating' rating='@average' post='@postId' title='@tooltip' sectiontype='@type'> @for (double i = .5; i <= 5.0; i = i + .5) { string left; if (i <= average) { left = (i * 2) % 2 == 1 ? "left_on" : "right_on"; } else { left = (i * 2) % 2 == 1 ? "left_off" : "right_off"; } <span class='rating stars @(actIt)star-@left' value='@i'></span> } @if (postRatingsCount > 0) { var ratingInfo = string.Format(CultureInfo.InvariantCulture, "امتیاز {0:0.00} از 5 توسط {1} نفر", average, postRatingsCount); <span>@ratingInfo</span> } else { <span></span> } </span> }
کنترلر ذخیره سازی اطلاعات دریافتی برنامه
using System.Web.Mvc; using System.Web.UI; using jQueryMvcSample03.DataSource; using jQueryMvcSample03.Security; namespace jQueryMvcSample03.Controllers { public class HomeController : Controller { public ActionResult Index() { var postsList = BlogPostDataSource.GetLatestBlogPosts(pageNumber: 0); return View(postsList); //نمایش صفحه اصلی } [HttpPost] [AjaxOnly] [OutputCache(Location = OutputCacheLocation.None, NoStore = true)] public ActionResult SaveRatings(int? postId, double? rating, string sectionType) { if (postId == null || rating == null || string.IsNullOrWhiteSpace(sectionType)) return Content(null); //اعلام بروز خطا if (!this.HttpContext.CanUserVoteBasedOnCookies(postId.Value, sectionType)) return Content("nok"); //اعلام فقط یکبار مجاز هستید رای دهید switch (sectionType) //قسمتهای مختلف سایت که در جداول مختلفی قرار دارند نیز میتوانند گزینه امتیاز دهی داشته باشند { case "BlogPost": //الان شماره مطلب و رای ارسالی را داریم که میتوان نسبت به ذخیره آن اقدام کرد //مثلا //_blogPostsService.SaveRating(postId.Value, rating.Value); break; //... سایر قسمتهای دیگر سایت default: return Content(null); //اعلام بروز خطا } return Content("ok"); //اعلام موفقیت آمیز بودن ثبت اطلاعات } [HttpGet] public ActionResult Post(int? id) { if (id == null) return Redirect("/"); //todo: show the content here return Content("Post " + id.Value); } } }
امضای اکشن متد SaveRatings دقیقا بر اساس سه پارامتر ارسالی توسط jquery.StarRating.js که پیشتر توضیح داده شد، تعیین گردیده است. در این متد ابتدا بررسی میشود که آیا اطلاعاتی دریافت شده است یا خیر. اگر خیر، null را بازگشت خواهد داد. سپس توسط متد CanUserVoteBasedOnCookies بررسی میشود که آیا کاربر میتواند (خصوصا مجددا) رای دهد یا خیر. این افزونه برای رای دهی کاربران وارد نشده به سیستم نیز مناسب است. به همین جهت از کوکیها برای ثبت اطلاعات رای دادن کاربران استفاده گردیده است. پیاده سازی متد CanUserVoteBasedOnCookies را در ادامه ملاحظه خواهید نمود.
در ادامه در متد SaveRatings، یک switch تشکیل شده است تا بر اساس نام قسمت مرتبط به رای گیری، اطلاعات را بتوان به سرویس خاصی در برنامه هدایت کرد. مثلا اطلاعات قسمت مطالب به سرویس مطالب و قسمت نظرات به سرویس نظرات هدایت شوند.
متدهایی برای کار با کوکیها در ASP.NET MVC
using System; using System.Web; namespace jQueryMvcSample03.Security { public static class CookieHelper { public static bool CanUserVoteBasedOnCookies(this HttpContextBase httpContext, int postId, string sectionType) { string key = sectionType + "-" + postId; var value = httpContext.GetCookieValue(key); if (string.IsNullOrWhiteSpace(value)) { httpContext.AddCookie(key, key); return true; } return false; } public static void AddCookie(this HttpContextBase httpContextBase, string cookieName, string value) { httpContextBase.AddCookie(cookieName, value, DateTime.Now.AddDays(30)); } public static void AddCookie(this HttpContextBase httpContextBase, string cookieName, string value, DateTime expires) { var cookie = new HttpCookie(cookieName) { Expires = expires, Value = httpContextBase.Server.UrlEncode(value) // For Cookies and Unicode characters }; httpContextBase.Response.Cookies.Add(cookie); } public static string GetCookieValue(this HttpContextBase httpContext, string cookieName) { var cookie = httpContext.Request.Cookies[cookieName]; if (cookie == null) return string.Empty; //cookie doesn't exist // For Cookies and Unicode characters return httpContext.Server.UrlDecode(cookie.Value); } } }
پیشنهادی در مورد نحوه ذخیره سازی اطلاعات دریافتی
using jQueryMvcSample03.Models; namespace jQueryMvcSample03.DataSource { public interface IBlogPostsService { void SaveRating(int postId, double rating); } public class SampleService : IBlogPostsService { /// <summary> /// یک نمونه از متد ذخیره سازی اطلاعات پیشنهادی /// فقط برای ایده گرفتن /// بدیهی است محل قرارگیری اصلی آن در لایه سرویس برنامه شما خواهد بود /// </summary> public void SaveRating(int postId, double rating) { BlogPost post = null; //post = _blogCtx.Find(postId); // بر اساس شماره مطلب، مطلب یافت شده و فیلدهای آن تنظیم میشوند if (post == null) return; if (!post.Rating.TotalRaters.HasValue) post.Rating.TotalRaters = 0; if (!post.Rating.TotalRating.HasValue) post.Rating.TotalRating = 0; if (!post.Rating.AverageRating.HasValue) post.Rating.AverageRating = 0; post.Rating.TotalRaters++; post.Rating.TotalRating += rating; post.Rating.AverageRating = post.Rating.TotalRating / post.Rating.TotalRaters; // todo: call save changes at the end. } } }
Viewهای برنامه
قسمت پایانی کار ما در اینجا تهیه دو View است:
الف) یک Partial view که لیست مطالب را به همراه گزینه رای دهی به آنها رندر میکند.
ب) View کاملی که از این Partial View استفاده کرده و همچنین افزونه jquery.StarRating.js را فراخوانی میکند.
@using System.Text.RegularExpressions @model IList<jQueryMvcSample03.Models.BlogPost> <ul> @foreach (var item in Model) { <li> <fieldset> <legend>مطلب @item.Id</legend> <h5> @Html.ActionLink(linkText: item.Title, actionName: "Post", controllerName: "Home", routeValues: new { id = item.Id }, htmlAttributes: null) </h5> @item.Body <div class="post_rating"> @Html.Raw(Regex.Replace(@StarRatingHelper.AddStarRating(item.Id, item.Rating.AverageRating, item.Rating.TotalRaters, "BlogPost").ToHtmlString(), @">\s+<", "><")) </div> </fieldset> </li> } </ul>
اگر به کدهای آن دقت کنید از Regex.Replace برای حذف فاصلههای خالی و خطوط جدید بین تگها استفاده گردیده است. اگر اینکار انجام نشود، نیمههای ستارههای نمایش داده شده، با فاصله از یکدیگر رندر میشوند که صورت خوشایندی ندارد.
و نهایتا View ایی که از این اطلاعات استفاده میکنید ساختار زیر را خواهد داشت:
@model IList<jQueryMvcSample03.Models.BlogPost> @{ ViewBag.Title = "Index"; var postInfoUrl = Url.Action(actionName: "SaveRatings", controllerName: "Home"); } <h2> سیستم امتیاز دهی</h2> @{ Html.RenderPartial("_ItemsList", Model); } @section JavaScript { <script type="text/javascript"> $(document).ready(function () { $(".rating.stars.active").StarRating({ ratingStarsSpan: '.rating.stars', postInfoUrl: '@postInfoUrl', loginUrl: '/login', errorHandler: function () { alert('خطایی رخ داده است'); }, completeHandler: function () { alert('با تشکر! رای شما با موفقیت ثبت شد'); }, onlyOneTimeHandler: function () { alert('فقط یکبار میتوانید به ازای هر مطلب رای دهید'); } }); }); </script> }
دریافت کدها و پروژه کامل این قسمت
jQueryMvcSample03.zip
تهیه یک بانک اطلاعاتی نمونه
برای نمایش امکانات کار با روش Database first، نیاز است یک بانک اطلاعاتی را به صورت مستقل و متداولی ایجاد کنیم. به همین جهت اسکریپت SQL ذیل را توسط Management studio اجرا کنید تا بانک اطلاعاتی BloggingCore2016، به همراه دو جدول به هم وابسته، در آن ایجاد شوند:
CREATE DATABASE [BloggingCore2016] GO USE [BloggingCore2016] GO CREATE TABLE [Blog] ( [BlogId] int NOT NULL IDENTITY, [Url] nvarchar(max) NOT NULL, CONSTRAINT [PK_Blog] PRIMARY KEY ([BlogId]) ); GO CREATE TABLE [Post] ( [PostId] int NOT NULL IDENTITY, [BlogId] int NOT NULL, [Content] nvarchar(max), [Title] nvarchar(max), CONSTRAINT [PK_Post] PRIMARY KEY ([PostId]), CONSTRAINT [FK_Post_Blog_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blog] ([BlogId]) ON DELETE CASCADE ); GO INSERT INTO [Blog] (Url) VALUES ('https://www.dntips.ir/'), ('http://blogs.msdn.com/dotnet'), ('http://blogs.msdn.com/webdev'), ('http://blogs.msdn.com/visualstudio') GO
پیشنیازهای مهندسی معکوس ساختار بانک اطلاعاتی در EF Core
در قسمت اول در حین بررسی «برپایی تنظیمات اولیهی EF Core 1.0 در یک برنامهی ASP.NET Core 1.0»، چهار مدخل جدید را به فایل project.json برنامه اضافه کردیم. مدخل جدید Microsoft.EntityFrameworkCore.Tools که به قسمت tools آن اضافه شد، پیشنیاز اصلی کار با EF Core Migrations است. همچنین وجود مدخل Microsoft.EntityFrameworkCore.SqlServer.Design برای تدارک امکانات مهندسی معکوس ساختار یک بانک اطلاعاتی SQL Server ضروری است.
تبدیل ساختار دیتابیس BloggingCore2016 به کدهای معادل EF Core آن
پس از فعال سازی ابزارهای خط فرمان EF Core، به پوشهی اصلی پروژه مراجعه کرده، کلید shift را نگه دارید. سپس کلیک راست کرده و گزینهی Open command window here را انتخاب کنید تا خط فرمان از این پوشه آغاز شود. در ادامه دستور ذیل را صادر کنید:
dotnet ef dbcontext scaffold "Data Source=(local);Initial Catalog=BloggingCore2016;Integrated Security = true" Microsoft.EntityFrameworkCore.SqlServer -o Entities --context MyDBDataContext --verbose
اجرا این دستور سبب اتصال به رشتهی اتصالی ذکر شده که به بانک اطلاعاتی BloggingCore2016 اشاره میکند، میشود. سپس پروایدر مدنظر ذکر شدهاست. سوئیچ o محل درج فایلهای نهایی را مشخص میکند. برای مثال در اینجا فایلهای نهایی مهندسی معکوس شده در پوشهی Entities درج میشوند (تصویر فوق). همچنین در اینجا امکان ذکر فایل context تولیدی نیز وجود دارد. اگر علاقمند باشید تا تمام ریز جزئیات این عملیات را نیز مشاهده کنید، میتوانید پارامتر اختیاری verbose را نیز به انتهای دستور اضافه نمائید.
بقیه مراحل کار با این فایلهای تولید شده، با نکاتی که تاکنون عنوان شدهاند یکی است. برای مثال اگر میخواهید رشتهی اتصالی پیش فرض را از این Context تولید شده خارج کنید:
public partial class MyDBDataContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Data Source=(local);Initial Catalog=BloggingCore2016;Integrated Security = true"); }
بررسی پارامترهای دیگر ابزار مهندسی معکوس به Code First
اگر دستور dotnet ef dbcontext scaffold --help را صادر کنیم، خروجی راهنمای ذیل را میتوان مشاهده کرد:
Usage: dotnet ef dbcontext scaffold [arguments] [options] Arguments: [connection] The connection string of the database [provider] The provider to use. For example, Microsoft.EntityFrameworkCore.SqlServer Options: -a|--data-annotations Use DataAnnotation attributes to configure the model where possible. If omitted, the output code will use only the fluent API. -c|--context <name> Name of the generated DbContext class. -f|--force Force scaffolding to overwrite existing files. Otherwise, the code will only proceed if no output files would be overwritten. -o|--output-dir <path> Directory of the project where the classes should be output. If omitted, the top-level project directory is used. --schema <schema> Selects a schema for which to generate classes. -t|--table <schema.table> Selects a table for which to generate classes. -e|--environment <environment> The environment to use. If omitted, "Development" is used. -h|--help Show help information -v|--verbose Enable verbose output
- حالت پیش فرض تنظیمات روابط مدلها در این روش، حالت استفاده از Fluent API است. اگر میخواهید آنرا به حالت استفادهی از Data Annotations تغییر دهید، پارامتر a- و یا data-annotations-- را در دستور نهایی ذکر کنید.
- حالت پیش فرض تولید فایلهای نهایی این روش، عدم بازنویسی فایلهای موجود است. اگر میخواهید پس از تغییر بانک اطلاعاتی، مجددا این فایلها را از صفر تولید کنید، پارامتر f- و یا force- را در دستور نهایی ذکر کنید.
بنابراین اگر میخواهید هربار فایلهای نهایی را بازنویسی کنید و همچنین روش کار با Data Annotations را ترجیح میدهید، دستور نهایی، شکل زیر را پیدا خواهد کرد:
dotnet ef dbcontext scaffold "Data Source=(local);Initial Catalog=BloggingCore2016;Integrated Security = true" Microsoft.EntityFrameworkCore.SqlServer -o Entities --context MyDBDataContext --verbose --force --data-annotations
کار با یک بانک اطلاعاتی موجود، با روش مهاجرتهای Code First
فرض کنید میخواهید از یک بانک اطلاعاتی از پیش موجود EF 6.x (یا هر بانک اطلاعاتی از پیش موجود دیگری)، به روش پیش فرض EF Core استفاده کنید. برای این منظور:
- ابتدا جدول migration history قدیمی آنرا حذف کنید؛ چون ساختار آن با EF Core یکی نیست.
- سپس با استفاده از دستور dotnet ef dbcontext scaffold فوق، معادل کلاسها، روابط و Context سازگار با EF Core آنرا تولید کنید.
- در ادامه رشتهی اتصالی پیش فرض آنرا از کلاس Context تولیدی خارج کرده و از یکی از روشهای مطرح شدهی در مطلب «شروع به کار با EF Core 1.0 - قسمت 1 - برپایی تنظیمات اولیه» استفاده کنید.
- سپس نیاز است این Context جدید را توسط متد services.AddDbContext به لیست سرویسهای برنامه اضافه کنید. این مورد نیز در قسمت اول بررسی شدهاست.
- مرحلهی بعد، افزودن جدول __EFMigrationsHistory جدید EF Core، به این بانک اطلاعاتی است. برای این منظور به روش متداول فعال کردن مهاجرتها، دستور ذیل را صادر کنید:
dotnet ef migrations add InitialDatabase
using Microsoft.EntityFrameworkCore.Migrations; namespace Core1RtmEmptyTest.DataLayer.Migrations { public partial class InitialDatabase : Migration { protected override void Up(MigrationBuilder migrationBuilder) { } protected override void Down(MigrationBuilder migrationBuilder) { } } }
سپس دستور به روز رسانی بانک اطلاعاتی را صادر کنید:
dotnet ef database update
پس از این مرحله، روش کار، Code first خواهد بود. برای مثال خاصیتی را به کلاسی اضافه میکنید و سپس دو دستور ذیل را صادر خواهید کرد که در آن v2 یک نام دلخواه است:
dotnet ef migrations add v2 dotnet ef database update
پس از نصب Git اطمینان حاصل کنید که NodeJs ، npm و jspm نیز بر روی سیستم شما نصب باشند. در این قسمت گفتهایم که چگونه از این «اطمینان» آگاه شوید.
حال نوبت به ساخت اولین پروژهی MVC ما میرسد. یک پروژه MVC جدید با نام دلخواه خودتان در مسیر دلخواه خودتان ایجاد کنید. با خط فرمان، در ریشهی پروژه دستور زیر را اجرا کنید:
jspm init
بدون هیچ تغییری، به هیچ کدام از سوالات پاسخ ندهید و از دکمهی enter استفاده کنید تا مقادیر پیشفرض اعمال شوند. اگر تصویر زیر را در خروجی مشاهده کردید یعنی تا بدین جای کار به درستی پیش رفتهاید :
حالا نوبت به نصب محتویات Aurelia میباشد. برای این کار دستورات زیر را اجرا کنید :
jspm install aurelia-framework jspm install aurelia-bootstrapper
توجه داشته باشید، اگر دستورات بالا به درستی اجرا و تکمیل شوند، باید پس از پایان هر دستور، پیام زیر را در انتهای خروجی مشاهده کنید:
پس از این کارها، فایل Layout را باز کنید و کدهای آن را به صورت زیر تغییر دهید:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Aurelia - www.dotnettips.info</title> </head> <body aurelia-app> <div> @RenderBody() </div> <script src="~/jspm_packages/system.js"></script> <script src="~/config.js"></script> <script> System.import("aurelia-bootstrapper"); </script> </body> </html>
export class App { }
<template> <h3>www.dotnettips.info</h3> </template>
در بخشهای بعدی در مورد کدهای فوق و همچنین به سایر مباحث دیگر Aurelia میپردازیم.
نکتهی تکمیلی
زمانیکه میخواهید از دستوارت jspm استفاده کنید، باید به ریشهی برنامه مراجعه کنید. حال اگر پوشههای تودرتوی زیادی داشته باشید، این رفت و آمدها زمانبر و خسته کننده خواهند شد. برای حل این مشکل کافیست روی پروژه، راست کلیک کنید و گزینهی Open Folder in File Explorer را انتخاب کنید تا ریشهی پروژه باز شود. حالا کافیست در فضای خالی Windows Explorer کلید Shift را گرفته و راست کلیک کنید. حالا یک آیتم جدید با نام Open command window here ظاهر شده است. کافیست روی آن کلیک کنید. CMD در ریشهی سایت باز خواهد شد.
دانلود پروژه جاری در مخزن گیت
Roslyn #6
پیشنیاز این بحث نصب مواردی است که در مطلب «شروع به کار با Roslyn » در قسمت دوم عنوان شدند:
الف) نصب SDK ویژوال استودیوی 2015
ب) نصب قالبهای ایجاد پروژههای مخصوص Roslyn
البته این قالبها چیزی بیشتر از ایجاد یک پروژهی کلاس Library جدید و افزودن ارجاعاتی به بستهی نیوگت Microsoft.CodeAnalysis، نیستند. اما درکل زمان ایجاد و تنظیم این نوع پروژهها را خیلی کاهش میدهند و همچنین یک پروژهی تست را ایجاد کرده و تولید بستهی نیوگت و فایل VSIX را نیز بسیار ساده میکنند.
هدف از تولید Analyzers
بسیاری از مجموعهها و شرکتها، یک سری قوانین و اصول خاصی را برای کدنویسی وضع میکنند تا به کدهایی با قابلیت خوانایی بهتر و نگهداری بیشتر برسند. با استفاده از Roslyn و آنالیز کنندههای آن میتوان این قوانین را پیاده سازی کرد و خطاها و اخطارهایی را به برنامه نویسها جهت رفع اشکالات موجود، نمایش داده و گوشزد کرد. بنابراین هدف از آنالیز کنندههای Roslyn، سهولت تولید ابزارهایی است که بتوانند برنامه نویسها را ملزم به رعایت استانداردهای کدنویسی کنند.
همچنین معلمها نیز میتوانند از این امکانات جهت ارائهی نکات ویژهای به تازهکاران کمک بگیرند. برای مثال اگر این قسمت از کد اینگونه باشد، بهتر است؛ مثلا بهتر است فیلدهای سطح کلاس، خصوصی تعریف شوند و امکان دسترسی به آنها صرفا از طریق متدهایی که قرار است با آنها کار کنند صورت گیرد.
این آنالیز کنندها به صورت پویا در حین تایپ کدها در ویژوال استودیو فعال میشوند و یا حتی به صورت خودکار در طی پروسهی Build پروژه نیز میتوانند ظاهر شده و خطاها و اخطارهایی را گزارش کنند.
بررسی مثال معتبری که میتواند بهتر باشد
در اینجا یک کلاس نمونه را مشاهده میکنید که در آن فیلدهای کلاس به صورت public تعریف شدهاند.
public class Student { public string FirstName; public string LastName; public int TotalPointsEarned; public void TakeExam(int pointsForExam) { TotalPointsEarned += pointsForExam; } public void ExtraCredit(int extraPoints) { TotalPointsEarned += extraPoints; } public int PointsEarned { get { return TotalPointsEarned; } } }
بنابراین در ادامه هدف ما این است که یک Roslyn Analyzer جدید را طراحی کنیم تا از طریق آن هشدارهایی را جهت تبدیل فیلدهای عمومی به خصوصی، به برنامه نویس نمایش دهیم.
با اجرای افزونهی View->Other windows->Syntax visualizer، تصویر فوق نمایان خواهد شد. بنابراین در اینجا نیاز است FieldDeclarationها را یافته و سپس tokenهای آنها را بررسی کنیم و مشخص کنیم که آیا نوع یا Kind آنها public است (PublicKeyword) یا خیر؟ اگر بلی، آن مورد را به صورت یک Diagnostic جدید گزارش میدهیم.
ایجاد اولین Roslyn Analyzer
پس از نصب پیشنیازهای بحث، به شاخهی قالبهای extensibility در ویژوال استودیو مراجعه کرده و یک پروژهی جدید از نوع Analyzer with code fix را آغاز کنید.
قالب Stand-alone code analysis tool آن دقیقا همان برنامههای کنسول بحث شدهی در قسمتهای قبل است که تنها ارجاعی را به بستهی نیوگت Microsoft.CodeAnalysis به صورت خودکار دارد.
قالب پروژهی Analyzer with code fix علاوه بر ایجاد پروژههای Test و VSIX جهت بسته بندی آنالایزر تولید شده، دارای دو فایل DiagnosticAnalyzer.cs و CodeFixProvider.cs پیش فرض نیز هست. این دو فایل قالبهایی را جهت شروع به کار تهیهی آنالیز کنندههای مبتنی بر Roslyn ارائه میدهند. کار DiagnosticAnalyzer آنالیز کد و ارائهی خطاهایی جهت نمایش به ویژوال استودیو است و CodeFixProvider این امکان را مهیا میکند که این خطای جدید عنوان شدهی توسط آنالایزر، چگونه باید برطرف شود و راهکار بازنویسی Syntax tree آنرا ارائه میدهد.
همین پروژهی پیش فرض ایجاد شده نیز قابل اجرا است. اگر بر روی F5 کلیک کنید، یک کپی جدید و محصور شدهی ویژوال استودیو را باز میکند که در آن افزونهی در حال تولید به صورت پیش فرض و محدود نصب شدهاست. اکنون اگر پروژهی جدیدی را جهت آزمایش، در این وهلهی محصور شدهی ویژوال استودیو باز کنیم، قابلیت اجرای خودکار آنالایزر در حال توسعه را فراهم میکند. به این ترتیب کار تست و دیباگ آنالایزرها با سهولت بیشتری قابل انجام است.
این پروژهی پیش فرض، کار تبدیل نام فضاهای نام را به upper case، به صورت خودکار انجام میدهد (که البته بیمعنا است و صرفا جهت نمایش و ارائهی قالبهای شروع به کار مفید است).
نکتهی دیگر آن، تعریف تمام رشتههای مورد نیاز آنالایزر در یک فایل resource به نام Resources.resx است که در جهت بومی سازی پیامهای خطای آن میتواند بسیار مفید باشد.
در ادامه کدهای فایل DiagnosticAnalyzer.cs را به صورت ذیل تغییر دهید:
using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; namespace CodingStandards { [DiagnosticAnalyzer(LanguageNames.CSharp)] public class CodingStandardsAnalyzer : DiagnosticAnalyzer { public const string DiagnosticId = "CodingStandards"; // You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat. internal static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.AnalyzerTitle), Resources.ResourceManager, typeof(Resources)); internal static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.AnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources)); internal static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.AnalyzerDescription), Resources.ResourceManager, typeof(Resources)); internal const string Category = "Naming"; internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor( DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } } public override void Initialize(AnalysisContext context) { // TODO: Consider registering other actions that act on syntax instead of or in addition to symbols context.RegisterSyntaxNodeAction(analyzeFieldDeclaration, SyntaxKind.FieldDeclaration); } static void analyzeFieldDeclaration(SyntaxNodeAnalysisContext context) { var fieldDeclaration = context.Node as FieldDeclarationSyntax; if (fieldDeclaration == null) return; var accessToken = fieldDeclaration .ChildTokens() .SingleOrDefault(token => token.Kind() == SyntaxKind.PublicKeyword); // Note: Not finding protected or internal if (accessToken.Kind() != SyntaxKind.None) { // Find the name of the field: var name = fieldDeclaration.DescendantTokens() .SingleOrDefault(token => token.IsKind(SyntaxKind.IdentifierToken)).Value; var diagnostic = Diagnostic.Create(Rule, fieldDeclaration.GetLocation(), name, accessToken.Value); context.ReportDiagnostic(diagnostic); } } } }
اولین کاری که در این کلاس انجام شده، خواندن سه رشتهی AnalyzerDescription (توضیحی در مورد آنالایزر)، AnalyzerMessageFormat (پیامی که به کاربر نمایش داده میشود) و AnalyzerTitle (عنوان پیام) از فایل Resources.resx است. این فایل را گشوده و محتوای آنرا مطابق تنظیمات ذیل تغییر دهید:
سپس کار به متد Initialize میرسد. در اینجا برخلاف مثالهای قسمتهای قبل، context مورد نیاز، توسط پارامترهای override شدهی کلاس پایه DiagnosticAnalyzer فراهم میشوند. برای مثال در متد Initialize، این فرصت را خواهیم داشت تا به ویژوال استودیو اعلام کنیم، قصد آنالیز فیلدها یا FieldDeclaration را داریم. پارامتر اول متد RegisterSyntaxNodeAction یک delegate یا Action است. این Action کار فراهم آوردن context کاری را برعهده دارد که نحوهی استفادهی از آنرا در متد analyzeFieldDeclaration میتوانید ملاحظه کنید.
سپس در اینجا نوع نود در حال آنالیز (همان نودی که کاربر در ویژوال استودیو انتخاب کردهاست یا در حال کار با آن است)، به نوع تعریف فیلد تبدیل میشود. سپس توکنهای آن استخراج شده و بررسی میشود که آیا یکی از این توکنها کلمهی کلیدی public هست یا خیر؟ اگر این فیلد عمومی تعریف شده بود، نام آنرا یافته و به عنوان یک Diagnostic جدید بازگشت و گزارش میدهیم.
ایجاد اولین Code fixer
در ادامه فایل CodeFixProvider.cs پیش فرض را گشوده و تغییرات ذیل را به آن اعمال کنید. در اینجا مهمترین تغییر صورت گرفته نسبت به قالب پیش فرض، اضافه شدن متد makePrivateDeclarationAsync بجای متد MakeUppercaseAsync از پیش موجود آن است:
using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace CodingStandards { [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CodingStandardsCodeFixProvider)), Shared] public class CodingStandardsCodeFixProvider : CodeFixProvider { public sealed override ImmutableArray<string> FixableDiagnosticIds { get { return ImmutableArray.Create(CodingStandardsAnalyzer.DiagnosticId); } } public sealed override FixAllProvider GetFixAllProvider() { return WellKnownFixAllProviders.BatchFixer; } public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); // TODO: Replace the following code with your own analysis, generating a CodeAction for each fix to suggest var diagnostic = context.Diagnostics.First(); var diagnosticSpan = diagnostic.Location.SourceSpan; // Find the type declaration identified by the diagnostic. var declaration = root.FindToken(diagnosticSpan.Start) .Parent.AncestorsAndSelf().OfType<FieldDeclarationSyntax>() .First(); // Register a code action that will invoke the fix. context.RegisterCodeFix( CodeAction.Create("Make Private", c => makePrivateDeclarationAsync(context.Document, declaration, c)), diagnostic); } async Task<Document> makePrivateDeclarationAsync(Document document, FieldDeclarationSyntax declaration, CancellationToken c) { var accessToken = declaration.ChildTokens() .SingleOrDefault(token => token.Kind() == SyntaxKind.PublicKeyword); var privateAccessToken = SyntaxFactory.Token(SyntaxKind.PrivateKeyword); var root = await document.GetSyntaxRootAsync(c); var newRoot = root.ReplaceToken(accessToken, privateAccessToken); return document.WithSyntaxRoot(newRoot); } } }
کاری که در متد RegisterCodeFixesAsync انجام میشود، مشخص کردن اولین مکانی است که مشکلی در آن گزارش شدهاست. سپس به این مکان منوی Make Private با متد متناظر با آن معرفی میشود. در این متد، اولین توکن public، مشخص شده و سپس با یک توکن private جایگزین میشود. اکنون این syntax tree بازنویسی شده بازگشت داده میشود. با Syntax Factory در قسمت سوم آشنا شدیم.
خوب، تا اینجا یک analyzer و یک code fixer را تهیه کردهایم. برای آزمایش آن دکمهی F5 را فشار دهید تا وهلهای جدید از ویژوال استودیو که این آنالایزر جدید در آن نصب شدهاست، آغاز شود. البته باید دقت داشت که در اینجا باید پروژهی CodingStandards.Vsix را به عنوان پروژهی آغازین ویژوال استودیو معرفی کنید؛ چون پروژهی class library آنالایزرها را نمیتوان مستقیما اجرا کرد. همچنین یکبار کل solution را نیز build کنید.
پس از اینکه وهلهی جدید ویژوال استودیو شروع به کار کرد (بار اول اجرای آن کمی زمانبر است؛ زیرا باید تنظیمات وهلهی ویژهی اجرای افزونهها را از ابتدا اعمال کند)، همان پروژهی Student ابتدای بحث را در آن باز کنید.
نتیجهی اعمال این افزونهی جدید را در تصویر فوق ملاحظه میکنید. زیر سطرهای دارای فیلد عمومی، خط قرمز کشیده شدهاست (به علت تعریف DiagnosticSeverity.Error). همچنین حالت فعلی و حالت برطرف شده را نیز با رنگهای قرمز و سبز میتوان مشاهده کرد. کلیک بر روی گزینهی make private، سبب اصلاح خودکار آن سطر میگردد.
روش دوم آزمایش یک Roslyn Analyzer
همانطور که از انتهای بحث قسمت دوم بهخاطر دارید، این آنالایزرها را میتوان به کامپایلر نیز معرفی کرد. روش انجام اینکار در ویژوال استودیوی 2015 در تصویر ذیل نمایش داده شدهاست.
نود references را باز کرده و سپس بر روی گزینهی analyzers کلیک راست نمائید. در اینجا گزینهی Add analyzer را انتخاب کنید. در صفحهی باز شده بر روی دکمهی browse کلیک کنید. در اینجا میتوان فایل اسمبلی موجود در پوشهی CodingStandards\bin\Debug را به آن معرفی کرد.
بلافاصله پس از معرفی این اسمبلی، آنالایزر آن شناسایی شده و همچنین فعال میگردد.
در این حالت اگر برنامه را کامپایل کنیم، با خطاهای جدید فوق متوقف خواهیم شد و برنامه کامپایل نمیشود (به علت تعریف DiagnosticSeverity.Error).
معرفی Zen Coding یا Emmet Syntax
معرفی Zen Coding یا Emmet Syntax
برای من همیشه نوشتن کدهای HTML با دشواری و مرارتهای زیادی همراه بوده. نوشتن ساختارهای تو در تو، قرار دادن id و class مناسب و باز و بسته کردن تگها و قرار دادن قرزندها در داخل تگها حقیقتا کار هایی خسته کننده، تکراری و حوصله سر بر هستند.
روش Zen Coding یا Emmet Syntax برای حل همین مسئله به وجود آمده و سرعت توسعه کدهای HTML را بسیار بیشتر خواهد کرد. پس از استفاده از این روش، به هیچ وجه قادر نخواهید بود که به روش سنتیِ گذشته کدهای HTML بزنید.
1. مثلا با استفاده از دستور div#control.panel.panel-success و فشردن کلید tab، یک div با شناسه panel و کلاسهای panel و panel-success ساخته خواهد شد.
2. با استفاده از Zen Codding به راحتی میتوانیم المنتهای داخلی هم در یک خط تعریف کنیم. مثلا ul>li*5 موجب خواهد شد یک ul همراه با پنج المنت li در درون اش تعریف شود.
سعی کنید حتما از روش Zen Coding استفاده کنید، چرا که سرعت توسعه شما را به شدت بالا خواهد برد.
Zen Coding پس از نصب افزونه Web Essentials در ویژوال استودیو افزوده خواهد شد. همچنین Visual Studio Code به طور پیش فرض از این روش پشتیبانی میکند.
public interface IAppVersionService { string GetAppVersion(); }
public class AndroidAppVersionService : IAppVersionService { public Android.Content.Context Context { get; set; } public string GetAppVersion() { return Context.PackageManager.GetPackageInfo(Context.PackageName, 0).VersionName; } }
containerBuilder.RegisterType<AndroidAppVersionService>() .As<IAppVersionService>() .PropertiesAutowired(PropertyWiringOptions.PreserveSetValues);
public class iOSAppVersionService : IAppVersionService { public string GetAppVersion() { var infoDictionary = NSBundle.MainBundle.InfoDictionary; return infoDictionary?["CFBundleShortVersionString"] as NSString; } }
public string GetAppVersion() { return $"{Package.Current.Id.Version.Major}.{Package.Current.Id.Version.Minor}"; }
async Task ShowSomeAlertToAndroidPhoneUsersOnly() { if (DeviceService.RuntimePlatform == RuntimePlatform.Android && DeviceService.Idiom == TargetIdiom.Phone) { await UserDialogs.AlertAsync("Some alert to android phone users only!", "Test"); } }
public class XamAppEntryRenderer : EntryRenderer { protected override void OnElementChanged(ElementChangedEventArgs<Entry> e) { base.OnElementChanged(e); if (e.NewElement != null) /* e.NewElement is a Xamarin Forms' Entry */ { Control.ClearButtonMode = UITextFieldViewMode.WhileEditing; // Control is UITextField } } }
[assembly: ExportRenderer(typeof(Entry), typeof(XamAppEntryRenderer))]
ایجاد کاتالوگهای Full text search و ایندکسهای آن
همانطور که در قسمت قبل نیز عنوان شد، فیلترهای FTS آفیس، علاوه بر اینکه امکان جستجوی پیشرفته FTS را بر روی کلیه فایلهای مجموعه آفیس میسر میکنند، امکان جستجوی FTS را بر روی خواص ویژه اضافی آنها، مانند نام نویسنده، واژههای کلیدی، تاریخ ایجاد و امثال آن نیز به همراه دارند.
اینکه چه خاصیتی را بتوان جستجو کرد نیز بستگی به نوع فیلتر نصب شده دارد. برای تعریف خواص قابل جستجوی یک سند، باید یک SEARCH PROPERTY LIST را ایجاد کرد:
CREATE SEARCH PROPERTY LIST WordSearchPropertyList; GO ALTER SEARCH PROPERTY LIST WordSearchPropertyList ADD 'Authors' WITH (PROPERTY_SET_GUID = 'F29F85E0-4FF9-1068-AB91-08002B27B3D9', PROPERTY_INT_ID = 4, PROPERTY_DESCRIPTION = 'System.Authors - authors of a given item.'); GO
بهبود کیفیت جستجو توسط Stop lists و Stop words
به یک سری از کلمات و حروف، اصطلاحا noise words گفته میشود. برای مثال در زبان انگلیسی حروف و کلماتی مانند a، is، the و and به صورت خودکار از FTS حذف میشوند؛ چون جستجوی آنها بیحاصل است. به اینها stop words نیز میگویند.
با استفاده از کوئری ذیل میتوان لیست stop words تعریف شده در بانک اطلاعاتی جاری را مشاهده کرد:
-- Check the Stopwords list SELECT w.stoplist_id, l.name, w.stopword, w.language FROM sys.fulltext_stopwords AS w INNER JOIN sys.fulltext_stoplists AS l ON w.stoplist_id = l.stoplist_id;
-- Stopwords list CREATE FULLTEXT STOPLIST SQLStopList; GO -- Add a stopword ALTER FULLTEXT STOPLIST SQLStopList ADD 'SQL' LANGUAGE 'English'; GO
کاتالوگهای Full Text Search
ایندکسهای ویژهی FTS، در مکانهایی به نام Full Text Catalogs ذخیره میشوند. این کاتالوگها صرفا یک شیء مجازی بوده و تنها برای تعریف ظرفی دربرگیرندهی ایندکسهای FTS تعریف میشوند. در نگارشهای پیش از 2012 اس کیوال سرور، این کاتالوگها اشیایی فیزیکی بودند؛ اما اکنون تبدیل به اشیایی مجازی شدهاند.
حالت کلی تعریف یک fulltext catalog به نحو ذیل است:
create fulltext catalog catalog_name on filegroup filegroup_name in path 'rootpath' with some_options as default authoriztion owner_name accent_sensivity = {on|off}
به صورت پیش فرض حساسیت به لهجه یا accent_sensivity خاموش است. اگر روشن شود، باید کل ایندکس مجددا بازسازی شود.
ایجاد ایندکسهای Full Text
پس از ایجاد یک fulltext catalog، اکنون نوبت به تعریف ایندکسهایی فیزیکی هستند که داخل این کاتالوگها ذخیره خواهند شد:
-- Full-text catalog CREATE FULLTEXT CATALOG DocumentsFtCatalog; GO -- Full-text index CREATE FULLTEXT INDEX ON dbo.Documents ( docexcerpt Language 1033, doccontent TYPE COLUMN doctype Language 1033 STATISTICAL_SEMANTICS ) KEY INDEX PK_Documents ON DocumentsFtCatalog WITH STOPLIST = SQLStopList, SEARCH PROPERTY LIST = WordSearchPropertyList, CHANGE_TRACKING AUTO; GO
CHANGE_TRACKING AUTO به این معنا است که SQL Server به صورت خودکار کار به روز رسانی این ایندکس را با تغییرات رکوردها انجام خواهد داد.
ذکر STATISTICAL_SEMANTICS، منحصر به SQL Server 2012 بوده و کار آن تشخیص واژههای کلیدی و ایجاد ایندکسهای یافتن اسناد مشابه است. برای استفاده از آن حتما نیاز است مطابق توضیحات قسمت قبل، Semantic Language Database پیشتر نصب شده باشد.
توسط STOPLIST، لیست واژههایی که قرار نیست ایندکس شوند را معرفی خواهیم کرد. SQLStopList را در ابتدای بحث ایجاد کردیم.
Language 1033 به معنای استفاده از زبان US English است.
نحوهی استفاده از SEARCH PROPERTY LIST ایی که پیشتر تعریف کردیم را نیز در اینجا ملاحظه میکنید.
مثالی برای ایجاد ایندکسهای FTS
برای اینکه ربط منطقی نکات عنوان شده را بهتر بتوانید بررسی و آزمایش کنید، مثال ذیل را درنظر بگیرید.
ابتدا جدول Documents را برای ذخیره سازی تعدادی سند، ایجاد میکنیم:
CREATE TABLE dbo.Documents ( id INT IDENTITY(1,1) NOT NULL, title NVARCHAR(100) NOT NULL, doctype NCHAR(4) NOT NULL, docexcerpt NVARCHAR(1000) NOT NULL, doccontent VARBINARY(MAX) NOT NULL, CONSTRAINT PK_Documents PRIMARY KEY CLUSTERED(id) );
سپس اطلاعاتی را در این جدول ثبت میکنیم:
-- Insert data -- First row INSERT INTO dbo.Documents (title, doctype, docexcerpt, doccontent) SELECT N'Columnstore Indices and Batch Processing', N'docx', N'You should use a columnstore index on your fact tables, putting all columns of a fact table in a columnstore index. In addition to fact tables, very large dimensions could benefit from columnstore indices as well. Do not use columnstore indices for small dimensions. ', bulkcolumn FROM OPENROWSET (BULK 'C:\Users\Vahid\Desktop\Updates\fts_docs\ColumnstoreIndicesAndBatchProcessing.docx', SINGLE_BLOB) AS doc; -- Second row INSERT INTO dbo.Documents (title, doctype, docexcerpt, doccontent) SELECT N'Introduction to Data Mining', N'docx', N'Using Data Mining is becoming more a necessity for every company and not an advantage of some rare companies anymore. ', bulkcolumn FROM OPENROWSET (BULK 'C:\Users\Vahid\Desktop\Updates\fts_docs\IntroductionToDataMining.docx', SINGLE_BLOB) AS doc; -- Third row INSERT INTO dbo.Documents (title, doctype, docexcerpt, doccontent) SELECT N'Why Is Bleeding Edge a Different Conference', N'docx', N'During high level presentations attendees encounter many questions. For the third year, we are continuing with the breakfast Q&A session. It is very popular, and for two years now, we could not accommodate enough time for all questions and discussions! ', bulkcolumn FROM OPENROWSET (BULK 'C:\Users\Vahid\Desktop\Updates\fts_docs\WhyIsBleedingEdgeADifferentConference.docx', SINGLE_BLOB) AS doc; -- Fourth row INSERT INTO dbo.Documents (title, doctype, docexcerpt, doccontent) SELECT N'Additivity of Measures', N'docx', N'Additivity of measures is not exactly a data warehouse design problem. However, you have to realize which aggregate functions you will use in reports for which measure, and which aggregate functions you will use when aggregating over which dimension.', bulkcolumn FROM OPENROWSET (BULK 'C:\Users\Vahid\Desktop\Updates\fts_docs\AdditivityOfMeasures.docx', SINGLE_BLOB) AS doc; GO
fts_docs.zip
در ادامه میخواهیم قادر باشیم تا بر روی متادیتای نویسندهی این اسناد نیز جستجوی کامل FTS را انجام دهیم. به همین جهت SEARCH PROPERTY LIST آنرا نیز ایجاد خواهیم کرد:
-- Search property list CREATE SEARCH PROPERTY LIST WordSearchPropertyList; GO ALTER SEARCH PROPERTY LIST WordSearchPropertyList ADD 'Authors' WITH (PROPERTY_SET_GUID = 'F29F85E0-4FF9-1068-AB91-08002B27B3D9', PROPERTY_INT_ID = 4, PROPERTY_DESCRIPTION = 'System.Authors - authors of a given item.'); GO
-- Stopwords list CREATE FULLTEXT STOPLIST SQLStopList; GO -- Add a stopword ALTER FULLTEXT STOPLIST SQLStopList ADD 'SQL' LANGUAGE 'English'; GO
اکنون زمان ایجاد یک کاتالوگ FTS است:
-- Full-text catalog CREATE FULLTEXT CATALOG DocumentsFtCatalog; GO
و در آخر ایندکس FTS ایی را که پیشتر در مورد آن بحث کردیم، ایجاد خواهیم کرد:
-- Full-text index CREATE FULLTEXT INDEX ON dbo.Documents ( docexcerpt Language 1033, doccontent TYPE COLUMN doctype Language 1033 STATISTICAL_SEMANTICS ) KEY INDEX PK_Documents ON DocumentsFtCatalog WITH STOPLIST = SQLStopList, SEARCH PROPERTY LIST = WordSearchPropertyList, CHANGE_TRACKING AUTO; GO
در این تصویر محل یافتن اجزای مختلف Full text search را در management studio مشاهده میکنید.
یک نکتهی تکمیلی
برای زبان فارسی نیز یک سری stop words وجود دارند. لیست آنها را از اینجا میتوانید دریافت کنید:
stopwords.sql
متاسفانه زبان فارسی جزو زبانهای پشتیبانی شده توسط FTS در SQL Server نیست (نه به این معنا که نمیتوان با آن کار کرد؛ به این معنا که برای مثال دستورات صرفی زبان را ندارد) و به همین جهت از زبان انگلیسی در اینجا استفاده شدهاست.
Accord.NET #2
برای استفاده از Accord.NET میتوان به یکی از دو صورت زیر اقدام کرد :
- دریافت آخرین نسخهی متن باز و یا dllهای پروژهی Accord.NET از طریق گیت هاب
- نصب از طریق NuGet (با توجه به این که در چارچوب Accord.NET کتابخانههای متنوعی وجود دارند و در هر پروژه نیاز به نصب همگی آنها نیست، فضای نامهای مختلف در بستههای مختلف نیوگت قرار گرفتهاند و برای نصب هر کدام میتوانیم یکی از فرمانهای زیر را استفاده کنیم)
PM> Install-Package Accord.MachineLearning
PM> Install-Package Accord.Imaging
PM> Install-Package Accord.Neuro
نکته : روشهای یادگیری به دو دسته کلی با نظارت (Supervised learning) و بدون نظارت (Unsupervised learning) تقسیم بندی میشوند. در روش با نظارت، دادهها دارای برچسب یا label هستند و عملا نوع کلاسها مشخص هستند و اصطلاحا برای طبقه بندی (Classification) استفاده میشوند. در روش بدون نظارت، دادههایمان بدون برچسب هستند و فقط تعداد کلاس ها و نیز یک معیار تفکیک پذیری مشخص است و برای خوشه بندی (Clustering) استفاده میشوند.
عملکرد SVM یا ماشین بردار پشتیبان به صورت خلاصه به این صورت است که با در نظر گرفتن یک خط یا ابرصفحه جدا کننده فرضی، ماشین یا دسته بندی را ایجاد میکند که از نقاط ابتدایی کلاسهای مختلف که بردار پشتیبان یا SV نام دارند، بیشترین فاصله را دارند و در نهایت دادها را به دو کلاس مجزا تقسیم میکند.
در تصویر بالا مقداری خطا مشاهده میشود که با توجه با خطی بودن جداساز مجبور به پذیرش این خطا هستیم.
در نسخههای جدیدتر این الگوریتم یک Kernel ( از نوع خطی Linear ، چند جملهای Polynomial، گوسین Gaussian و یا ...) برای آن در نظر گرفته شد که عملا نگاشتی را بین خط (نه صرفا فقط خطی) را با آن ابرصفحه جداکننده برقرار کند. در نتیجه دسته بندی با خطای کمتری را خواهیم داشت. (اطلاعات بیشتر در + و همچنین مطالب دکتر سعید شیری درباره SVM در +)
یک مثال مفهومی : هدف اصلی در این مثال شبیه سازی تابع XNOR به Kernel SVM میباشد.
برای شروع کار از فضای نام MachineLearning استفاده میکنیم و بستهی نیوگت مربوطه را فرخوانی میکنیم. پس از اجرا، مشاهده میکنیم که فضای نامهای Accord.Math و Accord.Statistics نیز به پروژه اضافه میشود.
در ابتدا مقادیر ورودی و برچسبها را تعریف میکنیم
// ورودی double[][] inputs = { new double[] { 0, 0 }, // 0 xnor 0: 1 (label +1) new double[] { 0, 1 }, // 0 xnor 1: 0 (label -1) new double[] { 1, 0 }, // 1 xnor 0: 0 (label -1) new double[] { 1, 1 } // 1 xnor 1: 1 (label +1) }; // خروجی دسته بند ماشین بردار پشتیبان باید -1 یا +1 باشد int[] labels = { // 1, 0, 0, 1 1, -1, -1, 1 };
// ساخت کرنل IKernel kernel = createKernel(); // ساخت دسته بند به کمک کرنل انتخابی و تنظیم تعداد ویژگیها ورودیها به مقدار 2 KernelSupportVectorMachine machine = new KernelSupportVectorMachine(kernel, 2);
private static IKernel createKernel() { //var numPolyConstant = 1; //return new Linear(numPolyConstant); //var numDegree = 2; //var numPolyConstant = 1; //return new Polynomial(numDegree, numPolyConstant); //var numLaplacianSigma = 1000; //return new Laplacian(numLaplacianSigma); //var numSigAlpha = 7; //var numSigB = 6; //return new Sigmoid(numSigAlpha, numSigB); var numSigma = 0.1; return new Gaussian(numSigma); }
// معرفی دسته بندمان به الگوریتم یادگیری SMO SequentialMinimalOptimization teacher_smo = new SequentialMinimalOptimization(machine_svm, inputs, labels); // اجرای الگوریتم یادگیری double error = teacher_smo.Run(); Console.WriteLine(string.Format("error rate : {0}", error));
// بررسی یکی از ورودیها var sample = inputs[0]; int decision = System.Math.Sign(machine_svm.Compute(sample)); Console.WriteLine(string.Format("result for sample '0 xnor 0' is : {0}", decision));
دریافت کد
Vue CLI
- زیرا نیاز به build processهایی داریم که به ما امکان استفاده از ES6, SCSS و دیگر ویژگیهای عالی را خواهند داد.
- جهت ساخت و یکیسازی فایلهای تمپلیت
- بارگذاری نکردن تمامی فایلها به صورت یکجا در زمان Startup
- میتوانیم تسکهایی از قبیل Server-side rendering, code-splitting را انجام دهیم.
npm install -g vue-cli
vue init webpack-simple dntVue
npm install
<template> <div> <!-- Write your HTML with Vue in here --> </div> </template> <script> export default { // Write your Vue component logic here } </script> <style scoped> /* Write your styles for the component in here */ </style>
import { New } from "./components/New.vue"; export default { components: { New } }
npm install -g @vue/cli
3.0.0-beta.11
vue create my-project
Vue CLI v3.0.0-beta.11 ? Please pick a preset: (Use arrow keys) ❯ default (babel, eslint) Manually select features
{ "useConfigFiles": true, "router": true, "vuex": true, "cssPreprocessor": "sass", "plugins": { "@vue/cli-plugin-babel": {}, "@vue/cli-plugin-eslint": { "config": "airbnb", "lintOn": ["save", "commit"] } } }
<!-- MyCard.vue --> <template> <div class="card"> <h1>Card Title</h1> <p>Card content goes here. Make sure it's not Lorem.</p> </div> </template>
npm install -g @vue/cli-service-global
vue serve MyCard.vue
خروجی: