مطالب
حذف لایه‌های جدید اضافه شده به فایل‌های PDF توسط iTextSharp
شاید یک سری از Ebookهای PDF ایی را دیده باشید که سایت‌های ثالث، آن‌ها را پس از افزودن لایه‌ای متنی، مثلا در ذیل تمام صفحات به همراه آدرس وب سایت خودشان، باز انتشار می‌دهند. در مطلب جاری قصد داریم، نحوه حذف این لایه‌های اضافی را توسط iTextSharp بررسی کنیم.




یافتن و حذف لایه‌های اضافه شده به صفحات یک فایل PDF

برای آشنایی با ساختار سطح پایین لایه‌های اضافه شده نیاز است به برنامه iText Rups مراجعه کنیم.


همانطور که مشاهده می‌کنید، برای رسیدن به لایه‌ای که حاوی متن اضافه شده به ذیل تمام صفحات است، نیاز است ابتدا صفحات را گشوده و سپس CONTENTS آن‌ها را استخراج کنیم. در این CONTENTS کلیه streamهای موجود را بررسی و هر کدام که حاوی متن مورد نظر ما بودند، یافته و سپس آن استریم را با مقدار دهی طول آن به صفر، حذف کنیم. روش کار را در متد ذیل مشاهده می‌کنید:
        private static void removeWatermarkLayer(string watermarkedFile, string text,  string unwatermarkedFile)
        {
            PdfReader.unethicalreading = true;
            PdfReader reader = new PdfReader(watermarkedFile);
            reader.RemoveUnusedObjects();
            int pageCount = reader.NumberOfPages;
            for (int i = 1; i <= pageCount; i++)
            {
                var page = reader.GetPageN(i);                
                var contentarray = page.GetAsArray(PdfName.CONTENTS);
                if (contentarray == null)
                    continue;

                for (int j = 0; j < contentarray.Size; j++)
                {
                    var stream = (PRStream)contentarray.GetAsStream(j);
                    //دریافت محتوای خام صفحه
                    var content = System.Text.Encoding.ASCII.GetString(PdfReader.GetStreamBytes(stream));
                    if (content.Contains(text))
                    {
                        //حذف کامل محتوا از فایل
                        stream.Put(PdfName.LENGTH, new PdfNumber(0));
                        stream.SetData(new byte[0]);
                    }
                }
            }

            using (var fileStream = new FileStream(unwatermarkedFile, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                using (var stamper = new PdfStamper(reader, fileStream))
                {
                    stamper.SetFullCompression();
                    stamper.Close();
                }
            }
        }
در این متد watermarkedFile همان فایل اصلی دارای لایه‌های اضافی است. Text متنی است که در استریم‌های صفحات به دنبال آن خواهیم گشت و unwatermarkedFile نام و مسیر فایل تصحیح شده نهایی است که قرار است تولید شود.
مطالب دوره‌ها
آشنایی با مدل برنامه نویسی TAP
تاریخچه‌ی اعمال غیر همزمان در دات نت فریم ورک

دات نت فریم ورک، از زمان ارائه نگارش یک آن، از اعمال غیرهمزمان و API خاص آن پشتیبانی می‌کرده‌است. همچنین این مورد یکی از ویژگی‌های Win32 نیز می‌باشد. نوشتن کدهای همزمان متداول بسیار ساده است. در این نوع کدها هر عملیات خاص، پس از پایان عملیات قبلی انجام می‌شود.
        public string TestNoneAsync()
        {
            var webClient = new WebClient();
            return webClient.DownloadString("http://www.google.com");
        }
در این مثال متداول، متد DownloadString به صورت همزمان یا synchronous عمل می‌کند. به این معنا که تا پایان عملیات دریافت اطلاعات از وب، منتظر مانده و ترد جاری را قفل می‌کند. مشکل از جایی آغاز می‌شود که مدت زمان دریافت اطلاعات، طولانی باشد. چون این عملیات در ترد UI در حال انجام است، کل رابط کاربری برنامه تا پایان عملیات نیز قفل شده و دیگر پاسخگوی سایر اعمال رسیده نخواهد بود. در این حالت عموما ویندوز در نوار عنوان برنامه، واژه‌های Not responding را نمایش می‌دهد.
این مورد همچنین در برنامه‌های سمت سرور نیز حائز اهمیت است. با قفل شدن تعداد زیادی ترد در حال اجرا، عملا قدرت پاسخ‌دهی سرور نیز کاهش می‌یابد. بنابراین در این نوع موارد، برنامه‌های چند ریسمانی هرچند در سمت کلاینت ممکن است مفید واقع شوند و برای مثال ترد UI را آزاد کنند، اما اثر آنچنانی بر روی برنامه‌های سمت سرور ندارند. زیرا در آن‌ها می‌توان هزاران ترد را ایجاد کرد که همگی دارای کدهای اصطلاحا blocking باشند. برای حل این مساله استفاده از API غیرهمزمان توصیه می‌شود.
برای نمونه کلاس WebClient توکار دات نت، دارای متدی به نام DownloadStringAsync نیز می‌باشد. این متد به محض فراخوانی، ترد جاری را آزاد می‌کند. به این معنا که فراخوانی آن سبب توقف ترد جاری برای دریافت نتیجه‌ی دریافت اطلاعات از وب نمی‌شود. به این نوع API، یک Asynchronous API گفته می‌شود؛ زیرا با سایر کدهای نوشته شده، هماهنگ و همزمان اجرا نمی‌شود.
هر چند این کد جدید مشکل عدم پاسخ دهی برنامه را برطرف می‌کند، اما مشکل دیگری را به همراه دارد؛ چگونه باید حاصل عملیات آن‌را پس از پایان کار دریافت کرد؟ چگونه باید خطاها و مشکلات احتمالی را مدیریت کرد؟
برای مدیریت این مساله، رخدادی به نام DownloadStringCompleted تعریف شده‌است. روال رویدادگردان آن پس از پایان کار دریافت اطلاعات از وب، فراخوانی می‌گردد.
        public void TestAsync()
        {
            var webClient = new WebClient();
            webClient.DownloadStringAsync(new Uri("http://www.google.com"));
            webClient.DownloadStringCompleted += webClientDownloadStringCompleted;
        }

        void webClientDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            // use e.Result
        }
در اینجا همچنین توسط آرگومان DownloadStringCompletedEventArgs، موفقیت یا شکست عملیات نیز گزارش می‌شود و مقدار e.Result حاصل عملیات است.

مشکل!
ما سادگی یک عملیات همزمان را از دست دادیم. متد TestNoneAsync از لحاظ پیاده سازی و همچنین خواندن و نگهداری آن در طول زمان، بسیار ساده‌تر است از نمونه‌ی TestAsync نوشته شده. در کدهای غیرهمزمان فوق، یک متد ساده، به دو متد مجزا خرد شده‌است و نتیجه‌ی نهایی، درون یک روال رخدادگردان بدست می‌آید.
به این مدل، EAP یا Event based asynchronous pattern نیز گفته می‌شود. EAP در دات نت 2 معرفی شد. روال‌های رخدادگردان در این حالت، در ترد اصلی برنامه اجرا می‌شوند. اما اگر به حالت اصلی اعمال غیرهمزمان موجود از دات نت یک کوچ کنیم، اینطور نیست. در WinForms و WPF برای به روز رسانی رابط کاربری نیاز است اطلاعات دریافت شده در همان تردی که رابط کاربری ایجاد شده است، تحویل گرفته شده و استفاده شوند. در غیراینصورت استثنایی صادر شده و برنامه خاتمه می‌یابد.


آشنایی با Synchronization Context

ابتدا یک برنامه‌ی WinForms ساده را آغاز کرده و یک دکمه‌ی جدید را به نام btnGetInfo و یک تکست باکس را به نام txtResults، به آن اضافه کنید. سپس کدهای فرم اصلی آن‌را به نحو ذیل تغییر دهید:
using System;
using System.Linq;
using System.Net;
using System.Windows.Forms;

