مطالب
توسعه برنامه‌های Cross Platform با Xamarin Forms & Bit Framework - قسمت یازدهم
در این قسمت قصد بررسی کامپوننت‌های فوق العاده‌ی Syncfusion را داریم. احتمالا Syncfusion را با کتاب‌های Succinctly Series آن می شناسید. این شرکت برای Xamarin Forms نزدیک به 130‌ کامپوننت، شامل موارد کار با دیتا، اعم از فرم‌های Data Entry ،Data Grid و ListView را نوشته که در کنار کنترل‌های کار با فایل‌های Office-PDF و همچنین گزارشات و چارت‌ها و سایر کنترل‌های آن، نیاز هر برنامه‌ای را برآورده می‌کند. یکی از چند ده کتاب Xamarin Forms نیز توسط این شرکت نوشته شده‌است.
ما ضمن استفاده کامپوننت List View آن، هم کار با List View را یاد می‌گیریم و هم کار با Syncfusion را. در نظر داشته باشید که خود Xamarin Forms نیز List View دارد و در نسخه‌ی 4 آن که هم اکنون در مرحله‌ی Preview است، کنترل جدیدی به نام CollectionView نیز ارائه شده که امکانات خیلی خوبی دارد.
توجه: مطالب آموزش زیر، از این لینک آورده شده‌است.

برای شروع، ابتدا Nuget Package مربوطه را در پروژه‌ی XamApp نصب کنید. نیازی به نصب کردن آن در XamApp.Android و XamApp.iOS و XamApp.UWP نیست، ولی باز و بسته کردن ویژوال استودیو بعد از نصب و راست کلیک کردن بر روی Solution و زدن Restore nuget packages ایده‌ی خوبی است!

در پروژه‌ی iOS، در فایل AppDelegate.cs، بعد از Forms.Init، کد زیر را کپی کنید:

SfListViewRenderer.Init();

همین کد را در MainPage.xaml.cs در پروژه UWP، قبل از LoadApplication قرار دهید. نیازی به انجام کاری در Android نیست.

سپس Product Key این محصول را به دست آورده و در پروژه XamApp، فولدر Views در فایل SyncfusionLicense قرار دهید.

حال برای نمایش لیستی از محصولات، ابتدا کلاس Product را ایجاد می‌کنیم. چه در زمانیکه یک Rest api را در سمت سرور فراخوانی می‌کنیم و چه زمانیکه با دیتابیس بر روی گوشی یعنی Sqlite کار می‌کنیم، در نهایت لیستی از یک کلاس را داریم (در اینجا Product).

    public class Product : Bindable
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public bool IsActive { get; set; }
        public decimal Price { get; set; }
    }

در یک View Model جدید با نام ProductsViewModel، در OnNavigatedToAsync، دیتا را از سرور یا دیتابیس، بر روی گوشی دریافت می‌کنیم؛ اما در این مثال، برای راحتی بیشتر یک List را New می‌کنیم:

    public class ProductsViewModel : BitViewModelBase
    {
        public List<Product> Products { get; set; }

        public async override Task OnNavigatedToAsync(INavigationParameters parameters)
        {
            Products = new List<Product> // getting products from server or sqlite database
            {
                new Product { Id = 1, IsActive = true, Name = "Product1" , Price = 12.2m /* m => decimal */ },
                new Product { Id = 2, IsActive = false, Name = "Product2" , Price = 14 },
                new Product { Id = 3, IsActive = true, Name = "Product3" , Price = 11 },
            };
            await base.OnNavigatedToAsync(parameters);
        }
    }

حال نوبت به دادن یک Template می‌رسد. مثلا فرض کنید می‌خواهیم نام را درون یک Label نمایش دهیم و بر اساس فعال یا غیر فعال بودن Product، یک Checkbox را تغییر داده، تیک بزنیم یا نزنیم و در نهایت نمایش قیمت را در یک Label دیگر خواهیم داشت.

    <sfListView:SfListView ItemsSource="{Binding Products}">
        <sfListView:SfListView.ItemTemplate>
            <DataTemplate>
                <FlexLayout x:DataType="model:Product" Direction="Row">
                    <Label
                        FlexLayout.Basis="50%"
                        Text="{Binding Name}"
                        VerticalTextAlignment="Center" />
                    <bitControls:BitCheckbox InputTransparent="True" FlexLayout.Basis="25%" IsChecked="{Binding IsActive}" />
                    <Label
                        FlexLayout.Basis="25%"
                        Text="{Binding Price}"
                        VerticalTextAlignment="Center" />
                </FlexLayout>
            </DataTemplate>
        </sfListView:SfListView.ItemTemplate>
    </sfListView:SfListView>

