StyleCop 4.3.1.3
به روز رسانی جدید StyleCop و یا همان ابزار استاندارد سازی و یکنواخت کردن ظاهر کدهای نوشته شده که پیشتر در تیمهای داخلی مایکروسافت مورد استفاده قرار میگرفت، از آدرس زیر قابل دریافت است.
همچنین نگارش جدید StyleCop for ReSharper را نیز میتوانید از آدرس زیر دریافت نمائید.
مایکروسافت سه ابزار امنیتی رایگان جدید را جهت توسعه دهندگان وب ارائه داده است که فعلا در مرحله آزمایش به سر میبرند و قرار است این مجموعه به صورت جرئی از مجموعه power tools ویژوال استودیو 2010 ارائه شوند. این سه ابزار به شرح زیر هستند:
الف) CAT.NET 2.0 CTP
CAT.NET کاملا از صفر بازنویسی شده و از موتور جدیدی کمک میگیرد. نگارش CTP فعلی آن فقط از طریق خط فرمان قابل اجرا است.
دریافت
ب) WACA 1.0 CTP
نام این ابزار مخفف Web Application Configuration Analyzer است که جهت بررسی تنظیمات برنامههای وب شما، همچنین IIS و SQL Server بکار میرود و نقایص امنیتی آنها را گوشزد خواهد کرد.
دریافت
ج) WPL 1.0 CTP
نام این ابزار مخفف Web Protection Library است. این ابزار شبیه به یک فایروال جهت برنامههای وب شما در مقابل حملات XSS و SQL injection عمل میکند و بدون نیاز به تغییر کد (با کمک یک HTTP Module)، محافظت قابل توجهی را ارائه خواهد داد.
دریافت
OPML های جدید
پیشنیازها
دو پروژه خالی ASP.NET Web forms و ASP.NET MVC را در VS.NET تحت ویندوز ایجاد نمائید. آنها را یکبار کامپایل کرده و اجرا کنید. سپس فایلهای آنها را به ubuntu منتقل کنید (پوشههای bin پروژهها فراموش نشوند؛ خصوصا نگارش MVC که به همراه یک سری کتابخانه جانبی است).
برای انتقال فایلها به لینوکس، اگر از VMWare workstation برای اجرا و آزمایش Ubuntu استفاده میکنید، کپی و paste مستقیم فایلها از ویندوز به درون ماشین مجازی لینوکس پشتیبانی میشود.
نصب وب سرور آزمایشی مونو یا XSP
اگر نیاز به یک وب سرور آزمایشی، چیزی شبیه به وب سرور توکار VS.NET داشتید، پروژه XSP جهت این نوع آزمایشات ایجاد شده است.
پس از نصب آن (که به همراه همان بسته PPA قسمت قبل، هم اکنون بر روی سیستم شما نصب است)، در ترمینال لینوکس، با استفاده از دستور cd به ریشه وب سایت خود وارد شوید، سپس دستور xsp4 را اجرا کنید تا وب سرور xsp4 مشغول هاست سایت شما شود (برای اجرا در مسیر /opt/bin/xsp4 نصب شده است).
اجرای برنامه ASP.NET Web forms 4 توسط XSP
بدون هیچ مشکل خاصی در همان ابتدای کار اجرا شد (البته باید دقت داشت که لینوکس به کوچکی و بزرگی حروف حساس است. یعنی حتما باید Default.aspx وارد شود و نه default.aspx):
اجرای برنامه ASP.NET MVC 4 توسط XSP
اجرا نشد! پیام میدهد که
"Missing method System.Web.Security.FormsAuthentication::get_IsEnabled() in assembly System.Web.dll
Compiler Error Message: CS1703: An assembly with the same identity `mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' has already been imported. Consider removing one of the references
git clone git://github.com/mono/xsp.git cd xsp ./autogen.sh --prefix=/opt make sudo make install
System.IO.FileNotFoundException: Could not load file or assembly XSP, Version=3.0.0.0
البته من سورس دریافت شده را در خود monodevelop کامپایل کردم (فایل sln آنرا در monodevelop باز کرده و پروژه را build کنید). در این حالت دو فایل Mono.WebServer.dll و Mono.WebServer.XSP.exe در پوشه xsp/src/Mono.WebServer.XSP/bin/Debug ظاهر میشوند.
یکی دیگر از دلایل ظاهر شدن خطای فوق، نیاز به نصب این دو فایل در GAC است که به نحو زیر قابل انجام میباشد:
cd xsp/src/Mono.WebServer.XSP/bin/Debug sudo gacutil -i Mono.WebServer.XSP.exe sudo gacutil -i Mono.WebServer.dll
cd myMvcAppPath sudo mono Mono.WebServer.XSP.exe
CS0234: The type or namespace name `Helpers' does not exist in the namespace `System.Web'. Are you missing an assembly reference?
<system.web> <compilation debug="true" targetFramework="4.0"> <assemblies> <add assembly="System.Web.Helpers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <add assembly="System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> </assemblies> </compilation>
Microsoft.Web.Infrastructure.dll
System.Net.Http.dll
System.Net.Http.Formatting.dll
System.Web.Http.dll
System.Web.Http.WebHost.dll
این فایلها توسط تیم Mono به صورت مستقل پیاده سازی شدهاند و نیازی نیست تا از ویندوز به لینوکس کپی شوند.
بعد از حذف این فایلهای اضافی، برنامه ASP.NET MVC نیز اجرا شد:
چند نکته تکمیلی
- نحوه تشخیص موجود بودن یک DLL خاص، در نگارش جاری Mono نصب شده:
$ gacutil -l Microsoft.Web.Infrastructure The following assemblies are installed into the GAC: Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 Number of items = 1
cd /opt/lib/mono/gac # assuming this is your main gac sudo find . */*/*.dll -exec gacutil -i '{}' \;
1) دریافت کتابخانههای لازم
نیاز به کتابخانههای Lucene.NET و همچنین Lucene.Net Contrib است که هر دو مورد را به سادگی توسط NuGet میتوانید دریافت و نصب کنید.
Highlighter استفاده شده، در کتابخانه Lucene.Net Contrib قرار دارد. به همین جهت این مورد را نیز باید جداگانه دریافت کرد.
2) تهیه منبع داده
در اینجا جهت سادگی کار فرض کنید که لیستی از مطالب را به فرمت زیر دراختیار داریم:
public class Post { public int Id { set; get; } public string Title { set; get; } public string Body { set; get; } }
3) تبدیل اطلاعات به فرمت Lucene.NET
همانطور که عنوان شد نیاز است هر رکورد از اطلاعات خود را به شیء Document نگاشت کنیم. نمونهای از اینکار را در متد ذیل مشاهده مینمائید:
static Document MapPostToDocument(Post post) { var postDocument = new Document(); postDocument.Add(new Field("Id", post.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); postDocument.Add(new Field("Title", post.Title, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS)); postDocument.Add(new Field("Body", post.Body, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS)); return postDocument; }
کار با ایجاد یک وهله از شیء Document شروع شده و سپس اطلاعات به صوت فیلدهایی به این سند اضافه میشوند.
توضیحات آرگومانهای مختلف سازنده کلاس Field:
- در ابتدا نام فیلد مورد نظر ذکر میگردد.
- سپس مقدار متناظر با آن فیلد، به صورت رشته باید معرفی شود.
- آرگومان سوم آن مشخص میکند که اصل اطلاعات نیز علاوه بر ایندکس شدن باید در فایلهای Lucene ذخیره شوند یا خیر. توسط Field.Store.YES مشخص میکنیم که بله؛ علاقمندیم تا اصل اطلاعات نیز از طریق Lucene قابل بازیابی باشند. این مورد جهت نمایش سریع نتایج جستجوها میتواند مفید باشد. اگر قرار نیست اطلاعاتی را از این فیلد خاص به کاربر نمایش دهید میتوانید از گزینه Field.Store.NO استفاده کنید. همچنین امکان فشرده سازی اطلاعات ذخیره شده با انتخاب گزینه Field.Store.COMPRESS نیز میسر است.
- توسط آرگومان چهارم آن تعیین خواهیم کرد که اطلاعات فیلد مورد نظر ایندکس شوند یا خیر. مقدار Field.Index.NOT_ANALYZED سبب عدم ایندکس شدن فیلد Id میشوند (چون قرار نیست روی id در قسمت جستجوی عمومی سایت، جستجویی صورت گیرد). به کمک مقدار Field.Index.ANALYZED، مقدار معرفی شده، ایندکس خواهد شد.
- پارامتر پنجم آنرا جهت سرعت عمل در نمایان سازی/برجسته کردن و highlighting عبارات جستجو شده در متنهای یافت شده معرفی کردهایم. الگوریتمهای متناظر با این روش در فایلهای Lucene.Net Contrib قرار دارند.
یک نکته
اگر اطلاعاتی را که قرار است ایندکس کنید از نوع HTML میباشند، بهتر است تمام تگهای آنرا پیش از افزودن به لوسین حذف کنید. به این ترتیب نتایج جستجوی دقیقتری را میتوان شاهد بود. برای این منظور میتوان از متد ذیل کمک گرفت:
public static string RemoveHtmlTags(string text) { return string.IsNullOrEmpty(text) ? string.Empty : Regex.Replace(text, @"<(.|\n)*?>", string.Empty); }
4) تهیه Full text index به کمک Lucene.NET
تا اینجا توانستیم اطلاعات خود را به فرمت اسناد لوسین تبدیل کنیم. اکنون ثبت و تبدیل آنها به فایلهای Full text search لوسین به سادگی زیر است:
static readonly Lucene.Net.Util.Version _version = Lucene.Net.Util.Version.LUCENE_29; public static void CreateIdx(IEnumerable<Post> dataList) { var directory = FSDirectory.Open(new DirectoryInfo(Environment.CurrentDirectory + "\\LuceneIndex")); var analyzer = new StandardAnalyzer(_version); using (var writer = new IndexWriter(directory, analyzer, create: true, mfl: IndexWriter.MaxFieldLength.UNLIMITED)) { foreach (var post in dataList) { writer.AddDocument(MapPostToDocument(post)); } writer.Optimize(); writer.Commit(); writer.Close(); directory.Close(); } }
ذکر version در اینجا ضروری است؛ از این جهت که اگر ایندکسی با فرمت مثلا LUCENE_29 تهیه شود ممکن است با نگارش بعدی این کتابخانه سازگار نباشد و در صورت ارتقاء، نتایج جستجوی انجام شده، کاملا بیربط نمایش داده شوند. با ذکر صریح نگارش، دیگر این اتفاق رخ نخواهد داد.
نکته
StandardAnalyzer توکار لوسین، امکان دریافت لیستی از واژههایی که نباید ایندکس شوند را نیز دارا است. اطلاعات بیشتر در اینجا.
5) به روز رسانی ایندکسها
به کمک سه متد ذیل میتوان اطلاعات ایندکسهای موجود را به روز یا حذف کرد:
public static void UpdateIndex(Post post) { var directory = FSDirectory.Open(new DirectoryInfo(Environment.CurrentDirectory + "\\LuceneIndex")); var analyzer = new StandardAnalyzer(_version); using (var indexWriter = new IndexWriter(directory, analyzer, create: false, mfl: IndexWriter.MaxFieldLength.UNLIMITED)) { var newDoc = MapPostToDocument(post); indexWriter.UpdateDocument(new Term("Id", post.Id.ToString()), newDoc); indexWriter.Commit(); indexWriter.Close(); directory.Close(); } } public static void DeleteIndex(Post post) { var directory = FSDirectory.Open(new DirectoryInfo(Environment.CurrentDirectory + "\\LuceneIndex")); var analyzer = new StandardAnalyzer(_version); using (var indexWriter = new IndexWriter(directory, analyzer, create: false, mfl: IndexWriter.MaxFieldLength.UNLIMITED)) { indexWriter.DeleteDocuments(new Term("Id", post.Id.ToString())); indexWriter.Commit(); indexWriter.Close(); directory.Close(); } } public static void AddIndex(Post post) { var directory = FSDirectory.Open(new DirectoryInfo(Environment.CurrentDirectory + "\\LuceneIndex")); var analyzer = new StandardAnalyzer(_version, getStopWords()); using (var indexWriter = new IndexWriter(directory, analyzer, create: false, mfl: IndexWriter.MaxFieldLength.UNLIMITED)) { var searchQuery = new TermQuery(new Term("Id", post.Id.ToString())); indexWriter.DeleteDocuments(searchQuery); var newDoc = MapPostToDocument(post); indexWriter.AddDocument(newDoc); indexWriter.Commit(); indexWriter.Close(); directory.Close(); } }
محل فراخوانی این متدها هم میتواند در کنار متدهای به روز رسانی اطلاعات اصلی در بانک اطلاعاتی برنامه باشند. اگر رکوردی اضافه یا حذف شده، ایندکس متناظر نیز باید به روز شود.
6) جستجو در اطلاعات ایندکس شده و نمایش آنها به همراه نمایان/برجسته سازی عبارات جستجو شده
قسمت نهایی کار با لوسین و اطلاعات ایندکسهای تهیه شده، کوئری گرفتن از آنها است. متدهای کامل مورد نیاز را در ذیل مشاهده میکنید:
public static void Query(string term) { var directory = FSDirectory.Open(new DirectoryInfo(Environment.CurrentDirectory + "\\LuceneIndex")); using (var searcher = new IndexSearcher(directory, readOnly: true)) { var analyzer = new StandardAnalyzer(_version); var parser = new MultiFieldQueryParser(_version, new[] { "Body", "Title" }, analyzer); var query = parseQuery(term, parser); var hits = searcher.Search(query, 10).ScoreDocs; if (hits.Length == 0) { term = searchByPartialWords(term); query = parseQuery(term, parser); hits = searcher.Search(query, 10).ScoreDocs; } FastVectorHighlighter fvHighlighter = new FastVectorHighlighter(true, true); foreach (var scoreDoc in hits) { var doc = searcher.Doc(scoreDoc.doc); string bestfragment = fvHighlighter.GetBestFragment( fvHighlighter.GetFieldQuery(query), searcher.GetIndexReader(), docId: scoreDoc.doc, fieldName: "Body", fragCharSize: 400); var id = doc.Get("Id"); var title = doc.Get("Title"); var score = scoreDoc.score; Console.WriteLine(bestfragment); } searcher.Close(); directory.Close(); } } private static Query parseQuery(string searchQuery, QueryParser parser) { Query query; try { query = parser.Parse(searchQuery.Trim()); } catch (ParseException) { query = parser.Parse(QueryParser.Escape(searchQuery.Trim())); } return query; } private static string searchByPartialWords(string bodyTerm) { bodyTerm = bodyTerm.Replace("*", "").Replace("?", ""); var terms = bodyTerm.Trim().Replace("-", " ").Split(' ') .Where(x => !string.IsNullOrEmpty(x)) .Select(x => x.Trim() + "*"); bodyTerm = string.Join(" ", terms); return bodyTerm; }
اکثر سایتها را که بررسی کنید، جستجوی بر روی یک فیلد را توضیح دادهاند. در اینجا نحوه جستجو بر روی چند فیلد را به کمک MultiFieldQueryParser ملاحظه میکنید.
نکتهی مهمی را هم که در اینجا باید به آن دقت داشت، حساس بودن لوسین به کوچکی و بزرگی نام فیلدهای معرفی شده است و در صورت عدم رعایت این مساله، جستجوی شما نتیجهای را دربر نخواهد داشت.
در ادامه برای parse اطلاعات، از متد کمکی parseQuery استفاده شده است. ممکن است به ParseException بخاطر یک سری حروف خاص بکارگرفته شده در عبارات مورد جستجو برسیم. در اینجا میتوان توسط متد QueryParser.Escape، اطلاعات دریافتی را اصلاح کرد.
سپس نحوه استفاده از کوئری تهیه شده و متد Search را ملاحظه میکنید. در اینجا بهتر است تعداد رکوردهای بازگشت داده شده را تعیین کرد (به کمک آرگومان دوم متد جستجو) تا بیجهت سرعت عملیات را پایین نیاورده و همچنین مصرف حافظه سیستم را نیز بالا نبریم.
ممکن است تعداد hits یا نتایج حاصل صفر باشد؛ بنابراین بد نیست خودمان دست به کار شده و به کمک متد searchByPartialWords، ورودی کاربر را بر اساس زبان جستجوی ویژه لوسین اندکی بهینه کنیم تا بتوان به نتایج بهتری دست یافت.
در آخر نحوه کار با ScoreDocs یافت شده را ملاحظه میکنید. اگر محتوای فیلد را در حین ایندکس سازی ذخیره کرده باشیم، به کمک متد doc.Get میتوان به اطلاعات کامل آن نیز دست یافت.
همچنین نکته دیگری را که در اینجا میتوان ملاحظه کرد استفاده از FastVectorHighlighter میباشد. به کمک این Highlighter ویژه میتوان نتایج جستجو را شبیه به نتایج نمایش داده شده توسط موتور جستجوی گوگل درآورد. برای مثال اگر شخصی ef code first را جستجو کرد، توسط متد GetBestFragment، بهترین جزئی که شامل بیشترین تعداد حروف جستجو شده است، یافت گردیده و همچنین به کمک تگهای B، ضخیم نمایش داده خواهند شد.
برای اینکار SQL Server از یک بافر 60 کیلوبایتی برای ذخیره سازی اطلاعات لاگهایی که قرار است به صورت غیرهمزمان با تراکنشها نوشته شوند، استفاده میکند. هر زمان که این 60KB پر شد، آنرا flush کرده و ثبت خواهد نمود. به این ترتیب به دو مزیت خواهیم رسید:
- پردازش تراکنشها بدون منتظر شدن جهت commit نهایی در دیسک سخت ادامه خواهند یافت. صبر کمتر به معنای امکان پردازش تراکنشهای بیشتری در یک سیستم پر ترافیک است.
- با توجه به بافری که از آن صحبت شد، اینبار اعمال Write به صورت یک سری batch اعمال میشوند که کارآیی و سرعت بیشتری نسبت به حالت تکی دارند.
اندکی تاریخچه
ایده یک چنین عملی 28 سال قبل توسط Hal Berenson ارائه شدهاست! اوراکل آنرا در سال 2006 تحت عنوان Asynchronous Commit پیاده سازی کرد و مایکروسافت در سال 2014 آنرا ارائه دادهاست.
فعال سازی ماندگاری غیرهمزمان در SQL Server
فعال سازی این قابلیت در سطح بانک اطلاعاتی، در سطح یک تراکنش مشخص و یا در سطح رویههای ذخیره شده کامپایل شده مخصوص OLTP درون حافظهای، میسر است.
برای فعال سازی ماندگاری با تاخیر در سطح یک دیتابیس، خواهیم داشت:
ALTER DATABASE dbname SET DELAYED_DURABILITY = DISABLED | ALLOWED | FORCED;
در اینجا اگر ALLOWED را انتخاب کنید، به این معنا است که لاگ کلیه تراکنشهای مرتبط با این بانک اطلاعاتی به صورت غیرهمزمان نوشته میشوند. حالت FORCED نیز دقیقا به همین معنا است با این تفاوت که اگر حالت ALLOWED انتخاب شود، تراکنشهای ماندگار (آنهایی که به صورت دستی DELAYED_DURABILITY را غیرفعال کردهاند)، سبب flush کلیه تراکنشهایی با ماندگاری به تاخیر افتاده خواهند شد و سپس اجرا میشوند. در حالت Forced تنظیم دسترسی DELAYED_DURABILITY = OFF در سطح تراکنشها تاثیری نخواهد داشت؛ اما در حالت ALLOWED این مساله به صورت دستی در سطح یک تراکنش قابل لغو است.
البته باید توجه داشت، صرفنظر از این تنظیمات، یک سری از تراکنشها همیشه ماندگار هستند و بدون تاخیر؛ مانند تراکنشهای سیستمی، تراکنشهای بین دو یا چند بانک اطلاعاتی و کلیه تراکنشهایی که با FileTable، Change Data Capture و Change Tracking سر و کار دارند.
در سطح تراکنشهای میتوان نوشت:
COMMIT TRANSACTION WITH (DELAYED_DURABILITY = ON);
BEGIN ATOMIC WITH (DELAYED_DURABILITY = ON, ...)
سؤال: آیا فعال سازی DELAYED_DURABILITY بر روی مباحث locking و isolation levels تاثیر دارند؟
پاسخ: خیر. کلیه تنظیمات قفل گذاریها همانند قبل و بر اساس isolation levels تعیین شده، رخ خواهند داد. تنها تفاوت در اینجا است که با فعال سازی DELAYED_DURABILITY، کار commit بدون صبر کردن برای پایان نوشته شدن اطلاعات در لاگ سیستم صورت میگیرد. به این ترتیب قفلهای انجام شده زودتر آزاد خواهند شد.
سؤال: میزان از دست دادن اطلاعات احتمالی در این روش چقدر است؟
در صورتیکه سرور کرش کند یا ریاستارت شود، حداکثر به اندازهی 60KB اطلاعات را از دست خواهید داد (اندازهی بافری که برای اینکار درنظر گرفته شدهاست). البته عنوان شدهاست که اگر ریاستارت یا خاموشی سرور، از پیش تعیین شده باشد، ابتدا کلیه لاگهای flush نشده، ذخیره شده و سپس ادامهی کار صورت خواهد گرفت؛ ولی زیاد به آن اطمینان نکنید. اما همواره با فراخوانی sys.sp_flush_log، میتوان به صورت دستی بافر لاگهای سیستم را flush کرد.
یک آزمایش
در ادامه قصد داریم یک جدول جدید را در بانک اطلاعاتی آزمایشی testdb2 ایجاد کنیم. سپس یکبار تنظیم DELAYED_DURABILITY = FORCED را انجام داده و 10 هزار رکورد را ثبت میکنیم و بار دیگر DELAYED_DURABILITY = DISABLED را تنظیم کرده و همین عملیات را تکرار خواهیم کرد:
CREATE TABLE tblData( ID INT IDENTITY(1, 1), Data1 VARCHAR(50), Data2 INT ); CREATE CLUSTERED INDEX PK_tblData ON tblData(ID); CREATE NONCLUSTERED INDEX IX_tblData_Data2 ON tblData(Data2); ------------------------- alter database testdb2 SET DELAYED_DURABILITY = FORCED; ------------------------- SET NOCOUNT ON Print 'DELAYED_DURABILITY = FORCED' DECLARE @counter AS INT = 0 DECLARE @start datetime = getdate() WHILE (@counter < 10000) BEGIN INSERT INTO tblData (Data1, Data2) VALUES('My Data', @counter) SET @counter += 1 END Print DATEDIFF(ms,@start,getdate()); GO ------------------------- alter database testdb2 SET DELAYED_DURABILITY = DISABLED; truncate table tblData; ------------------------- SET NOCOUNT ON Print 'DELAYED_DURABILITY = DISABLED' DECLARE @counter AS INT = 0 DECLARE @start datetime = getdate() WHILE (@counter < 10000) BEGIN INSERT INTO tblData (Data1, Data2) VALUES('My Data', @counter) SET @counter += 1 END Print DATEDIFF(ms,@start,getdate()); GO -----------------------
DELAYED_DURABILITY = FORCED 666 DELAYED_DURABILITY = DISABLED 2883
برای مطالعه بیشتر
Control Transaction Durability
SQL Server 2014 Delayed Durability/Lazy Commit
Delayed Durability in SQL Server 2014 – Part 1
Is In-Memory OLTP Always a silver bullet for achieving better transactional speed
Delayed Durability in SQL Server 2014
در حال حاضر توصیهی مایکروسافت ، عدم استفادهی از System.Drawing.Common و جایگزینی آن با یکی از کتابخانههای زیر است:
- SkiaSharp
- Microsoft.Maui.Graphics
البته پیشتر در این لیست توصیه شده، کتابخانهی SixLabors.ImageSharp.Drawing هم وجود داشت که به علت تغییر مجوز آن، به یک مجوز نیمه تجاری، نیمه سورس باز، از لیست فوق حذف شدهاست.
مشکل فارسی نویسی با SkiaSharp
اگر سعی کنیم با استفاده از مثالهای متداول SkiaSharp، یک متن فارسی را نمایش دهیم، به خروجی زیر خواهیم رسید:
// crate a surface var info = new SKImageInfo(256, 256); using var surface = SKSurface.Create(info); // the the canvas and properties var canvas = surface.Canvas; // make sure the canvas is blank canvas.Clear(SKColors.White); // draw some text using var typeface = SKTypeface.FromFamilyName("Tahoma"); using var paint = new SKPaint { Color = SKColors.Black, IsAntialias = true, Style = SKPaintStyle.Fill, TextAlign = SKTextAlign.Center, TextSize = 24, Typeface = typeface, }; var coord = new SKPoint(info.Width / 2, (info.Height + paint.TextSize) / 2); canvas.DrawText("آزمایش", coord, paint); // save the file using var image = surface.Snapshot(); using var data = image.Encode(SKEncodedImageFormat.Png, 100); using var stream = File.OpenWrite("farsi-text-1.png"); data.SaveTo(stream);
<ItemGroup> <PackageReference Include="SkiaSharp" Version="2.88.3" /> </ItemGroup>
همانطور که مشاهده میکنید، حروف فارسی در آن از هم جدا هستند و همچنین از چپ به راست نمایش داده شدهاست.
رفع مشکل فارسی نویسی با SkiaSharp
برای رفع مشکل فوق، نیاز است از افزونهی «حرف باز» این کتابخانه استفاده کرد که روش نصب آن به صورت زیر است:
<ItemGroup> <PackageReference Include="SkiaSharp" Version="2.88.3" /> <PackageReference Include="SkiaSharp.HarfBuzz" Version="2.88.3" /> </ItemGroup>
// crate a surface var info = new SKImageInfo(256, 256); using var surface = SKSurface.Create(info); // the the canvas and properties var canvas = surface.Canvas; // make sure the canvas is blank canvas.Clear(SKColors.White); // draw some text using var typeface = SKTypeface.FromFamilyName("Tahoma"); using var shaper = new SKShaper(typeface); using var paint = new SKPaint { Color = SKColors.Black, IsAntialias = true, Style = SKPaintStyle.Fill, TextAlign = SKTextAlign.Center, TextSize = 24, Typeface = typeface, }; var coord = new SKPoint(info.Width / 2, (info.Height + paint.TextSize) / 2); canvas.DrawShapedText(shaper, "آزمایش", coord, paint); // save the file using var image = surface.Snapshot(); using var data = image.Encode(SKEncodedImageFormat.Png, 100); using var stream = File.OpenWrite("farsi-text-2.png"); data.SaveTo(stream);
پروژه MsQuic ماکروسافت سورس باز شد
پروتکلهای قبل از HTTP/3 از پروتکلTCP برای انتقال اطلاعات و از لایه امنیتی TLS "بر روی آن" برای تامین امنیت استفاده میکردند
ولی HTTP/3 از پروتکل QUIC (مخفف Quick UDP Internet Connections) برای انتقال اطلاعات و از TLS 1.3 "به صورت تو کار" استفاده میکند.
پروتکل QUIC یک Transport Layer Network Protocol (پروتکل انتقال اطلاعات در شبکه - مثلا مشابه TCP یا UDP است) که در ابتدا توسط گوگل توسعه داده شده و بعدها توسط IETF (کارگروه مهندسی اینترنت) به عنوان یک استاندارد به ثبت رسیده.
این پروتکل نسبت به TCP سریعتر و قابل اطمینانتر است، latency (تاخیر) کمتری دارد و ایمنتر است.
حالا ماکروسافت پیاده سازی خود از این پروتکل رو به نام MsQuic به صورت متن باز منتشر کرده.
از طرفی قراره توی دات نت 5 به صورت آزمایشی از HTTP/3 پشتیبانی بشه و داخل Kestrel و HttpClient ازش بهره برده بشه. تیم دات نت برای پیاده سازی این این قابلیت از MsQuic استفاده میکنه