namespace Async02
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnGetInfo_Click(object sender, EventArgs e)
        {
            var req = (HttpWebRequest)WebRequest.Create("http://www.google.com");
            req.Method = "HEAD";
            req.BeginGetResponse(
                asyncResult =>
                {
                    var resp = (HttpWebResponse)req.EndGetResponse(asyncResult);
                    var headersText = formatHeaders(resp.Headers);
                    txtResults.Text = headersText;
                }, null);
        }

        private string formatHeaders(WebHeaderCollection headers)
        {
            var headerString = headers.Keys.Cast<string>()
                                      .Select(header => string.Format("{0}:{1}", header, headers[header]));
            return string.Join(Environment.NewLine, headerString.ToArray());
        }
    }
}
در اینجا از روش دیگری برای دریافت اطلاعات از وب استفاده کرده‌ایم. با استفاده از امکانات HttpWebRequest، کوئری‌های پیشرفته‌تری را می‌توان تهیه کرد. برای مثال می‌توان نوع متد را به HEAD تنظیم نمود؛ تا صرفا مقادیر هدر آدرس درخواستی از سرور، دریافت شوند.
همچنین در این مثال از متد غیرهمزمان BeginGetResponse نیز استفاده شده‌است. در این نوع API خاص، کار با BeginGetResponse آغاز شده و سپس در callback نهایی توسط EndGetResponse، نتیجه‌ی عملیات به دست می‌آید.
اگر برنامه را اجرا کنید، با استثنای زیر مواجه خواهید شد:
 An exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll but was not handled in user code
Additional information: Cross-thread operation not valid: Control 'txtResults' accessed from a thread other than the thread it was created on.
علت اینجا است که asyncResult دریافتی، در تردی دیگر نسبت به ترد اصلی برنامه که UI را اداره می‌کند، اجرا می‌شود. یکی از راه حل‌های این مشکل و انتقال اطلاعات به ترد اصلی برنامه، استفاده از Synchronization Context است:
        private void btnGetInfo_Click(object sender, EventArgs e)
        {
            var sync = SynchronizationContext.Current;
            var req = (HttpWebRequest)WebRequest.Create("http://www.google.com");
            req.Method = "HEAD";
            req.BeginGetResponse(
                asyncResult =>
                {
                    var resp = (HttpWebResponse)req.EndGetResponse(asyncResult);
                    var headersText = formatHeaders(resp.Headers);
                    sync.Post(delegate { txtResults.Text = headersText; }, null);
                }, null);
        }
SynchronizationContext.Current در اینجا چون در ابتدای متد دریافت اطلاعات اجرا می‌شود، به ترد UI، یا ترد اصلی برنامه اشاره می‌کند. به همین جهت این زمینه را نباید داخل Async callback دریافت کرد؛ زیرا ترد جاری آن، ترد UI مدنظر ما نیست. سپس همانطور که ملاحظه می‌کنید، توسط متد Post آن می‌توان اطلاعات را در زمینه‌ی تردی که SynchronizationContext به آن اشاره می‌کند اجرا کرد.


برای درک بهتر آن، سه break point را پیش از متد BeginGetResponse، داخل  Async calback و داخل delegate متد Post قرار دهید. پس از اجرای برنامه، از منوی دیباگ در VS.NET گزینه‌ی Windows و سپس Threads را انتخاب کنید.
در اینجا همانطور که مشخص است، کد داخل delegate تعریف شده، در ترد اصلی برنامه اجرا می‌شود و نه یکی از Worker threadهای ثانویه.
هر چند استفاده از متدهای تو در تو و lambda syntax، نیاز به تعریف چندین متد جداگانه را برطرف کرده‌است، اما باز هم کد ساده‌ای به نظر نمی‌رسد. در سی شارپ 5، برای مدیریت بهتر تمام مشکلات یاد شده، پشتیبانی توکاری از اعمال غیرهمزمان، به هسته‌ی زبان اضافه شده‌است.


Syntax ابتدایی یک متد Async

در ابتدا کلاس و متد Async زیر را در نظر بگیرید:
using System;
using System.Threading.Tasks;

namespace Async01
{
    public class AsyncExample
    {
        public async Task DoWorkAsync(int parameter)
        {
            await Task.Delay(parameter);
            Console.WriteLine(parameter);
        }
    }
}
شیوه‌ی نگارش آن بر اساس راهنمای نوشتن برنامه‌های Async یا Task asynchronous programming model یا به اختصار TAP است:
- در مدل برنامه نویسی TAP، متدهای غیرهمزمان باید یک Task را بازگشت دهند؛ یا نمونه‌ی جنریک آن‌را. البته کامپایلر، async void را نیز پشتیبانی می‌کند ولی در قسمت‌های بعدی بررسی خواهیم کرد که چرا استفاده از آن مشکل‌زا است و باید از آن پرهیز شود.
- همچنین مطابق TAP، اینگونه متدها باید به پسوند Async ختم شوند تا استفاده کننده در حین کار با Intellisense، بتواند آ‌ن‌ها را از متدهای معمولی سریعتر تشخیص دهد.
- از واژه‌ی کلیدی async نیز استفاده می‌گردد تا کامپایلر از وجود اعمال غیر همزمان مطلع گردد.
- await به کامپایلر می‌گوید، عبارت پس از من، یک وظیفه‌ی غیرهمزمان است و ادامه‌ی کدهای نوشته شده، تنها زمانی باید اجرا شوند که عملیات غیرهمزمان معرفی شده، تکمیل گردد.

در متد DoWorkAsync، ابتدا به اندازه‌‌ای مشخص توقف حاصل شده و سپس سطر بعدی یعنی Console.WriteLine اجرا می‌شود.


یک اشتباه عمومی! استفاده از واژه‌های کلیدی async و await متد شما را async نمی‌کنند.

برخلاف تصور ابتدایی از بکارگیری واژه‌های کلیدی async و await، این کلمات نحوه‌ی اجرای متد شما را async نمی‌کنند. این کلمات صرفا برای تشکیل متدهایی که هم اکنون غیرهمزمان هستند، مفید می‌باشند. برای توضیح بیشتر آن به مثال ذیل دقت کنید:
        public async Task<double> GetNumberAsync()
        {
            var generator = new Random();
            await Task.Delay(generator.Next(1000));

            return generator.NextDouble();
        }
در این متد با استفاده از Task.Delay، انجام یک عملیات طولانی شبیه سازی شده‌است؛ مثلا دریافت یک عدد یا نتیجه از یک وب سرویس. سپس در نهایت، عددی را بازگشت داده است. برای بازگشت یک خروجی double، در اینجا از نمونه‌ی جنریک Task استفاده شده‌است.
در ادامه برای استفاده از آن خواهیم داشت:
        public async Task<double> GetSumAsync()
        {
            var leftOperand = await GetNumberAsync();
            var rightOperand = await GetNumberAsync();

            return leftOperand + rightOperand;
        }
خروجی این متد تنها زمانی بازگشت داده می‌شود که نتایج leftOperand و rightOperand از وب سرویس فرضی، دریافت شده باشند و در اختیار مصرف کننده قرارگیرند. بنابراین همانطور که ملاحظه می‌کنید از واژه‌ی کلیدی await جهت تشکیل یک عملیات غیرهمزمان و مدیریت ساده‌تر کدهای نهایی، شبیه به کدهای معمولی همزمان استفاده شده‌است.
در کدهای همزمان متداول، سطر اول ابتدا انجام می‌شود و بعد سطر دوم و الی آخر. با استفاده از واژه‌ی کلیدی await یک چنین عملکردی را با اعمال غیرهمزمان خواهیم داشت. پیش از این برای مدیریت اینگونه اعمال از یک سری callback و یا رخداد استفاده می‌شد. برای مثال ابتدا عملیات همزمانی شروع شده و سپس نتیجه‌ی آن در یک روال رخ‌داد گردان جایی در کدهای برنامه دریافت می‌شد (مانند مثال ابتدای بحث). اکنون تصور کنید که قصد داشتید جمع نهایی حاصل دو عملیات غیرهمزمان را از دو روال رخدادگردان جدا از هم، جمع آوری کرده و بازگشت دهید. هرچند اینکار غیرممکن نیست، اما حاصل کار به طور قطع آنچنان زیبا نبوده و قابلیت نگهداری پایینی دارد. واژه‌ی کلیدی await، انجام اینگونه امور غیرهمزمان را طبیعی و همزمان جلوه می‌دهد. به این ترتیب بهتر می‌توان بر روی منطق و الگوریتم‌های مورد استفاده تمرکز داشت، تا اینکه مدام درگیر مکانیک اعمال غیرهمزمان بود.