همانطور که می‌بینید، در DataTemplate از Flex Layout استفاده شده است. Flex Layout در کنار Grid, Stack, Relative, Absolute و سایر Layout‌‌های Xamarin Forms در پروژه قابلیت استفاده دارد و مزیت‌های خاص خود را دارد.

این Data Template توسط List View، حداکثر سه بار ساخته می‌شود؛ چون View Model در لیست مثال خود، سه Product دارد. خود List View تکنیک‌های Virtualization و Cell Reuse را بدون نیاز به هیچ کد اضافه‌ای هندل می‌کند و Performance خوبی دارد. در View مربوطه یعنی ProductsView.xaml، هر Binding ای (مثل Binding Products) به View Model اشاره می‌کند، اما درون Data Template، هر Binding به Product ای اشاره می‌کند که آن ردیف List View، دارد نمایش‌اش می‌دهد. برای همین x:DataType را روی Flex Layout درون Data Template به Product وصل کرده‌ایم. در این صورت اگر بنویسیم Binding N_ame، به ما خطا داده می‌شود که کلاس Product هیچ Property با نام N_ame ندارد که خطای درستی است.

روی BitCheckbox مقدار InputTransparent را برابر با True داده‌ایم که باعث می‌شود کلیک روی Checkbox عملا در نظر گرفته نشود. این منطقی است، زیرا عوض کردن مقدار Checkbox در این مثال ما ذخیره نمی‌شود و کاربرد نمایشی دارد و فقط باعث گیج شدن کاربر می‌شود.

کنترل BitCheckbox از مجموعه کنترل‌های Bit است که اخیرا با BitDatePicker آن آشنا شده‌اید. برای آشنایی با نحوه افزودن این کنترل‌ها به یک پروژه، به مستندات Bit Framework مراجعه کنید. خود Syncfusion نیز Checkbox دارد.

حال فرض کنید که قرار است دکمه‌ای برای هر ردیف List View داشته باشیم که با زدن روی آن، اطلاعات Product به سرور ارسال شود و جزئیات بیشتری دریافت و در قالب یک Alert نمایش داده شود.  برای این کار، ابتدا به Data Template که Flex Layout است، یک دکمه اضافه می‌کنیم. سپس Command آن دکمه را به View Model بایند می‌کنیم. در آن Command البته احتیاج داریم بدانیم درخواست نمایش جزئیات بیشتر، برای کدام Product داده شده. این مهم با Command Parameter شدنی است.

برای پیاده سازی این مثال، در سمت View Model داریم:

 
public BitDelegateCommand<Product> ShowProductDetailsCommand { get; set; }
public IUserDialogs UserDialogs { get; set; } async Task ShowProductDetails(Product product) { string productDetail = $"Product: {product.Name}'s more info: ..."; // get more info from server. await UserDialogs.AlertAsync(productDetail, "Product Detail"); }

کامند ShowProductDetailCommand یک پارامتر را از جنس Product می‌گیرد و آن Product ای است که روی دکمه آن کلیک شده‌است. با Clone کردن آخرین نسخه XamApp و درخواست نمایش صفحه‌ی Products در App.xaml.cs به صورت زیر و اجرای برنامه، می‌توانید درک بهتری از عملکرد آن داشته باشید:

await NavigationService.NavigateAsync("/Nav/Products", animated: false);

سپس در View مربوطه داریم:

 
...
<Button Command="{Binding ShowProductDetailsCommand}" CommandParameter="{Binding .}" Text="Detail..." /> </FlexLayout> </DataTemplate>

CommandParameter اگر برابر با Binding Id می‌بود، به Command در سمت View Model، بجای کل Product، فقط Id آن ارسال می‌شد. ولی Show Product Detail Command منتظر یک Product کامل است، نه فقط Id آن. با نوشتن 

CommandParameter="{Binding .}"

کل Product با کلیک روی دکمه به Command ارسال می‌شود.

اکنون اگر پروژه را Build کنید، خطایی را از x:DataType خواهید گرفت که منطقی است. اگر Binding Name و Binding Price دو Property با نام‌های Name و Price را از کلاس Product جستجو می‌کنند، پس قاعدتا ShowProductDetailCommand نیز در همان کلاس مدل، یعنی Product جستجو می‌شود! ولی می‌دانیم که این Command در View Model ما یعنی ProductsViewModel است. برای حل این مشکل، به جای Binding از bit:ViewModelBinding استفاده می‌کنیم:

Command="{bit:ViewModelBinding ShowProductDetailsCommand}"

در این صورت، بجای جستجو کردن ShowProductDetailCommand در کلاس Product، این را در ProductsViewModel جستجو می‌کند که منجر به خروجی درست می‌شود.

این List View دارای امکاناتی چون Infinite loading، Pull to refresh و Grouping-Sorting-Filtering و ... است که می‌توانید از روی مستندات خوب Syncfusion، آنها را راه اندازی کنید و اگر به مشکلی برخوردید نیز اینجا بپرسید. همچنین نگاهی به لیست 129 کنترل دیگر بیاندازید و ببینید که در برنامه‌های خود از کدام یک از آنها می‌توانید استفاده کنید.

مطالب
مشکل ی و ک فارسی و عربی در یک دیتابیس اس کیوال سرور

دیروز به من اطلاع دادند که در یکی از برنامه‌ها دو تا گروه "تاسیسات مکانیکی" پیدا شده!!
تاسیسات مکانیکی
تاسیسات مکانیکی

استاندارد این شرکت، استفاده از kbdfa.dll مخصوص و نسبتا قدیمی است. بنابراین استاندارد مورد استفاده همان ی و ک عربی است. (کاری ندارم خوب است یا بد، یا باید اینطور باشد یا نه، بحث این است که فعلا اینطور است و قرار نیست چیزی عوض بشود!)
در مثال فوق، ی و ک عبارت دوم فارسی است. یعنی نصب kbdfa.dll روی ویندوز تازه نصب شده، فراموش شده بوده.

راه حل‌ها:
الف) قبل از ثبت، یکسان سازی صورت گیرد. یعنی اجرای متدی شبیه به متد زیر بر روی هر ورودی متنی فارسی:

