At Build 2019, Microsoft announced the release date for .NET Core 3.0 to be this coming September. This release includes the highly touted support for desktop platforms like WinForms and WPF. Today, there’s still a large developer base that’s building desktop applications using these .NET Windows desktop frameworks and by using .NET Core 3.0, you can now build desktop applications on the .NET Core platform.
On 16th November 2016, Nat Friedman and James Montemagno introduced Visual Studio for Mac, the newest member of the Visual Studio family at Connect(); 2016 event. I thought let's give it a try so I installed the same and went through the project templates available in it. This blog is kind of a getting started guide to install Visual Studio For Mac.
NET Core 3 Preview 4. منتشر شد
Rust – the Ultimate Programming Language?
What makes a good programming language? Syntax? Compiler? Tools and ecosystem? It is tempting to say “all of that” but in that case, why there are so many different programming languages? All these components are very important but they alone can’t make the language “good”. One of essential things is the purpose — like languages for rapid development, or development of distributed algorithms, or general purpose for high-level and low-level applications, or easy to learn, or safe to use and so on.
Use Tailwind CSS with RN for awesome styling!
Use the Google Distance Matrix API to calculate Travel time and Distance (+ Cost!)
Use the Directions Google API for real navigation!
Use Google places API for real navigations!
Use apple & google maps for iOS & Android
Use React Native Navigation to navigate between screens!
Use React Native Elements to elevate your app design!
Collations و حساسیت به بزرگی و کوچکی حروف
پردازش متون در بانکهای اطلاعاتی پیچیدهاست و عموما فراتر است از انتظارات سادهی اولیه، خصوصا اینکه بانکهای اطلاعاتی متفاوت، روشهای متفاوتی را هم در این زمینه بکار میگیرند. برای مثال بانکهای اطلاعاتی مانند SQLite و PostgreSQL به صورت پیشفرض به بزرگی و کوچکی حروف حساس هستند، اما بانکهایی مانند SQL Server و MySQL خیر. همچنین این حساسیت، بر روی کارآیی جستجو نیز بسیار تاثیر گذار است. برای مثال میتوان از متدهایی مانند string.ToLower برای انجام جستجوهای غیرحساس به بزرگی و کوچکی حروف استفاده کرد، اما بکارگیری آنها بلافاصله استفادهی از ایندکسها را غیرفعال میکنند و سبب انجام جستجوهایی بسیار کند خواهند شد.
برای مواجه شدن با یک چنین حالتهایی بدون افت کارآیی برنامه، مفهوم پایهای به نام collation در بانکهای اطلاعاتی ارائه شدهاست که مشخص میکند مقادیر رشتهای چگونه باید مرتب شده یا مقایسه شوند. برای مثال یک collation غیرحساس به بزرگی و کوچکی حروف، در حین مقایسهی رشتهها، به بزرگی و کوچکی حروف بکار گرفته شدهی در عبارت اهمیتی نمیدهد. همچنین باید دقت داشت که یک چنین مقایسهای بسته به فرهنگ بکار گرفته شده، میتوان متفاوت باشد؛ برای مثال در زبان ترکی، i و I حروف متفاوتی هستند و نباید در حین مقایسهی غیرحساس به بزرگی و کوچکی حروف، یکی در نظر گرفته شوند. به همین جهت تعداد قابل ملاحظهای case-insensitive collations از پیش تعریف شده، بسته به فرهنگهای مختلف وجود دارند؛ نمونهی دیگر آن فرهنگ آلمانی است که در آن عموما ä و ae را یکسان درنظر میگیرند. به علاوه collation بر روی نحوهی مرتب سازی حروف نیز تاثیر دارد؛ برای مثال در فرهنگ آلمانی، ä پس از a قرار میگیرد، اما در فرهنگ سوئدی در انتهای حروف الفباء واقع شدهاست.
تمام پردازشهای متنی در بانکهای اطلاعاتی (چه به صورت صریح و یا ضمنی) از collations استفاده میکنند و نام آنها از هر بانک اطلاعاتی به بانک اطلاعاتی دیگری متفاوت است. عموما میتوان این collations را در سطح کل بانک اطلاعاتی و یا در سطح یک ستون مشخص از آن و یا حتی در سطح یک کوئری مشخص، تعیین کرد.
روش تعیین collation در سطح بانک اطلاعاتی
در اغلب بانکهای اطلاعاتی، یک collation پیشفرض، در سطح کل آنها تعریف شدهاست و بر روی تمام پردازشهای متنی و تمام ستونهای جداول تاثیرگذار است. برای مثال حالت پیشفرض collation در SQL Server (اگر هیچ تنظیم پیشفرض دیگری در حین تعریف بانک اطلاعاتی وجود نداشته باشد) مقدار SQL_Latin1_General_CP1_CI_AS است. این مقدار یک collation غیرحساس به بزرگی و کوچکی حروف است. مقدار CI آن به معنای case-insensitive و AS آن مخفف accent-sensitive (حساس به لهجه) است.
از زمان EF-Core 5x، امکان کار با collations و تعیین آنها نیز میسر شدهاست. برای مثال برای تعیین یک چنین collation ای در سطح بانک اطلاعاتی میتوان به صورت زیر در متد OnModelCreating عمل کرد:
modelBuilder.UseCollation("SQL_Latin1_General_CP1_CS_AS");
روش تعیین collation در سطح جداول بانک اطلاعاتی
Collations را همچنین میتوان در سطح جداول نیز مشخص کرد تا بتوان در صورت نیاز، collation پیشفرض بانک اطلاعاتی را بازنویسی نمود. برای مثال شاید نیاز داشته باشید جداولی case-insensitive و تعدادی دیگر case-sensitive باشند.
در EF-Core 5x به بعد، روش انجام اینکار به صورت زیر است:
modelBuilder.Entity<Customer>().Property(c => c.Name) .UseCollation("SQL_Latin1_General_CP1_CI_AS");
روش تعیین پویای collation در سطح کوئریهای بانک اطلاعاتی
یک جدول میتواند collation پیشفرضی داشته باشد، اما در حین کوئری گرفتن، collation آنرا به صورت موقت و پویا تغییر داد. برای مثال بجای استفاده از متد ToLower که سبب میشود از ایندکسها استفاده نشود، میتوان از collation خاصی در حین کوئری گرفتن استفاده کرد:
var customers = context.Customers .Where(c => EF.Functions.Collate(c.Name, "SQL_Latin1_General_CP1_CS_AS") == "John").ToList();
تعیین collation غیرحساس به بزرگی و کوچکی حروف در SQLite، توسط EF-Core
با توجه به توضیحات فوق، متد زیر، collation ویژهی nocase را که در SQLite به معنای collation غیرحساس به بزرگی و کوچکی حروف است، به کل بانک اطلاعاتی و همچنین تمام ستونهای رشتهای آن به صورت خودکار اعمال میکند:
public static void SetCaseInsensitiveSearchesForSQLite(this ModelBuilder modelBuilder) { if (modelBuilder == null) { throw new ArgumentNullException(nameof(modelBuilder)); } modelBuilder.UseCollation("NOCASE"); foreach (var property in modelBuilder.Model.GetEntityTypes() .SelectMany(t => t.GetProperties()) .Where(p => p.ClrType == typeof(string))) { property.SetCollation("NOCASE"); } }
protected override void OnModelCreating(ModelBuilder modelBuilder) { if (modelBuilder == null) { throw new ArgumentNullException(nameof(modelBuilder)); } modelBuilder.SetCaseInsensitiveSearchesForSQLite(); }
ایجاد یک بانک اطلاعاتی با پشتیبانی از جداول بهینه سازی شده برای حافظه
برای ایجاد جداول بهینه سازی شده برای حافظه، ابتدا نیاز است تا تنظیمات خاصی را به بانک اطلاعاتی آن اعمال کنیم. برای اینکار میتوان یک بانک اطلاعاتی جدید را به همراه یک filestream filegroup ایجاد کرد که جهت جداول بهینه سازی شده برای حافظه، ضروری است؛ یا اینکه با تغییر یک بانک اطلاعاتی موجود و افزودن filegroup یاد شده نیز میتوان به این مقصود رسید.
در اینگونه جداول خاص، اطلاعات در حافظهی سیستم ذخیره میشوند و برخلاف جداول مبتنی بر دیسک سخت، صفحات اطلاعات وجود نداشته و نیازی نیست تا به کش بافر وارد شوند. برای مقاصد ذخیره سازی نهایی اطلاعات جداول بهینه سازی شده برای حافظه، موتور OLTP درون حافظهای آن، فایلهای خاصی را به نام checkpoint در یک filestream filegroup ایجاد میکند که از آنها جهت ردیابی اطلاعات استفاده خواهد کرد و نحوی ذخیره سازی اطلاعات در آنها از شیوهی با کارآیی بالایی به نام append only mode پیروی میکند.
با توجه به متفاوت بودن نحوهی ذخیره سازی نهایی اطلاعات اینگونه جداول و دسترسی به آنها از طریق استریمها، توصیه شدهاست که filestream filegroupهای تهیه شده را در یک SSD یا Solid State Drive قرار دهید.
پس از اینکه بانک اطلاعاتی خود را به روشهای معمول ایجاد کردید، به برگهی خواص آن در management studio مراجعه کنید. سپس صفحهی file groups را در آن انتخاب کرده و در پایین برگهی آن، در قسمت جدید memory optimized data، بر روی دکمهی Add کلیک کنید. سپس نام دلخواهی را وارد نمائید.
پس از ایجاد یک گروه فایل جدید، به صفحهی files خواص بانک اطلاعاتی مراجعه کرده و بر روی دکمهی Add کلیک کنید. سپس File type این ردیف اضافه شده را از نوع file stream data و file group آنرا همان گروه فایلی که پیشتر ایجاد کردیم، تنظیم کنید. در ادامه logical name دلخواهی را وارد کرده و در آخر بر روی دکمهی Ok کلیک کنید تا تنظیمات مورد نیاز جهت تعاریف جدول بهینه سازی شده برای حافظه به پایان برسد.
این مراحل را توسط دو دستور T-SQL ذیل نیز میتوان سریعتر انجام داد:
USE [master] GO ALTER DATABASE [testdb2] ADD FILEGROUP [InMemory_InMemory] CONTAINS MEMORY_OPTIMIZED_DATA GO ALTER DATABASE [testdb2] ADD FILE ( NAME = N'InMemory_InMemory', FILENAME = N'D:\SQL_Data\MSSQL11.MSSQLSERVER\MSSQL\DATA\InMemory_InMemory' ) TO FILEGROUP [InMemory_InMemory] GO
ساختار گروه فایل بهینه سازی شده برای حافظه
گروه فایل بهینه سازی شده برای حافظه، دارای چندین دربرگیرنده است که هر کدام چندین فایل را در خود جای خواهند داد:
- Root File که در برگیرندهی متادیتای اطلاعات است.
- Data File که شامل ردیفهای اطلاعات ثبت شده در جداول بهینه سازی شدهی برای حافظه هستند. این ردیفها همواره به انتهای data file اضافه میشوند و دسترسی به آنها ترتیبی است. کارآیی IO این روش نسبت به روش دسترسی اتفاقی به مراتب بالاتر است. حداکثر اندازه این فایل 128 مگابایت است و پس از آن یک فایل جدید ساخته میشود.
- Delta File شامل ردیفهایی است که حذف شدهاند. به ازای هر ردیف، حداقل اطلاعاتی از آن را در خود ذخیره خواهد کرد؛ شامل ID ردیف حذف شده و شماره تراکنش آن. همانطور که پیشتر نیز ذکر شد، این موتور جدید درون حافظهای، برای یافتن راه چارهای جهت به حداقل رسانی قفل گذاری بر روی اطلاعات، چندین نگارش از ردیفها را به همراه timestamp آنها در خود ذخیره میکند. به این ترتیب، هر به روز رسانی به همراه یک حذف و سپس ثبت جدید است. به این ترتیب دیگر بانک اطلاعاتی نیازی نخواهد داشت تا به دنبال رکورد موجود برگردد و سپس اطلاعات آنرا به روز نماید. این موتور جدید فقط اطلاعات به روز شده را در انتهای رکوردهای موجود با فرمت خود ثبت میکند.
ایجاد جداول بهینه سازی شده برای حافظه
پس از آماده سازی بانک اطلاعاتی خود و افزودن گروه فایل استریم جدیدی به آن برای ذخیره سازی اطلاعات جداول بهینه سازی شده برای حافظه، اکنون میتوانیم اینگونه جداول خاص را در کنار سایر جداول متداول موجود، تعریف و استفاده نمائیم:
-- It is not a Memory Optimized CREATE TABLE tblNormal ( [CustomerID] int NOT NULL PRIMARY KEY NONCLUSTERED, [Name] nvarchar(250) NOT NULL, CustomerSince DATETIME not NULL INDEX [ICustomerSince] NONCLUSTERED ) -- DURABILITY = SCHEMA_AND_DATA CREATE TABLE tblMemoryOptimized_Schema_And_Data ( [CustomerID] INT NOT NULL PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 1000000), [Name] NVARCHAR(250) NOT NULL, [CustomerSince] DATETIME NOT NULL INDEX [ICustomerSince] NONCLUSTERED ) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA) -- DURABILITY = SCHEMA_ONLY CREATE TABLE tblMemoryOptimized_Schema_Only ( [CustomerID] INT NOT NULL PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 1000000), [Name] NVARCHAR(250) NOT NULL, [CustomerSince] DATETIME NOT NULL INDEX [ICustomerSince] NONCLUSTERED ) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_ONLY)
همانطور که مشخص است، دو جدول بهینه سازی شده برای حافظه، همان سه ستون جدول معمولی مبتنی بر دیسک سخت را دارا هستند؛ اما با این تفاوتها:
- دارای ویژگی MEMORY_OPTIMIZED = ON میباشند. به این ترتیب اینگونه جداول نسبت به جداول متداول مبتنی به دیسک سخت متمایز خواهند شد.
- دارای ویژگی DURABILITY بوده و توسط مقدار SCHEMA_AND_DATA آن مشخص میکنیم که آیا قرار است اطلاعات و ساختار جدول، ذخیره شوند یا تنها قرار است ساختار جدول ذخیره گردد (حالت SCHEMA_ONLY).
- بر روی ستون Id آنها یک hash index ایجاد شدهاست که وجود آن ضروری است و در کل بیش از 8 ایندکس را نمیتوان تعریف کرد.
برخلاف ایندکسهای B-tree جداول مبتنی بر سخت دیسک، ایندکسهای جداول بهینه سازی شده برای حافظه، اطلاعات را تکرار نمیکنند. اینها صرفا اشارهگرهایی هستند به ردیفهای اصلی اطلاعات. به این معنا که این ایندکسها لاگ نشده و همچنین بر روی سخت دیسک ذخیره نمیشوند. کار بازسازی مجدد آنها در اولین بار بازیابی بانک اطلاعاتی و آغاز آن به صورت خودکار انجام میشود. به همین جهت مباحثی مانند index fragmentation و نگهداری ایندکسها دیگر در اینجا معنا پیدا نمیکنند.
دو نوع ایندکس را در اینجا میتوان تعریف کرد. اولین آنها hash index است و دومین آنها range index. هش ایندکسها برای حالاتی که در کوئریها از عملگر تساوی استفاده میشود بسیار مناسب هستند. برای عملگرهای مقایسهای از ایندکسهای بازهای استفاده میشود.
همچنین باید دقت داشت که پس از ایجاد ایندکسها، دیگر امکان تغییر آنها و یا تغییر ساختار جدول ایجاد شده نیست.
همچنین ایندکسهای تعریف شده در جداول بهینه سازی شده برای حافظه، تنها بر روی ستونهایی غیرنال پذیر از نوع BIN2 collation مانند int و datetime قابل تعریف هستند. برای مثال اگر سعی کنیم بر روی ستون Name ایندکسی را تعریف کنیم، به این خطا خواهیم رسید:
Indexes on character columns that do not use a *_BIN2 collation are not supported with indexes on memory optimized tables.
- نوعهای قابل تعریف ستونها نیز در اینجا به موارد ذیل محدود هستند و جمع طول آنها از 8060 نباید بیشتر شود:
bit, tinyint, smallint, int, bigint, money, smallmoney, float, real, datetime, smalldatetime, datetime2, date, time, numberic, decimal, char(n), varchar(n) ,nchar(n), nvarchar(n), sysname, binary(n), varbinary(n), and Uniqueidentifier
همچنین در management studio، گزینهی جدید new -> memory optimized table نیز اضافه شدهاست و انتخاب آن سبب میشود تا قالب T-SQL ایی برای تهیه این نوع جداول، به صورت خودکار تولید گردد.
البته این گزینه تنها برای بانکهای اطلاعاتی که دارای گروه فایل استریم مخصوص جداول بهینه سازی شده برای حافظه هستند، فعال میباشد.
ثبت اطلاعات در جداول معمولی و بهینه سازی شده برای حافظه و مقایسه کارآیی آنها
در مثال زیر، 100 هزار رکورد را در سه جدولی که پیشتر ایجاد کردیم، ثبت کرده و سپس مدت زمان اجرای هر کدام از مجموعه عملیات را بر حسب میلی ثانیه بررسی میکنیم:
set statistics time off SET STATISTICS IO Off set nocount on go ----------------------------- Print 'insert into tblNormal' DECLARE @start datetime = getdate() declare @insertCount int = 100000 declare @startId int = 1 declare @customerID int = @startId while @customerID < @startId + @insertCount begin insert into tblNormal values (@customerID, 'Test', '2013-01-01T00:00:00') set @customerID +=1 end Print DATEDIFF(ms,@start,getdate()); go ----------------------------- Print 'insert into tblMemoryOptimized_Schema_And_Data' DECLARE @start datetime = getdate() declare @insertCount int = 100000 declare @startId int = 1 declare @customerID int = @startId while @customerID < @startId + @insertCount begin insert into tblMemoryOptimized_Schema_And_Data values (@customerID, 'Test', '2013-01-01T00:00:00') set @customerID +=1 end Print DATEDIFF(ms,@start,getdate()); Go ----------------------------- Print 'insert into tblMemoryOptimized_Schema_Only' DECLARE @start datetime = getdate() declare @insertCount int = 100000 declare @startId int = 1 declare @customerID int = @startId while @customerID < @startId + @insertCount begin insert into tblMemoryOptimized_Schema_Only values (@customerID, 'Test', '2013-01-01T00:00:00') set @customerID +=1 end Print DATEDIFF(ms,@start,getdate()); Go
insert into tblNormal 36423 insert into tblMemoryOptimized_Schema_And_Data 30516 insert into tblMemoryOptimized_Schema_Only 3176
set nocount on print 'tblNormal' set statistics time on select count(CustomerID) from tblNormal set statistics time off go print 'tblMemoryOptimized_Schema_And_Data' set statistics time on select count(CustomerID) from tblMemoryOptimized_Schema_And_Data set statistics time off go print 'tblMemoryOptimized_Schema_Only' set statistics time on select count(CustomerID) from tblMemoryOptimized_Schema_Only set statistics time off go
tblNormal SQL Server Execution Times: CPU time = 46 ms, elapsed time = 52 ms. tblMemoryOptimized_Schema_And_Data SQL Server Execution Times: CPU time = 32 ms, elapsed time = 33 ms. tblMemoryOptimized_Schema_Only SQL Server Execution Times: CPU time = 31 ms, elapsed time = 30 ms.
برای مطالعه بیشتر
Getting started with SQL Server 2014 In-Memory OLTP
Introduction to SQL Server 2014 CTP1 Memory-Optimized Tables
Overcoming storage speed limitations with Memory-Optimized Tables for SQL Server
Memory-optimized Table – Day 1 Test
Memory-Optimized Tables – Insert Test
Memory Optimized Table – Insert Test …Again