امکان استفاده از واژه‌ی کلیدی await در هر جایی از کدها وجود دارد. برای نمونه در مثال زیر، برای ترکیب دو عملیات غیرهمزمان، از await در حین تشکیل عملیات ضرب نهایی، دقیقا در جایی که مقدار متد باید بازگشت داده شود، استفاده شده‌است:
        public async Task<double> GetProductOfSumAsync()
        {
            var leftOperand = GetSumAsync();
            var rightOperand = GetSumAsync();

            return await leftOperand * await rightOperand;
        }
اگر await را از این مثال حذف کنیم، خطای کامپایل زیر را دریافت خواهیم کرد:
 Operator '*' cannot be applied to operands of type 'System.Threading.Tasks.Task<double>' and 'System.Threading.Tasks.Task<double>'
خروجی متد GetSumAsync صرفا یک Task است و نه یک عدد. پس از استفاده از await، عملیات آن انجام شده و بازگشت داده می‌شود.


اگر متد DownloadString همزمان ابتدای بحث را نیز بخواهیم تبدیل به نمونه‌ی async سی‌شارپ 5 کنیم، می‌توان از متد الحاقی جدید آن به نام DownloadStringTaskAsync کمک گرفت:
        public async Task<string> DownloadAsync()
        {
            var webClient = new WebClient();
            return await webClient.DownloadStringTaskAsync("http://www.google.com");
        }
نکته‌ی مهم این کد علاوه بر ساده سازی اعمال غیر همزمان، برای استفاده از نتیجه‌ی نهایی آن، نیازی به SynchronizationContext معرفی شده در تاریخچه‌ی ابتدای بحث نیست. نتیجه‌ی دریافتی از آن در ترد اصلی برنامه تحویل داده شده و به سادگی قابل استفاده است.


سؤال: آیا استفاده از await نیز ترد جاری را قفل می‌کند؟

اگر به کدها دقت کنید، استفاده از await به معنای صبر کردن تا پایان عملیات async است. پس اینطور به نظر می‌رسد که در اینجا نیز ترد اصلی، همانند قبل قفل شده‌است.
        public void TestDownloadAsync()
        {
            Debug.WriteLine("Before DownloadAsync");
            DownloadAsync();
            Debug.WriteLine("After DownloadAsync");
        }
اگر این متد را اجرا کنید (در آن await بکار نرفته)، بلافاصله خروجی ذیل را مشاهده خواهید کرد:
 Before DownloadAsync
After DownloadAsync
به این معنا که در اصل، همانند سایر روش‌های async موجود از دات نت یک، در اینجا نیز فراخوانی متد async ترد اصلی را بلافاصله آزاد می‌کند و ترد آن‌را قفل نخواهد کرد. استفاده از await نیز عملکرد کدها را تغییر نمی‌دهد. تنها کامپایلر در پشت صحنه همان کدهای لازم جهت مدیریت روال‌های رخدادگردان و callbackها را تولید می‌کند، به نحوی که صرفا نحوه‌ی کدنویسی ما همزمان به نظر می‌رسد، اما در پشت صحنه، نحوه‌ی اجرای آن غیرهمزمان است.


برنامه‌های Async و نگارش‌های مختلف دات نت

شاید در ابتدا به نظر برسد که قابلیت‌های جدید async و await صرفا متعلق هستند به دات نت 4.5 به بعد؛ اما خیر. اگر کامپایلری را داشته باشید که از این واژه‌های کلیدی را پشتیبانی کند، امکان استفاده از آن‌ها را با دات نت 4 نیز خواهید داشت. برای این منظور تنها کافی است از VS 2012 به بعد استفاده نمائید. سپس در کنسول پاورشل نیوگت دستور ذیل را اجرا نمائید (فقط برای برنامه‌های دات نت 4 البته):
 PM> Install-Package Microsoft.Bcl.Async
این روال متداول VS.NET بوده است تا به امروز. برای مثال اگر VS 2010 را نصب کنید و سپس یک برنامه‌ی دات نت 3.5 را ایجاد کنید، امکان استفاده‌ی کامل از تمام امکانات سی‌شارپ 4، مانند آرگومان‌های نامدار و یا مقادیر پیش فرض آرگومان‌ها را در یک برنامه‌ی دات نت 3.5 نیز خواهید داشت. همین نکته در مورد async نیز صادق است. VS 2012 (یا نگارش‌های جدیدتر) را نصب کنید و سپس یک پروژه‌ی دات نت 4 را آغاز کنید. امکان استفاده از async و await را خواهید داشت. البته در این حالت دسترسی به متدهای الحاقی جدید را مانند DownloadStringTaskAsync نخواهید داشت. برای رفع این مشکل باید بسته‌ی  Microsoft.Bcl.Async را نیز توسط نیوگت نصب کنید.
مطالب
خودکار کردن تعاریف DbSetها در EF Code first
پیشنیاز:
تعریف نوع جنریک به صورت متغیر

مطلبی را چندی قبل در مورد نحوه خودکار کردن افزودن کلاس‌های EntityTypeConfiguration به modelBuilder در این سایت مطالعه کردید. در مطلب جاری به خودکار سازی تعاریف مرتبط با DbSetها خواهیم پرداخت.
ابتدا مثال کامل زیر را درنظر بگیرید:
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
using System.Reflection;

namespace MyNamespace
{
    public abstract class BaseEntity
    {
        public int Id { set; get; }
        public string CreatedBy { set; get; }
    }

    public class User : BaseEntity
    {
        public string Name { get; set; }
    }

    public class MyContext : DbContext
    {
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            var asm = Assembly.GetExecutingAssembly();
            loadEntities(asm, modelBuilder, "MyNamespace");
        }

        void loadEntities(Assembly asm, DbModelBuilder modelBuilder, string nameSpace)
        {
            var entityTypes = asm.GetTypes()
                                    .Where(type => type.BaseType != null &&
                                           type.Namespace == nameSpace &&
                                           type.BaseType.IsAbstract &&
                                           type.BaseType == typeof(BaseEntity))
                                    .ToList();

            var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");
            entityTypes.ForEach(type =>
            {
                entityMethod.MakeGenericMethod(type).Invoke(modelBuilder, new object[] { });
            });
        }
    }

    public class Configuration : DbMigrationsConfiguration<MyContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = true;
            AutomaticMigrationDataLossAllowed = true;
        }

        protected override void Seed(MyContext context)
        {
            context.Set<User>().Add(new User { Name = "name-1" });
            context.Set<User>().Add(new User { Name = "name-2" });
            context.Set<User>().Add(new User { Name = "name-3" });
            base.Seed(context);
        }
    }

    public static class Test
    {
        public static void RunTests()
        {
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Configuration>());
            using (var context = new MyContext())
            {
                var user1 = context.Set<User>().Find(1);
                if (user1 != null)
                    Console.WriteLine(user1.Name);
            }
        }
    }
}
توضیحات:
همانطور که ملاحظه می‌کنید در این مثال خبری از تعاریف DbSetها نیست. به کمک Reflection تمام مدل‌های برنامه که از نوع کلاس پایه BaseEntity هستند (روشی مرسوم جهت مدیریت خواص تکراری مدل‌ها) یافت شده (در متد loadEntities) و سپس نتیجه حاصل به صورت پویا به متد جنریک Entity ارسال می‌شود. حاصل، افزوده شدن خودکار کلاس‌های مورد نظر به سیستم EF است.
البته در این حالت چون دیگر کلاس‌های مدل‌ها در MyContext به صورت صریح تعریف نمی‌شوند، نحوه استفاده از آن‌ها را توسط متد Set، در متدهای RunTests و یا Seed، ملاحظه می‌کنید. 
مطالب
پیدا کردن آیتم‌های تکراری در یک لیست به کمک LINQ