public string SafeFarsiStr(string input)
{
return input.Replace("ی", "ی").Replace("ک", "ک");
}

ب) خوب، الان که این یکسان سازی صورت نگرفته چه باید کرد؟
اسکریپتی را تهیه کرده‌ام (مخصوص SQL Server 2005 به بعد) به صورت زیر که این تبدیل را برای شما انجام می‌دهد.
به صورت خودکار تمامی فیلدهای متنی کلیه جداول دیتابیس جاری شما را یافته و ی و ک آن‌ها را یکسان می‌کند. البته همانطور که عرض شد، مطابق استاندارد این شرکت و استفاده از فایل kbdfa.dll قدیمی مورد استفاده، تمام ی و ک های فارسی به عربی تبدیل می‌شوند.

--اسکریپتی برای یک دست سازی ی و ک در تمامی رکوردهای تمامی جداول دیتابیس جاری
-- اسکریپت زیر ی و ک فارسی را به عربی تبدیل می‌کند
-- در صورت نیاز به حالت عکس ، جای مقادیر عددی یونیکد را تعویض نمائید

USE TestDb;

DECLARE @Table NVARCHAR(MAX),
@Col NVARCHAR(MAX)

DECLARE Table_Cursor CURSOR
FOR
--پیدا کردن تمام فیلدهای متنی تمام جداول دیتابیس جاری
SELECT a.name, --table
b.name --col
FROM sysobjects a,
syscolumns b
WHERE a.id = b.id
AND a.xtype = 'u' --User table
AND (
b.xtype = 99 --ntext
OR b.xtype = 35 -- text
OR b.xtype = 231 --nvarchar
OR b.xtype = 167 --varchar
OR b.xtype = 175 --char
OR b.xtype = 239 --nchar
)

OPEN Table_Cursor FETCH NEXT FROM Table_Cursor INTO @Table,@Col
WHILE (@@FETCH_STATUS = 0)
BEGIN
EXEC (
'update [' + @Table + '] set [' + @Col +
']= REPLACE(REPLACE(CAST([' + @Col +
'] as nvarchar(max)) , NCHAR(1740), NCHAR(1610)),NCHAR(1705),NCHAR(1603)) '
)

FETCH NEXT FROM Table_Cursor INTO @Table,@Col
END CLOSE Table_Cursor DEALLOCATE Table_Cursor

توضیحات و نکاتی در مورد اسکریپت فوق:
الف) برای آشنایی با انواع XType Datatype مورد استفاده در کوئری فوق به این آدرس مراجعه نمائید.
ب) همانطور که در مطالب قبلی این وبلاگ نیز ذکر شد، امکان استفاده از تابع replace بر روی فیلدهای text و ntext وجود ندارد. هیچ اشکالی ندارد! تمام آن‌ها به nvarchar از نوع max دار cast شده و این مشکل به این صورت حل می‌شود.
ج) اس کیوال سرور اجازه تعریف یک جدول یا فیلد را به صورت متغیر بکار رفته در یک کوئری T-SQL نمی‌دهد. برای حل این موضوع باید عبارت SQL مورد نظر را به صورت یک رشته درآورد و سپس exec کرد.
د) مجبور شدم از معاد‌ل‌های عددی برای دقت بیشتر کار استفاده کنم



(در کل از تابع UNICODE اس کیوال سرور برای بدست آوردن این اعداد می‌توان استفاده کرد)