گاهی از اوقات نیاز می‌شود تا در یک لیست، آیتم‌های تکراری موجود را مشخص کرد. به صورت پیش فرض متد Distinct برای حذف مقادیر تکراری در یک لیست با استفاده از LINQ موجود است که البته آن‌هم اما و اگرهایی دارد که در ادامه به آن پرداخته خواهد شد، اما باز هم این مورد پاسخ سؤال اصلی نیست (نمی‌خواهیم موارد تکراری را حذف کنیم).

برای حذف آیتم‌های تکراری از یک لیست جنریک می‌توان متد زیر را نوشت:
public static List<T> RemoveDuplicates<T>(List<T> items)
{
return (from s in items select s).Distinct().ToList();
}
برای مثال:
public static void TestRemoveDuplicates()
{
List<string> sampleList =
new List<string>() { "A1", "A2", "A3", "A1", "A2", "A3" };
sampleList = RemoveDuplicates(sampleList);
foreach (var item in sampleList)
Console.WriteLine(item);
}
این متد بر روی لیست‌هایی با نوع‌های اولیه مانند string‌ و int و امثال آن درست کار می‌کند. اما اکنون مثال زیر را در نظر بگیرید:
public class Employee
{
public int ID { get; set; }
public string FName { get; set; }
public int Age { get; set; }
}

public static void TestRemoveDuplicates()
{
List<Employee> lstEmp = new List<Employee>()
{
new Employee(){ ID=1, Age=20, FName="F1"},
new Employee(){ ID=2, Age=21, FName="F2"},
new Employee(){ ID=1, Age=20, FName="F1"},
};

lstEmp = RemoveDuplicates<Employee>(lstEmp);

foreach (var item in lstEmp)
Console.WriteLine(item.FName);
}
اگر متد TestRemoveDuplicates را اجرا نمائید، رکورد تکراری این لیست جنریک حذف نخواهد شد؛ زیرا متد distinct بکارگرفته شده نمی‌داند اشیایی از نوع کلاس سفارشی Employee را چگونه باید با هم مقایسه نماید تا بتواند موارد تکراری آن‌ها را حذف کند.
برای رفع این مشکل باید از آرگومان دوم متد distinct جهت معرفی وهله‌ای از کلاسی که اینترفیس IEqualityComparer را پیاده سازی می‌کند، کمک گرفت.
public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer);
که نمونه‌ای از پیاده سازی آن به شرح زیر می‌تواند باشد:

public class EmployeeComparer : IEqualityComparer<Employee>
{
public bool Equals(Employee x, Employee y)
{
//آیا دقیقا یک وهله هستند؟
if (Object.ReferenceEquals(x, y)) return true;

//آیا یکی از وهله‌ها نال است؟
if (Object.ReferenceEquals(x, null) ||
Object.ReferenceEquals(y, null))
return false;

return x.Age == y.Age && x.FName == y.FName && x.ID == y.ID;
}

public int GetHashCode(Employee obj)
{
if (Object.ReferenceEquals(obj, null)) return 0;
int hashTextual = obj.FName == null ? 0 : obj.FName.GetHashCode();
int hashDigital = obj.Age.GetHashCode();
return hashTextual ^ hashDigital;
}
}
اکنون اگر یک overload برای متد RemoveDuplicates با درنظر گرفتن IEqualityComparerتهیه کنیم، به شکل زیر خواهد بود:
public static List<T> RemoveDuplicates<T>(List<T> items, IEqualityComparer<T> comparer)
{
return (from s in items select s).Distinct(comparer).ToList();
}
به این صورت متد آزمایشی ما به شکل زیر (که وهله‌ای از کلاس EmployeeComparer‌ به آن ارسال شده) تغییر خواهد کرد:
public static void TestRemoveDuplicates()
{
List<Employee> lstEmp = new List<Employee>()
{
new Employee(){ ID=1, Age=20, FName="F1"},
new Employee(){ ID=2, Age=21, FName="F2"},
new Employee(){ ID=1, Age=20, FName="F1"},
};

lstEmp = RemoveDuplicates(lstEmp, new EmployeeComparer());

foreach (var item in lstEmp)
Console.WriteLine(item.FName);
}
پس از این تغییر، حاصل این متد تنها دو رکورد غیرتکراری می‌باشد.

سؤال: برای یافتن آیتم‌های تکراری یک لیست چه باید کرد؟
احتمالا مقاله "روش‌هایی برای حذف رکوردهای تکراری" را به خاطر دارید. اینجا هم می‌توان کوئری LINQ ایی را نوشت که رکوردها را بر اساس سن، گروه بندی کرده و سپس گروه‌هایی را که بیش از یک رکورد دارند، انتخاب نماید.
public static void FindDuplicates()
{
List<Employee> lstEmp = new List<Employee>()
{
new Employee(){ ID=1, Age=20, FName="F1"},
new Employee(){ ID=2, Age=21, FName="F2"},
new Employee(){ ID=1, Age=20, FName="F1"},
};

var query = from c in lstEmp
group c by c.Age into g
where g.Count() > 1
select new { Age = g.Key, Count = g.Count() };

foreach (var item in query)
{
Console.WriteLine("Age {0} has {1} records", item.Age, item.Count);
}
}


Vote on iDevCenter
نظرات مطالب
آماده سازی زیرساخت تهیه Integration Tests برای ServiceLayer
نکته تکمیلی
در پروژه خود از الگوی Container Per Request استفاده می‌کنید؟ برای نزدیکتر کردن شرایط تست به شرایط محیط عملیاتی می‌توان به شکل زیر عمل کرد:
کلاسی برای ایجاد و تخریب Nested Container 
    public static class TestDependencyScope
    {
        private static IContainer _currentNestedContainer;

        public static void Begin()
        {
            if (_currentNestedContainer != null)
                throw new Exception("Cannot begin test dependency scope. Another dependency scope is still in effect.");

            _currentNestedContainer = IoC.Container.GetNestedContainer();
        }

        public static IContainer CurrentNestedContainer
        {
            get
            {
                if (_currentNestedContainer == null)
                    throw new Exception($"Cannot access the {nameof(CurrentNestedContainer)}. There is no dependency scope in effect.");

                return _currentNestedContainer;
            }
        }

        public static void End()
        {
            if (_currentNestedContainer == null)
                throw new Exception("Cannot end test dependency scope. There is no dependency scope in effect.");

            _currentNestedContainer.Dispose();
            _currentNestedContainer = null;
        }
    }

سپس به مانند  AutoRollbackAttrbiute مذکور در مطلب جاری، ContainerPerTestCaseAttribute را برای مدیریت این قضیه در نظر می‌گیریم:
 public class ContainerPerTestCaseAttribute : Attribute, ITestAction
    {   
        public void BeforeTest(ITest test)
        {
            TestDependencyScope.Begin();
        }

        public void AfterTest(ITest test)
        {
            TestDependencyScope.End();
        }

        public ActionTargets Targets => ActionTargets.Test;
    }
و حال نحوه استفاده از آن:
    [PopulateHttpContext]
    [ContainerPerTestCase]
    [Transactional]
    [TestFixture]
    public class IntegratedTestBase
    {
        [SetUp]
        public void EachTestSetUp()
        {
            BeforeEachTest();
        }
        [TearDown]
        public void EachTestTearDown()
        {
            AfterEachTest();
        }
        protected virtual void BeforeEachTest()
        {
        }
        protected virtual void AfterEachTest()
        {
        }

        protected void UsingUnitOfWork(Action<IUnitOfWork> action)
        {
            IoC.Container.Using((IUnitOfWork uow)=>
            {
                uow.DisableAllFilters();
                action(uow);
            });
        }

        protected T UsingUnitOfWork<T>(Func<IUnitOfWork, T> func)
        {
            var uow = IoC.Resolve<IUnitOfWork>();

            uow.DisableAllFilters();

            using (uow)
            {
                var result = func(uow);

                uow.SaveChanges();

                return result;
            }
        }
        protected async Task<T> UsingUnitOfWorkAsync<T>(Func<IUnitOfWork, Task<T>> func)
        {
            var uow = IoC.Resolve<IUnitOfWork>();

            uow.DisableAllFilters();

            using (uow)
            {
                var result = await func(uow).ConfigureAwait(false);

                await uow.SaveChangesAsync().ConfigureAwait(false);

                return result;
            }

        }
    }
و برای دسترسی به Nested Container جاری می‌توان به شکل زیر عمل کرد:
namespace ProjectName.ServiceLayer.IntegrationTests
{
    public static class Testing
    {
        private static IContainer Container => TestDependencyScope.CurrentNestedContainer;

        public static T Resolve<T>()
        {
            return Container.GetInstance<T>();
        }

        public static object Resolve(Type type)
        {
            return Container.GetInstance(type);
        }

        public static void Inject<T>(T instance) where T : class
        {
            Container.Inject(instance);
        }
    }
}

//in test classes
using static ProjectName.ServiceLayer.IntegrationTests.Testing;
namespace ProjectName.ServiceLayer.IntegrationTests
{
    public class RoleServiceTests : IntegratedTestBase
    {
        private IRoleService _service;
        protected override void BeforeEachTest()
        {
            _service = Resolve<IRoleService>();
        }
     }
}

مطالب
معرفی Async Parallel.ForEach در دات نت 6
عموما زمانیکه می‌خواهیم تمام وظایف مدنظر، به صورت موازی اجرا شوند، آن‌ها را Task.WhenAll می‌کنیم. برای مثال 10 هزار درخواست HTTP را به صورت وظایفی، WhenAll می‌کنیم و ... در این حالت ... سرور ریموت، IP شما را خواهد بست! چون کنترلی بر روی تعداد وظیفه‌ی در حالت اجرای موازی وجود ندارد و یک چنین عملی، شبیه به یک حمله‌ی DDOS عمل می‌کند! برای مدیریت بهتر یک چنین مواردی، در دات نت 6 متدهای Parallel.ForEachAsync ارائه شده‌اند تا دیگر نیازی به استفاده از راه‌حل‌های ثالثی که عموما آنچنان بهینه هم نیستند، نباشد.
public static Task ForEachAsync<TSource>(IEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask> body)
public static Task ForEachAsync<TSource>(IEnumerable<TSource> source, CancellationToken cancellationToken, Func<TSource, CancellationToken, ValueTask> body)
public static Task ForEachAsync<TSource>(IEnumerable<TSource> source, ParallelOptions parallelOptions, Func<TSource, CancellationToken, ValueTask> body)
public static Task ForEachAsync<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask> body)
public static Task ForEachAsync<TSource>(IAsyncEnumerable<TSource> source, CancellationToken cancellationToken, Func<TSource, CancellationToken, ValueTask> body)
public static Task ForEachAsync<TSource>(IAsyncEnumerable<TSource> source, ParallelOptions parallelOptions, Func<TSource, CancellationToken, ValueTask> body)
این مجموعه متدها از ValueTaskها بجای Taskها استفاده می‌کند تا سربار ایجاد Taskها در حلقه‌ها کاهش یابد. همچنین در اینجا degree of parallelism به صورت پیش‌فرض به تعداد هسته‌های سی‌پی تنظیم شده‌است (Environment.ProcessorCount)؛ چون عموما توسعه دهنده‌ها نمی‌دانند که چه عددی را باید برای آن انتخاب کنند. هر چند امکان تنظیم دستی آن‌ها هم وجود دارد (یکی از مهم‌ترین مشکلات کار با WhenAll).

یک مثال: در اینجا می‌خواهیم به صورت موازی، مشخصات کاربرانی از Github را توسط HttpClient دریافت کنیم. هر بار هم فقط می‌خواهیم سه وظیفه اجرا شوند و نه بیشتر
using System.Net.Http.Headers;
using System.Net.Http.Json;
 
var userHandlers = new []  { "users/VahidN", "users/shanselman", "users/jaredpar", "users/davidfowl" };
 
using HttpClient client = new()
{
    BaseAddress = new Uri("https://api.github.com"),
};
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("DotNet", "6"));
 
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = 3 };
 
await Parallel.ForEachAsync(userHandlers, parallelOptions, async (uri, token) =>
{
    var user = await client.GetFromJsonAsync<GitHubUser>(uri, token); 
    Console.WriteLine($"Name: {user.Name}\nBio: {user.Bio}\n");
});
 
public class GitHubUser
{
    public string Name { get; set; }
    public string  Bio { get; set; }
}
در این مثال، نمونه‌ای از کارکرد متد جدید Parallel.ForEachAsync را مشاهده می‌کنید که اینبار، MaxDegreeOfParallelism آن قابل تنظیم است. یعنی با تنظیم فوق، هربار فقط سه وظیفه به صورت موازی اجرا خواهند شد. البته تنظیم آن به منهای یک، همان حالت WhenAll را سبب خواهد شد؛ یعنی محدودیتی وجود نخواهد داشت.
متد Parallel.ForEachAsync، آرایه‌ای را که باید بر روی آن کار کند، دریافت می‌کند. سپس تنظیمات اجرای موازی آن‌ها را هم مشخص می‌کنیم. در ادامه آن‌ها را در دسته‌های مشخصی، به صورت موازی بر اساس منطقی که مشخص می‌کنیم، اجرا خواهد کرد.


وضعیت امکان اجرای موازی متدهای async همزمان، تا پیش از دات نت 6

<List<T به همراه متد الحاقی ForEach است که می‌تواند یک <Action<T را بر روی المان‌های این لیست، اجرا کند و ... عموما زمانیکه به وظایف async می‌رسیم، به اشتباه مورد استفاده قرار می‌گیرد:
customers.ForEach(c => SendEmailAsync(c));
مثال فوق، با اجرای حلقه‌ی زیر تفاوتی ندارد:
foreach(var c in customers)
{
    SendEmailAsync(c); // the return task is ignored
}
یعنی یک عملیات async، بدون await فراخوانی شده‌است و تا پایان عملیات مدنظر، صبر نخواهد شد. حداقل مشکل آن این است که اگر در این بین استثنایی رخ دهد، هیچگاه متوجه آن نخواهید شد و حتی می‌تواند کل پروسه‌ی برنامه را خاتمه دهد. شاید عنوان کنید که می‌شود این مشکل را به صورت زیر حل کرد:
customers.ForEach(async c => await SendEmailAsync(c));
اما ... این روش هم تفاوتی با قبل ندارد. از این لحاظ که متد ForEach یک <Action<T را دریافت می‌کند که خروجی آن void است. یعنی در نهایت با راه حل دوم، فقط یک async void ایجاد می‌شود که باز هم قابلیت صبر کردن تا پایان عملیات را ندارد. نکته‌ی مهم اینجا است که اجرای موازی آن‌ها توسط متد Parallel.ForEach نیز دقیقا همین مشکل را دارد.
تنها راه حل پذیرفته‌ی شده‌ی چنین عمل async ای، فراخوانی آن‌ها به صورت متداول زیر و بدون استفاده از متد ForEach است:
foreach(var c in customers)
{
   await SendEmailAsync(c);
}
و یا Task.WhenAll کردن آن‌ها، با علم به این موضوع که MaxDegreeOfParallelism آن قابل کنترل نیست (حداقل به صورت استاندارد و بدون نیاز به کتابخانه‌های جانبی). برای مثال بجای نوشتن:
foreach(var o in orders)
{
    await ProcessOrderAsync(o);
}
می‌توان آن‌را به صورت زیر درآورد:
var tasks = orders.Select(o => ProcessOrderAsync(o)).ToList();
await Task.WhenAll(tasks);
در این حالت عملیات ProcessOrderAsync را تبدیل به لیستی از وظایف مدنظر کرده و به متد Task.WhenAll ارسال می‌کنیم تا به صورت موازی اجرا شوند. اما ... اگر 10 هزار Task وجود داشته باشند، کنترلی بر روی تعداد وظایف در حال اجرای موازی وجود نخواهد داشت و این مورد نه تنها سبب بالا رفتن کارآیی نخواهد شد، بلکه می‌تواند سرور را هم با اخلال پردازشی، به علت کمبود منابع در دسترس مواجه کند.