تذکر: این اسکریپت بر روی یک دیتابیس کاری تست شده و نتیجه رضایت بخش بوده؛ اما اگر شما روزی خواستید از آن استفاده کنید، حتما full backup را قبل از اجرای آن فراموش نکنید و پس از اجرا، تابع SafeFarsiStr فوق را برای ادامه کار حتما لحاظ نمائید یا از یک kbdfa.dll هماهنگ استفاده کنید.


مطالب
رشته ها و پردازش متن در دات نت به زبان ساده
رشته، مجموعه‌ای از کاراکترهاست که پشت سرهم، در مکانی از حافظه قرار گرفته‌اند. هر کاراکتر حاوی یک شماره سریال در جدول یونیکد هست. به طور پیش فرض دات نت برای هر کاراکتر (نوع داده char) شانزده بیت در نظر گرفته است که برای 65536 کاراکتر کافی است.
برای نگهداری از رشته‌ها و انجام عملیات بر روی آنها در دات نت از نوع system.string استفاده می‌کنیم:
string greeting = "Hello, C#";

که در این حالت مجموعه‌ای از کاراکترها را ایجاد خواهد کرد:

اتفاقاتی که در داخل کلاس string رخ می‌دهد بسیار ساده است و ما را از تعریف []char بی‌نیاز می‌کند تا مجبور نشویم خانه‌های  آرایه را به ترتیب پر کنیم. از معایب استفاده از آرایه char میتوان موارد زیر را برشمارد:
  1. خانه‌های آن یک ضرب پر نمیشوند بلکه به ترتیب، خانه به خانه پر می‌شوند.
  2. قبل از انتساب متن باید باید از طول متن مطمئن شویم تا بتوانیم تعداد خانه‌ها را بر اساس آن ایجاد کنیم.
  3. همه عملیات آرایه‌ها از پر کردن ابتدای کار گرفته تا هر عملی، نیاز است به صورت دستی صورت بگیرد و تعداد خطوط کد برای هر کاری هم بالا می‌رود.
البته استفاده از string هم راه حل نهایی برای کار با متون نیست. در انتهای این مطلب مورد دیگری را نیز بررسی خواهیم کرد. از ویژگی دیگر رشته‌ها این است که آن‌ها شباهت زیادی به آرایه‌ای از کاراکتر‌ها دارند؛ ولی اصلا شبیه آن‌ها نیستند و نمی‌توانید به صورت یک آرایه آن‌ها را مقداردهی کنید. البته کلاس string امکاناتی را با استفاده از indexer [] مهیا کرده است که میتوانید بر اساس اندیس‌ها به کاراکترها به صورت جداگانه دسترسی داشته باشید ولی نمی‌توانید آن‌ها را مقدار دهی کنید. این اندیس‌ها از 0 تا طول آن length-1 ادامه دارند.
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"

مقداردهی رشته‌ها و پایدار (تغییر ناپذیر) بودن آنها Immutable
رشته‌ها ساختاری پایدار هستند؛ به این معنی که به صورت reference مقداردهی می‌شوند. موقعی که شما مقداری را به یک رشته انتساب می‌دهید، مقدار متغیر در  String pool یا لینک در Heap ذخیره می‌شوند و اگر همین متغیر را به یک متغیر دیگر انتساب دهیم، متغیر جدید مقدار آن را دیگر در حافظه پویا (داینامیک) Heap به عنوان مقدار جدید ذخیره نخواهد کرد؛ بلکه تنها یک pointer خواهد بود که به آدرس حافظه متغیر اولی اشاره می‌کند. به مثال زیر دقت کنید. متغیر source مقدار some source را ذخیره می‌کند و بعد همین متغیر، به متغیر assigned انتساب داده میشود؛ ولی مقداری جابجا نمی‌شود. بلکه متغیر assign به آدرسی در حافظه اشاره می‌کند که متغیر source اشاره می‌کند. هرگاه که در یکی از متغیرها، تغییری رخ دهد، همان متغیری که تغییر کرده است، به آدرس جدید با محتوای تغییر داده شده اشاره می‌کند.
string source = "Some source";
string assigned = source;

این ویژگی نوع reference فقط برای ساختارهای Immutable به معنی پایدار رخ می‌دهد و نه برای ساختار‌های ناپایدار (تغییر پذیر)  mutable؛ به این خاطر که آن‌ها مقادیرشان را مستقیما تغییر میدهند و اشاره‌ای در حافظه صورت نمی‌گیرد. 
string hel = "Hel";
string hello = "Hello";
string copy = hel + "lo";

string hello = "Hello";
string same = "Hello";

برای اطلاعات بیشتر در این زمینه این لینک را مطالعه نمایید.


مقایسه رشته‌ها
برای مقایسه دو رشته میتوان از علامت == یا از متد Equals استفاده نماییم. در این حالت به خاطر اینکه کد حروف کوچک و بزرگ متفاوت است، مقایسه حروف هم متفاوت خواهد بود. برای اینکه حروف کوچک و بزرگ تاثیری بر مقایسه ما نگذارند و #c را با #C برابر بدانند باید از متد Equals به شکل زیر استفاده کنیم:
Console.WriteLine(word1.Equals(word2,
    StringComparison.CurrentCultureIgnoreCase));