دات نت 6، هم کنترل MaxDegreeOfParallelism را میسر کرده‌است و هم اینکه اینبار نگارش async واقعی Parallel.ForEachAsync را ارائه داده‌است تا دیگر همانند حالت قبلی Parallel.ForEach، به async void‌ها و مشکلات مرتبط با آن‌ها نرسیم.
مطالب
اجزاء معماری سیستم عامل اندروید (قسمت دوم رمزنگاری اندروید) :: بخش ششم
ذخیره داده‌ها در اندروید
اندروید برنامه‌های کاربردی را در زمینه‌ی (context) امنیت جداگانه‌ای اجرا و برای اجرای آنها زمینه‌های خاصی را در سیستم عامل تعیین تکلیف می‌کند و این برای ما کاملا شفاف است که در این سیستم عامل بزرگ و گسترده چه تدابیری ارائه شده است. این بدان معنا است که هر برنامه با UID و GID خود اجرا خواهد شد. برای مثال زمانیکه در یک برنامه اطلاعاتی را می‌نویسید، برنامه‌های دیگر قادر نخواهند بود آن داده‌ها را بخوانند.
اگر می‌خواهید اطلاعاتی را بین برنامه‌ها به اشتراک بگذارید، پس باید به صراحت این اشتراک گذاری را با استفاده از یک تامین‌کننده محتوا به اشتراک بگذارید تا امنیت قابل توجهی بین آنها ایجاد شود. اندروید به شما اجازه می‌دهد تا داده‌ها را با استفاده از پنج گزینه مختلف ذخیره کنید. این شما هستید که باید تصمیم بگیرید که چگونه داده‌های خاص خود را براساس الزامات پروژه ذخیره کنید. به تصویر زیر دقت کنید:
در این تصویر اطلاعات کاملی از مکانیزم ذخیره سازی داده‌ها در پلتفرم اندروید مشاهده می‌کنید.


ما می‌توانیم داده‌های خود را در یک بانک از نوع SQLite در نظر بگیریم. پایگاه‌داده به این دلیل، ما را از تصمیم غیرضروری برای اجرای ساختار بی نظم داده‌ها بی‌نیاز می‌کند. بیایید به یک مثال از نحوه ذخیره و بازیابی داده‌ها با استفاده از یکی از این مکانیزم‌ها نگاه کنیم.

Shared Preferences یا اولویت‌های اشتراکی

اولویت‌های مشترک عمدتا برای ذخیره‌سازی تنظیمات برنامه‌ها مفید هستند و تا زمانی که راه‌اندازی مجدد توسط دستگاه انجام شود، معتبر خواهد بود. بیایید بگوییم که باید اطلاعات مربوط به یک سرور ایمیل را ذخیره کنیم که برنامه ما برای بازیابی داده‌ها به آن نیاز دارد.

ما باید نام میزبان ایمیل (hostname) , درگاه (port) و کارگزاری که از SSL استفاده می‌کند را ذخیره کنیم. کلاس زیر این کار را برای ما انجام میدهد به قطعه کد زیر توجه نمایید:

package net.zenconsult.android;
import java.util.Hashtable;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.preference.PreferenceManager;
public class StoreData {
 public static boolean storeData(Hashtable data, Context ctx) {
  SharedPreferences prefs = PreferenceManager
   .getDefaultSharedPreferences(ctx);
  String hostname = (String) data.get("hostname");
  int port = (Integer) data.get("port");
  boolean useSSL = (Boolean) data.get("ssl");
  Editor ed = prefs.edit();
  ed.putString("hostname", hostname);
  ed.putInt("port", port);
  ed.putBoolean("ssl", useSSL);
  return ed.commit();
 }
}
و بازیابی داده‌ها از طریق کلاس زیر میسر می‌شود
package net.zenconsult.android;
import java.util.Hashtable;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
public class RetrieveData {
 public static Hashtable get(Context ctx) {
  String hostname = "hostname";
  String port = "port";
  String ssl = "ssl";
  Hashtable data = new Hashtable();
  SharedPreferences prefs = PreferenceManager
   .getDefaultSharedPreferences(ctx);
  data.put(hostname, prefs.getString(hostname, null));
  data.put(port, prefs.getInt(port, 0));
  data.put(ssl, prefs.getBoolean(ssl, true));
  return data;
 }
}
کلاس main برای ذخیره داده‌ها بدین صورت نوشته خواهد شد
package net.zenconsult.android;
import java.util.Hashtable;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.widget.EditText;
public class StorageExample1Activity extends Activity {
 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  Context cntxt = getApplicationContext();
  Hashtable data = new Hashtable();
  data.put("hostname", "smtp.gmail.com");
  data.put("port", 587);
  data.put("ssl", true);
  if (StoreData.storeData(data, cntxt))
   Log.i("SE", "Successfully wrote data");
  else
   Log.e("SE", "Failed to write data to Shared Prefs");
  EditText ed = (EditText) findViewById(R.id.editText1);
  ed.setText(RetrieveData.get(cntxt).toString());
 }
}
با توجه به مثال فوق، خروجی ما در انتها به صورت زیر خواهد بود که تصویر زیر توضیحات بالا را پوشش می‌دهد.


مطالب
خلاصه‌ای در مورد SQL Server CE

SQL Server CE برای اولین بار جهت استفاده در SmartPhones طراحی شد؛ جزو خانواده‌ی Embedded databases قرار می‌گیرد و این مزایا را دارد:
- نیازی به نصب ندارد و از چند DLL تشکیل شده است (برای مثال جهت استفاده در کارهای تک کاربره‌ی قابل حمل ایده‌آل است).
- رایگان است (جهت استفاده در کارهای تجاری و غیرتجاری).
- حجم کمی دارد (جمعا کمتر از دو مگابایت).
- پروایدر ADO.NET آن موجود است (توسط فضای نام System.Data.SqlServerCe که به کمک اسمبلی System.Data.SqlServerCe.dll قرار گرفته در مسیر C:\Program Files\Microsoft SQL Server Compact Edition\v3.5\Desktop ارائه می‌شود).
- با کمک ORM هایی مانند Entity framework و یا NHibernate نیز می‌توان با آن کار کرد.
- نسخه‌ی 4 نهایی آن که قرار است در زمان ارائه‌ی SP1 مربوط به VS.NET 2010 ارائه شود، جهت استفاده در برنامه‌های ASP.NET (برنامه‌های چند کاربره) ایی که تعداد کاربر کمی دارند، بهینه سازی شده و این مورد یک مزیت مهم نسبت به SQLite است که اساسا با تردهای همزمان جهت کار با بانک اطلاعاتی مشکل دارد.
- امکان گذاشتن کلمه‌ی عبور بر روی بانک اطلاعاتی آن وجود دارد که سبب رمزنگاری خودکار آن نیز خواهد شد (این مورد به صورت پیش فرض در SQLite پیش بینی نشده و جزو مواردی که است که باید برای آن هزینه کرد). الگوریتم رمزنگاری آن به صورت رسمی معرفی نشده، ولی به احتمال زیاد AES می‌باشد.
- از ADO.NET Sync Framework پشتیبانی می‌کند.

ملاحظات:
- به آن می‌توان به صورت نسخه‌ی تعدیل شده‌ی SQL Server 2000 با توانایی‌های کاهش یافته نگاه کرد. در آن خبری از رویه‌های ذخیره شده، View ها ، Full text search ، CLR Procs، CLR Triggers و غیره نیست (سطح توقع را باید در حد همان 2 مگابایت پایین نگه داشت!). لیست کامل : (+)
- Management studio مربوط به SQL Server 2005 به هیچ عنوان از آن پشتیبانی نمی‌کند و تنها نسخه‌ی 2008 است که نگارش 3 و نیم آن‌را پشتیبانی می‌کند آن هم نه با توانایی‌هایی که جهت کار با SQL Server اصلی وجود دارد. مثلا امکان rename یک فیلد را ندارد و باید برای اینکار کوئری نوشت. خوشبختانه یک سری پروژه‌ی رایگان در سایت CodePlex این نقایص را پوشش داده‌اند؛ برای مثال : ExportSqlCe
- از آنجائیکه DLL های SQL CE از نوع Native هستند، باید دقت داشت که حین استفاده از آن‌ها در دات نت فریم ورک اگر platform target قسمت build برنامه بر روی ALL CPU تنظیم شده باشد، برنامه به احتمال زیاد در سیستم‌های 64 بیتی کرش خواهد کرد (اگر در حین توسعه برنامه از DLL‌های بومی 32 بیتی آن استفاده شده باشد). بنابراین نیاز است DLL های 64 بیتی را به صورت جداگانه جهت سیستم‌های 64 بیتی ارائه داد. اطلاعات بیشتر: (+) و (+) و (+)
- Entity framework یک سری از قابلیت‌های این بانک اطلاعاتی را پشتیبانی نمی‌کند. برای مثال اگر یک primary key از نوع identity را تعریف کردید، برنامه کار نخواهد کرد! لیست مواردی را که پشتیبانی نمی‌شوند، در این آدرس می‌توان مشاهده کرد.

و اخبار مرتبط با SQL CE را در این بلاگ می‌توانید دنبال کنید.

مطالب
INPC استاندارد با بهره گیری از صفت CallerMemberName
یکی از Attribute‌های بسیار کاربردی که در سی شارپ 5 اضافه شد CallerMemberNameAttribute بود. این صفت به یک متد اجازه میدهد که از فراخواننده‌ی خود مطلع شود. این صفت را می‌توان بر روی یک پارامتر انتخابی که مقدار پیش‌فرضی دارد اعمال نمود.

استفاده از این صفت هم بسیار ساده است:

private void A ( [CallerMemberName] string callerName = "") 
{
  Console.WriteLine("Caller is " + callerName);
}

private static void B()
{
        // let's call A
        A();
}
در کد فوق، متد A به راحتی می‌تواند بفهمد چه کسی آن را فراخوانی کرده است. از جمله کاربردهای این صفت در ردیابی و خطایابی است.

ولی یک استفاده‌ی بسیار کاربردی از این صفت، در پیاده سازی رابط INotifyPropertyChanged می‌باشد.

معمولا هنگام پیاده سازی INotifyPropertyChanged کدی شبیه به این را می‌نویسیم:

    public class PersonViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        private string name;
        public string Name
        {
            get { return name; }
            set
            {
                this.name = value;
                OnPropertyChanged("Name");
            }
        }
    }

یعنی در Setter معمولا نام ویژگی ای را که تغییر کرده است، به متد OnPropertyChanged می‌فرستیم تا اطلاع رسانی‌های لازم انجام پذیرد. تا اینجای کار همه چیز خوب و آرام است. اما به محضی که کد شما کمی طولانی شود و شما به دلایلی نیاز به Refactor کردن کد و احیانا تغییر نام ویژگی‌ها را پیدا کنید، آن موقع مسائل جدیدی بروز پیدا می‌کند.

برای مثال فرض کنید پس از نوشتن کلاس PersonViewModel تصمیم می‌گیرد نام ویژگی Name را به FirstName تغییر دهید؛ چرا که می‌خواهید اجزای نام یک شخص را به صورت مجزا نگهداری و پردازش کنید. پس احتمالا با زدن کلید F2 روی فیلد name آن را به firstName و ویژگی Name را به FirstName تغییر نام می‌دهید. همانند کد زیر:

private string firstName;
public string FirstName
{
            get { return firstName; }
            set
            {
                this.firstName = value;
                OnPropertyChanged("Name");
            }
}

برنامه را کامپایل کرده و در کمال تعجب می‌بینید که بخشی از برنامه درست رفتار نمی‌کند و تغییراتی که در نام کوچک شخص توسط کاربر ایجاد می‌شود به درستی بروزرسانی نمی‌شوند. علت ساده است: ما کد را به صورت اتوماتیک Refactor کرده ایم و گزینه‌ی Include String را در حین Refactor، در حالت پیشفرض غیرفعال رها کرده‌ایم. پس جای تعجبی ندارد که در هر جای کد که رشته‌ای به نام "Name" با ماهیت نام شخص داشته ایم، دست نخورده باقی مانده است. در واقع در کد تغییر یافته، هنگام تغییر FirstName، ما به سیستم گزارش می‌کنیم که ویژگی Name (که اصلا وجود ندارد) تغییر یافته است و این یعنی خطا.

حال احتمال بروز این خطا را در ViewModel هایی با ده‌ها ویژگی و ترکیب‌های مختلف در نظر بگیرید. پس کاملا محتمل است و برای خیلی از دوستان این اتفاق رخ داده است.

و اما راه حل چیست؟ به کارگیری صفت CallerMemberName

بهتر است که یک کلاس انتزاعی برای تمام ViewModel‌های خود داشته باشیم و پیاده سازی جدید INPC را در درون آن قرار دهیم تا براحتی VM‌های ما از آن مشتق شوند:

public abstract class ViewModelBase : INotifyPropertyChanged
{
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            OnPropertyChangedExplicit(propertyName);
        }

        protected void OnPropertyChanged<TProperty>(Expression<Func<TProperty>> projection)
        {
            var memberExpression = (MemberExpression)projection.Body;
            OnPropertyChangedExplicit(memberExpression.Member.Name);
        }

        void OnPropertyChangedExplicit(string propertyName)
        {
            this.CheckPropertyName(propertyName);

            PropertyChangedEventHandler handler = this.PropertyChanged;

            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

        #region Check property name

        [Conditional("DEBUG")]
        [DebuggerStepThrough]
        public void CheckPropertyName(string propertyName)
        {
            if (TypeDescriptor.GetProperties(this)[propertyName] == null)
                throw new Exception(String.Format("Could not find property \"{0}\"", propertyName));
        }

        #endregion // Check property name
}

در این کلاس، ما پارامتر propertyName را از متد OnPropertyChanged، توسط صفت CallerMemberName حاشیه نویسی کرده‌ایم. این کار باعث می‌شود در Setter‌های ویژگی‌ها، به راحتی بدون نوشتن نام ویژگی، عملیات اطلاع رسانی تغییرات را انجام دهیم. بدین صورت که کافیست متد OnPropertyChanged بدون هیچ آرگومانی در Setter فراخوانی شود و صفت CallerMemberName به صورت اتوماتیک نام ویژگی ای که فراخوانی از درون آن انجام شده است را درون پارامتر propertyName قرار می‌دهد.

پس کلاس PersonViewModel را به صورت زیر می‌توانیم اصلاح و تکمیل کنیم:

public class PersonViewModel : ViewModelBase
{
        private string firstName;
        public string FirstName
        {
            get { return firstName; }
            set
            {
                this.firstName = value;

                OnPropertyChanged();
                OnPropertyChanged(() => this.FullName);
            }
        }

        private string lastName;
        public string LastName
        {
            get { return lastName; }
            set
            {
                this.lastName = value;

                OnPropertyChanged();
                OnPropertyChanged(() => this.FullName);
            }
        }

        public string FullName
        {
            get { return string.Format("{0} {1}", FirstName, LastName); }
        }
}
همانطور که می‌بینید متد OnPropertyChanged بدون آرگومان فراخوانی میشود. اکنون اگر شما اقدام به Refactor کردن کد خود بکنید دیگر نگرانی از بابت تغییر نکردن رشته‌ها و کامنت‌ها نخواهید داشت و مطمئن هستید، نام ویژگی هر چیزی که باشد، به صورت خودکار به متد ارسال خواهد شد.

کلاس ViewModelBase یک پیاده سازی دیگر از OnPropetyChanged هم دارد که به شما اجازه می‌دهد با استفاده دستورات لامبدا، OnPropertyChanged را برای هر یک از اعضای دلخواه کلاس نیز فراخوانی کنید. همانطور که در مثال فوق می‌بینید، تغییرات نام خانوادگی در نام کامل شخص نیز اثرگذار است. در نتیجه به وسیله‌ی یک Func به راحتی بیان می‌کنیم که FullName هم تغییر کرده است و اطلاع رسانی برای آن نیز باید صورت پذیرد.