برای اینکه بزرگی و کوچکی اعداد را مشخص کنیم از علامت‌های < و > استفاده میکنیم ولی برای رشته‌ها از متد CompareTo بهره می‌بریم که چینش قرارگیری آن‌ها را بر اساس حروف الفبا مقایسه می‌کند و سه عدد، می‌تواند خروجی آن باشند. اگر 0 باشد یعنی برابر هستند، اگر -1 باشد رشته اولی قبل از رشته دومی است و اگر 1 باشد رشته دومی قبل از رشته اولی است.
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
 اینبار هم برای اینکه حروف کوچک و بزرگ، دخالتی در کار نداشته باشند، میتوانید از داده شمارشی StringComparison در متد ایستای (string.Compare(s1,s2,StringComparison استفاده نمایید؛ یا از نوع داده‌ای boolean برای تعیین نوع مقایسه استفاده کنید.
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
این عبارات الگوهایی هستند که قرار است عبارات مشابه الگویی را در رشته‌ها پیدا کنند. برای مثال الگوی +[A-Z0-9] مشخص می‌کند که رشته مورد نظر نباید خالی باشد و حداقل با یکی از حروف بزرگ یا اعداد پرشده باشد. این الگوها میتوانند برای واکشی داده‌ها یا قالب‌های خاص در رشته‌ها به کار بروند. برای مثال شماره تماس‌ها ، پست الکترونیکی و ...
در اینجا میتواند نحوه‌ی الگوسازی را بیاموزید. کد زیر بر اساس یک الگو، شماره تماس‌های مورد نظر را یافته و البته با فیلتر گذاری آن‌ها را نمایش می‌دهد:
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********
سه شماره تماس در رشته‌ی بالا با الگوی ما همخوانی دارند که بعد با استفاده از متد replace در شی Regex عبارات دلخواه خودمان را جایگزین شماره تماس‌ها خواهیم کرد. الگوی بالا شماره تماس‌هایی را میابد که با 08 آغاز شده‌اند و بعد از آن 8 عدد دیگر از 0 تا 9 قرار گرفته‌اند. بعد از اینکه متن مطابق الگو یافت شد، ما آن را با الگوی ********1$ جایگزین می‌کنیم که علامت $ یک placeholder برای یک گروه است. هر عبارت () در عبارات با قاعده یک گروه حساب میشود و اولین پرانتر 1$ و دومین پرانتز یا گروه میشود 2$ که در عبارت بالا (08) میشود 1$ و به جای مابقی الگو، 8 علامت ستاره نمایش داده میشود.

اتصال رشته‌ها در Loop
برای اتصال رشته‌ها ما از علامت + یا متد ایستای string.concat استفاده می‌کنیم ولی استفاده‌ی از آن در داخل یک حلقه باعث کاهش کارآیی برنامه خواهد شد. برای همین بیایید ببینم در حین اتتقال رشته‌ها در حافظه چه اتفاقی رخ میدهد. ما در اینجا دو رشته str1 و str2 داریم که عبارات "super" و "star" را نگه داری می‌کنند و در واقع دو متغیر هستند که به حافظه‌ی پویای Heap اشاره می‌کنند. اگر این دو را با هم جمع کنیم و نتیجه را در متغیر result قرار دهیم، سه متغیر میشوند که هر کدام به حافظه‌ای جداگانه در heap اشاره می‌کنند. در واقع برای این اتصال، قسمت جدیدی از حافظه تخصصیص داده شده و مقدار جدید در آن نشسته‌است. در این حالت یک متغیر جدید ساخته شد که به آدرس آن اشاره می‌کند. کل این فرآیند یک فرآیند کاملا زمانبر است که با تکرار این عمل موجب از دست دادن کارآیی برنامه می‌شود؛ به خصوص اگر در یک حلقه این کار صورت بگیرد.
سیستم دات نت همانطور که میدانید شامل GC یا سیستم خودکار پاکسازی حافظه است که برنامه نویس را از dispose کردن بسیاری از اشیاء بی نیاز می‌کند. موقعی‌که متغیری به قسمتی از حافظه اشاره می‌کند که دیگر بلا استفاده است، سیستم GC به صورت خودکار آنها را پاکسازی می‌کند که این عمل زمان بر هم خودش موجب کاهش کارآیی می‌شود. همچنین انتقال رشته‌ها از یک مکان حافظه به مکانی دیگر، باز خودش یک فرآیند زمانبر است؛ به خصوص اگر رشته مورد نظر طولانی هم باشد.
مثال عملی: در تکه کد زیر قصد داریم اعداد 1 تا 20000 را در یک رشته الحاق کنیم:
 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();
کد بالا تاز زمان نمایش کامل، بسته به قدرت سیستم ممکن است یکی دو ثانیه طول بکشد. حالا عدد را به 200000 تغییر دهید (یک صفر اضافه تر). برنامه را اجرا کنید و مجددا تست بزنید. در این حالت چند دقیقه ای بسته به قدرت سیستم زمان خواهد برد؛ مثلا دو دقیقه یا سه دقیقه یا کمتر و بیشتر.
عملیاتی که در حافظه صورت میگیرد این چند گام را طی میکند:
  • قسمتی از حافظه به طور موقت برای این دور جدید حلقه، گرفته میشود که به آن بافر میگوییم.
  • رشته قبلی به بافر انتقال میابد که بسته به مقدار آن زمان بر و کند است؛ 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 چه میکند که زمان پردازش آن قدر کوتاه است؟
همانطور که گفتیم این کلاس mutable یا تغییر پذیر است و برای انجام عملیات‌های ویرایشی نیازی به ایجاد شیء جدید در حافظه ندارد؛ در نتیجه باعث کاهش انتقال غیرضروری داده‌ها برای عملیات پایه‌ای چون الحاق رشته‌ها میگردد.
stringbuilder شامل یک بافر با ظرفیتی مشخص است (به طور پیش فرض 16 کاراکتر). این کلاس آرایه‌هایی از کاراکترها را پیاده سازی میکند که برای عملیات و پردازش‌هایش  از یک رابط کاربرپسند برای برنامه نویسان استفاده می‌کند. اگر تعداد کاراکترها کمتر از 16 باشد مثلا 5 ، فقط 5 خانه آرایه استفاده میشود و مابقی خانه‌ها خالی میماند و با اضافه شدن یک کاراکتر جدید، دیگر شیء جدیدی در حافظه درست نمی‌شود؛ بلکه در خانه ششم قرار می‌گیرد و اگر تعداد کاراکترهایی که اضافه می‌شوند باعث شود از 16 کاراکتر رد شود، مقدار خانه‌ها دو برابر میشوند؛ هر چند این عملیات دو برابر شدن resizing عملیاتی کند است ولی این اتفاق به ندرت رخ می‌دهد.
کد زیر یک آرایه 15 کاراکتری ایجاد می‌کند و عبارت #Hello C را در آن قرار می‌دهد.
StringBuilder sb = new StringBuilder(15);
sb.Append("Hello, C#!");

در شکل بالا خانه هایی خالی مانده است Unused و  جا برای کاراکترهای جدید به اندازه خانه‌های unused هست و اگر بیشتر شود همانطور که گفتیم تعداد خانه‌ها 2 برابر می‌شوند که در اینجا میشود 30.

استفاده از متد ایستای 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);
در کد بالا ابتدا ساختار قرار گرفتن تاریخ را بر اساس الگو بین {} مشخص می‌کنیم و متغیر date در آن قرار می‌گیرد و سپس برای {1},{2},{3} به ترتیب قرار گیری آن‌ها متغیرهای name,last,location قرار میگیرند.
از ()ToString. هم می‌توان برای فرمت بندی خروجی استفاده کرد؛ مثل همین عبارت MM/dd/yyyy در خروجی نوع داده تاریخ و زمان.
مطالب
اتصال و کار با SQL Server توسط VSCode
نگارش‌های بعدی SQL Server چندسکویی بوده و هم اکنون نگارش‌های آزمایشی آن برای لینوکس در دسترس هستند. به همین جهت مایکروسافت افزونه‌ی چندسکویی را برای VSCode به منظور اتصال و کار با SQL Server تدارک دیده‌است که آن‌را می‌توان یک نمونه‌ی سبک وزن Management Studio آن دانست.




دریافت و نصب افزونه‌ی SQL Server مخصوص VSCode

برای افزودن این افزونه، ابتدا در برگه‌ی Extensions، عبارت mssql را جستجو کرده و سپس آن‌را نصب کنید:


پس از نصب آن، مرحله‌ی بعد، ایجاد یک فایل خالی با پسوند sql است.


انجام اینکار ضروری است و شبیه به حالت نصب افزونه‌ی #C می‌باشد. به این ترتیب وابستگی‌های اصلی آن دریافت، نصب و فعال خواهند شد. این ابزارها نیز سورس باز بوده و موتور SQL Formatter، اجرای SQL و Intellisense آن‌را فراهم می‌کند و چون مبتنی بر NET Core. تهیه شده‌است، چندسکویی است.

تا اینجا مزیتی را که به دست خواهیم آورد Syntax highlighting و Intellisense جهت درج واژه‌های کلیدی عبارات SQL است:

و یا اگر بر روی فایل sql جاری کلیک راست کنیم، گزینه‌ی Format Document آن سبب می‌شود تا کدهای SQL نوشته شده، با فرمتی استاندارد، مرتب و یک‌دست شوند:


بنابراین اگر علاقمندید تا فایل‌ها و عبارات SQL خود را فرمت کنید، این افزونه‌ی سبک وزن چندسکویی، یک چنین قابلیت توکاری را به همراه دارد.
همچنین اگر علاقمندید به یک کتابخانه‌ی سورس باز چندسکویی SQL Formatter و SQL Parser دات نتی دسترسی داشته باشید، کدهای Microsoft/sqltoolsservice در دسترس هستند.


اتصال به SQL Server و کار با آن

پس از نصب مقدماتی افزونه‌ی mssql، دکمه‌های ctrl+shift+p (و یا F1) را فشرده و عبارت sql را جستجو کنید:


در اینجا سایر قابلیت‌های این افزونه‌ی نصب شده را می‌توان مشاهده کرد. در لیست ظاهر شده، گزینه‌ی Connect را انتخاب کنید. بلافاصله گزینه‌ی انتخاب پروفایل ظاهر می‌شود. چون هنوز پروفایلی را تعریف نکرده‌ایم، گزینه‌ی Create connection profile را انتخاب خواهیم کرد:


در ادامه باید نام سرور را وارد کرد. یا می‌توانید نام سرور کامل SQL خود را وارد کنید و یا اگر با LocalDB کار می‌کنید نیز امکان اتصال به آن با تایپlocaldb\MSSQLLocalDB  وجود دارد:


سپس نام بانک اطلاعاتی را که می‌خواهیم به آن متصل شویم ذکر می‌کنیم:


در مرحله‌ی بعد، باید نوع اعتبارسنجی اتصال مشخص شود:


چون در ویندوز هستیم، می‌توان گزینه‌ی Integrated را نیز انتخاب کرد (یا همان Windows Authentication).

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


اکنون کار اتصال به این بانک اطلاعاتی انجام شده و اگر به status bar دقت کنید، نمایش می‌دهد که در حال به روز رسانی اطلاعات intellisense است.


برای نمونه اینبار دیگر intellisense ظاهر شده منحصر به درج واژه‌های کلیدی SQL نیست. بلکه شامل تمام اشیاء بانک اطلاعاتی که به آن متصل شده‌ایم نیز می‌باشد:


در ادامه برای اجرا این کوئری می‌توان دکمه‌های Ctrl+Shift+E را فشرد و یا ctrl+shift+p (و یا F1) را فشرده و در منوی ظاهر شده، گزینه‌ی execute query را انتخاب کنید (این گزینه بر روی منوی کلیک راست ظاهر شده‌ی بر روی فایل sql جاری نیز قرار دارد):





نگاهی به محل ذخیره سازی اطلاعات اتصال به بانک اطلاعاتی

پروفایلی را که در قسمت قبل ایجاد کردیم، در منوی File->Preferences->Settings قابل مشاهده است:
// Place your settings in this file to overwrite the default settings
{
    "workbench.colorTheme": "Default Light+",
    "files.autoSave": "afterDelay",
    "typescript.check.tscVersion": false,
    "terminal.integrated.shell.windows": "cmd.exe",
    "workbench.iconTheme": "material-icon-theme",
    "vsicons.dontShowNewVersionMessage": true,
    "mssql.connections": [
        {
            "server": "(localdb)\\MSSQLLocalDB",
            "database": "TestASPNETCoreIdentityDb",
            "authenticationType": "Integrated",
            "profileName": "testLocalDB",
            "password": ""
        }
    ]
}
همانطور که مشخص است، کلید mssql.connections یک آرایه است و در اینجا می‌توان چندین پروفایل مختلف را تعریف و استفاده کرد.
برای مثال پروفایلی را که تعریف کردیم، در دفعات بعدی انتخاب گزینه‌ی Connect، به صورت ذیل ظاهر می‌شود:



تهیه‌ی خروجی از کوئری اجرا شده

اگر به نوار ابزار سمت راست نتیجه‌ی کوئری اجرا شده دقت کنید، سه دکمه‌ی تهیه‌ی خروجی با فرمت‌های csv، json و اکسل نیز در اینجا قرار داده شده‌است:


برای مثال اگر گزینه‌ی json آن‌را انتخاب کنید، بلافاصله نام فایلی را پرسیده و سپس این نتیجه را با فرمت JSON نمایش می‌دهد:


ضمن اینکه حتی می‌توان سطرها و سلول‌های خاصی را نیز از این خروجی انتخاب کرد و سپس با کلیک بر روی آن‌ها، تنها از این انتخاب، یک خروجی ویژه را تهیه نمود:



مشاهده‌ی ساختار اشیاء

اگر بر روی هر کدام از اجزای یک کوئری SQL متصل به بانک اطلاعاتی، کلیک راست کنیم، گزینه‌ی Go to definition نیز ظاهر می‌شود:


با انتخاب آن، بلافاصله عبارت کامل CREATE TABLE [dbo].[AppRoles] ظاهر می‌شود که در اینجا می‌توان ساختار این جدول را به صورت یک عبارت SQL مشاهده کرد.



تغییر تنظیمات افزونه‌ی MSSql

در منوی File->Preferences->Settings با جستجوی mssql می‌توان تنظیمات پیش فرض این افزونه را یافت. برای مثال اگر می‌خواهید تا SQL Formatter آن به صورت خودکار تمام واژه‌های کلیدی را با حروف بزرگ نمایش دهد، گزینه‌ی mssql.format.keywordCasing را انتخاب کنید. در کنار آن آیکن قلم ویرایش ظاهر می‌شود. با کلیک بر روی آن، منوی انتخاب uppercase را خواهیم داشت:


پس از این تغییر، اکنون بر روی صفحه کلیک راست کرده و گزینه‌ی Format Document را انتخاب کنید. در این حالت علاوه بر تغییر فرمت سند SQL جاری، تمام واژه‌های کلیدی آن نیز uppercase خواهند شد.
پیشنهادها
نوع‌های Generics در دات نت و اعمال ریاضی بر روی آن‌ها
نوع‌های Generics در دات نت از اعمال ریاضی مانند جمع و ضرب و منها پشتیبانی نمی‌کنند. در مقالات ذیل راه حل‌هایی برای رفع این مشکل ارائه شده‌اند که می‌توانند تبدیل به یک مقاله جدید گردند:
اشتراک‌ها
پکیج C# و ASP برای دریافت و جستوجوی استان ها ، شهر ها ،بخش ها ،دهستان ها و روستاهای ایران

این پکیج دیتا بیس تمام تقسیمات ارضی کشور را دارا میباشد و به راحتی با استفاده از سینتکس LINQ میتوان لیست استان‌ها ، شهر‌ها ، شهرستان‌ها ،بخش‌ها ،دهستان‌ها و روستاهای ایران را دریافت و روی آنها سرچ کند

این پکیج برای استفاده در بخش هایی از سایت که نیازمند نمایش این لیست‌ها میباشد بسیار مفید میباشد و همچنین بدلیل استفاده از دیتابیس در پکیج مشکل پرفرومنس نخواهد داشت

پکیج C# و ASP برای دریافت و جستوجوی استان ها ، شهر ها ،بخش ها ،دهستان ها و روستاهای ایران
نظرات مطالب
انتخاب پویای فیلد ها در LINQ
با سلام؛ با ایجاد ستون ردیف با Select new در LINQ مشکل دارم. طوریکه بصورت اتوماتیک یک ستون ردیف ایجاد نماییم:

var all = (from x in db.tblZones
select new
{
  RowNuber=????????????
  Code = x.xCode,
  Caption = x.xCaption,
  Comment = x.xComments,
  DT_RowId = "tr" + x.xCode.ToString(),

});
نظرات مطالب
آشنایی با Window Function ها در SQL Server بخش دوم
سلام 
مقاله زیر به خوبی طرز استفاده از Execution Plan را آموزش می‌دهد.
دو کتاب زیر، جهت مطالعه و بهینه سازی در ایجاد Query مفید است:
موفق باشید.
اشتراک‌ها
روش های مقایسه اشیاء با null

Check

Code 

Description

Is Null
if(variable is null) return true;

  • 🙂 This syntax supports static analysis such that later code will know whether variable is null or not.
  • 🙁 Doesn’t produce a warning even when comparing against a non-nullable value type making the code to check pointless.
  • 😐 Requires C# 7.0 because it leverages type pattern matching.
Is Not Null
if(variable is { }) return false

  • 🙂 This syntax supports static analysis such that later code will know whether variable is null or not.
  • 😐 Requires C# 8.0 since this is the method for checking for not null using property pattern matching.
Is Not Null
if(variable is object) return false

  • 🙂 Triggers a warning when comparing a non-nullable value type which could never be null
  • 🙂 This syntax works with C# 8.0’s static analysis so later code will know that variable has been checked for null.
  • Checks if the value not null by testing whether it is of type object.  (Relies on the fact that null values are not of type object.)
Is Null
if(variable == null) return true

  • 🙂 The only way to check for null prior to C# 7.0.
  • 🙁 However, because the equality operator can be overridden, this has the (remote) possibility of failing or introducing a performance issue.
Is Not Null
if(variable != null) return false

  • 🙂 The only way to check for not null prior to C# 7.0.
  • 😐 Since the not-equal operator can be overridden, this has the (remote) possibility of failing or introducing a performance issue. 
روش های مقایسه اشیاء با null