برای استفاده از صفت CallerMemberName باید دات نت هدف خود را 4.5 یا 4.6 قرار دهید.

ارجاع:
Raise INPC witout string name
مطالب
واژه‌های کلیدی جدید and، or و not در C# 9.0
یکی از ویژگی‌های زبان VB، شباهت بیش از اندازه‌ی آن به زبان انگلیسی است. برای مثال در این زبان با استفاده از not و and:
If Not a And b Then
  ...
Else
  ...
EndIf
می‌توان if‌های خواناتری را نسبت به #C ایجاد کرد:
if(!(a) && b)
{
...
}
else
{
}
در ادامه خواهیم دید که چگونه C# 9.0، این آرزوی دیرین را برآورده می‌کند! البته مایکروسافت در جای دیگری هم عنوان کرده‌است که زبان VB را دیگر پیگیری نمی‌کند و تغییر خاصی را در آن شاهد نخواهید بود. شاید به همین دلیل و جذب برنامه نویس‌های VB به #C، یک چنین تغییراتی رخ داده‌اند!


معرفی واژه‌ی کلیدی جدید not در C# 9.0

در ابتدا اینترفیس نمونه‌ای را به همراه دو کلاس مشتق شده‌ی از آن درنظر بگیرید:
public interface ICommand
{
}

public class Command1 : ICommand
{
}

public class Command2 : ICommand
{
}
اکنون اگر وهله‌ای از Command1 را ایجاد کرده و بخواهیم بررسی کنیم که آیا از نوع کلاس Command2 هست یا خیر، با استفاده از pattern matching و واژه‌ی کلیدی if می‌توان به صورت زیر عمل کرد:
ICommand command = new Command1();
if (!(command is Command2))
{

}
در C# 9.0، برای خواناتر کردن یک چنین بررسی‌هایی، می‌توان از pattern matching بهبود یافته‌ی آن و واژه‌ی کلیدی جدید not نیز استفاده کرد:
if (command is not Command2)
{

}


معرفی واژه‌های کلیدی جدید and و or در C# 9.0

واژه‌های کلیدی جدید and و or نیز درک و نوشتن عبارات pattern matching را بسیار ساده می‌کنند. برای نمونه قطعه کد متداول زیر را درنظر بگیرید:
if ((command is ICommand) && !(command is Command2))
{

}
اکنون در C# 9.0 با استفاده از واژه‌های کلیدی جدید and، or و not، می‌توان قطعه کد فوق را بسیار ساده کرد:
if (command is ICommand and not Command2)
{

}
نه تنها این قطعه کد ساده‌تر شده‌است، بلکه خوانایی آن افزایش یافته‌است و مانند یک سطر نوشته شده‌ی به زبان انگلیسی به نظر می‌رسد. همچنین در این حالت نیازی هم به تکرار command، در هر بار مقایسه نیست.
و یا حتی در اینجا در صورت نیاز می‌توان از واژه‌ی کلیدی جدید or نیز استفاده کرد:
if (command is Command1 or Command2)
{

}


امکان اعمال واژه‌های کلیدی جدید and، or و not به سایر نوع‌ها نیز وجود دارند

تا اینجا مثال‌هایی را که بررسی کردیم، در مورد بررسی نوع اشیاء بود. اما می‌توان این واژه‌های کلیدی جدید در C# 9.0 را به هر نوع ممکنی نیز اعمال کرد. برای نمونه، مثال ساده‌ی زیر را که در مورد بررسی اعداد است، درنظر بگیرید:
var number = new Random().Next(1, 10);
if (number > 2 && number < 8)
{

}
اکنون در C# 9.0 و با استفاده از امکانات جدید pattern matching آن می‌توان شرط متداول فوق را به صورت زیر ساده کرد:
if (number is > 2 and < 8)
{

}
در اینجا تنها یکبار نیاز به ذکر number است و از واژه‌های کلیدی is و and استفاده شده‌است.

یک مثال دیگر: متد زیر را در نظربگیرید که با استفاده از && و || متداول #C نوشته شده‌است:
public static bool IsLetterOrSeparator(char c) =>
   (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '.' || c == ',';
روش ارائه‌ی C# 9.0 ای آن به صورت زیر است:
public static bool IsLetterOrSeparator(char c) =>
   c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z') or '.' or ',';


امکان اعمال واژه‌های کلیدی جدید and، or و not به switchها نیز وجود دارد

برای نمونه قطعه کد if/else دار متداول زیر را درنظر بگیرید که قابلیت تبدیل به یک سوئیچ را نیز دارد:
 var number = new Random().Next(1, 10);
 if (number <= 0)
 {
     Console.WriteLine("Less than or equal to 0");
 }
 else if (number > 0 && number <= 10)
 {
     Console.WriteLine("More than 0 but less than or equal to 10");
 }
 else
 {
     Console.WriteLine("More than 10");
 }
اگر بخواهیم همین قطعه کد را به کمک واژه‌های کلیدی جدید C# 9.0 و pattern matching بهبود یافته‌ی آن تبدیل به یک سوئیچ کنیم، به قطعه کد زیر خواهیم رسید:
// C#9.0
 switch (number)
 {
     case <= 0:
         Console.WriteLine("Less than or equal to 0");
         break;
     case > 0 and <= 10:
         Console.WriteLine("More than 0 but less than or equal to 10");
         break;
     default:
         Console.WriteLine("More than 10");
         break;
 }
تا پیش از C# 7.0، سوئیچ‌های #C امکان بررسی باز‌ه‌ای از مقادیر را نداشتند. از آن زمان با معرفی pattern matching، چنین محدودیتی برطرف شد و اکنون می‌توان syntax قدیمی آن‌را توسط C# 9.0، بسیار خلاصه‌تر کرد. در ذیل، معادل قطعه کد فوق را بر اساس امکانات C# 7.0 مشاهده می‌کنید که خوانایی کمتری را داشته و حجم کد نویسی بیشتری را دارد:
// C#7.0
 switch (number)
 {
     case int value when value <= 0:
         Console.WriteLine("Less than or equal to 0");
         break;
     case int value when value > 0 && value <= 10:
         Console.WriteLine("More than 0 but less than or equal to 10");
         break;
     default:
         Console.WriteLine("More than 10");
         break;
 }

و یا حتی می‌توان سوئیچ C# 9.0 را توسط switch expression بهبود یافته‌ی C# 8.0 نیز به شکل زیر بازنویسی کرد:
 var message = number switch
 {
     <= 0 => "Less than or equal to 0",
     > 0 and <= 10 => "More than 0 but less than or equal to 10",
     _ => "More than 10"
 };


انواع pattern matching‌های اضافه شده‌ی به C# 9.0

در این مطلب سعی شد مفاهیم pattern matching اضافه شده‌ی به C# 9.0، ذیل عنوان واژه‌های کلیدی جدید آن بحث شوند؛ اما هر کدام دارای نام‌های خاصی هم هستند:
الف) relational patterns: امکان استفاده‌ی از <, >, <= and >= را در الگوها میسر می‌کنند. مانند نمونه‌های سوئیچی که نوشته شد.
ب) logical patterns: امکان استفاده‌ی از واژه‌های کلیدی and، or و not را در الگوها ممکن می‌کنند.
ج) not pattern: امکان استفاده‌ی از واژه‌ی کلیدی not را در عبارات if میسر می‌کند.
د) Simple type pattern: در مثال‌های زیر، پس از انطباق با یک الگو، کاری با متغیر یا شیء مرتبط نداریم. در نگارش‌های قبلی برای صرفنظر کردن از آن، ذکر _ ضروری بود؛ اما در C#9.0 می‌توان آن‌را نیز ذکر نکرد:
private static int GetDiscount(Product p) => p switch
        {
            Food => 0, // Food _ => 0 before C# 9
            Book b => 75, // Book b _ => 75 before C# 9
            _ => 25
        };