مطالب
دریافت زمانبندی شده به روز رسانی‌های آنتی ویروس Symantec به کمک کتابخانه‌های Quartz.NET و Html Agility Pack
در این رابطه آقای راد در دو قسمت به صورت مختصر و مفید این کتابخانه قدرتمند رو همراه با ارائه چندین مثال کاربردی معرفی کردند:
قسمت اول
قسمت دوم
در تکمیل قسمت‌های فوق بنده می‌خوام مثالی رو در این رابطه براتون بذارم، هدف از ارائه این مثال اتوماتیک سازی یک فرآیند روتین می‌باشد، به این صورت که در جایی که بنده مشغول به کار هستم یک سری لایسنس آنتی ویروس برای کلاینت‌ها در یک شبکه با مقیاس متوسط تهیه گردیده است، حال یک نسخه رایگان نیز برای کاربرانی که قصد دارند آنتی ویروس را برای سیستم شخصی خود نصب کنند نیز موجود می‌باشد که نیاز به آپدیت دارد معمولا آپدیت‌ها هر چند روز یکبار یا هر هفته در دو نسخه 64 و 32 بیتی ارائه می‌شوند، روال معمول برای دریافت آپدیت مراجعه به سایت و دانلود نسخه‌های مربوطه میباشد.

حال توسط کتابخانه قدرتمند Quartz.NET این فرآیند روتین را به صورت اتوماتیک می‌خواهیم انجام دهیم، استفاده از کتابخانه ذکر شده سخت نیست همانطور که در دو مطلب قبلی مرتبط ذکر گردیده، تنها پیاده سازی چندین اینترفیس است و بس.

namespace SymantecUpdateDownloader
{
    using System;
    using System.IO;
    using Quartz;
    using Quartz.Impl;
    using System.Globalization;
    public class TestJob : IJob
    {
        public void Execute(IJobExecutionContext context)
        {
            new Download().Scraping();
        }
    }
    public interface ISchedule
    {
        void Run();
    }
    public class TestSchedule : ISchedule
    {
        public void Run()
        {
            
            DateTimeOffset startTime = DateBuilder.FutureDate(2, IntervalUnit.Second);

            IJobDetail job = JobBuilder.Create<HelloJob>()
                                       .WithIdentity("job1")
                                       .Build();

            ITrigger trigger = TriggerBuilder.Create()
                                             .WithIdentity("trigger1")
                                             .StartAt(startTime)
                                             .WithDailyTimeIntervalSchedule(x => x.OnEveryDay().StartingDailyAt(new TimeOfDay(7, 0)).WithRepeatCount(0))
                                             .Build();

            ISchedulerFactory sf = new StdSchedulerFactory();
            IScheduler sc = sf.GetScheduler();
            sc.ScheduleJob(job, trigger);

            sc.Start();
        }
    }
}
در این کد که همانند کدهای پیشنیازهای مطلب است، در خط 33 از متد WithDailyTimeIntervalSchedule استفاده شده است و همانطور که مشخص است وظیفه تعیین شده و هر روز ساعت 7 اجرا میشود.
مورد بعدی عملیات دانلود فایل می‌باشد که در ادامه مشاهده خواهید کرد، صفحه ایی که لینک فایل‌های دانلود را ارائه داده است دو نسخه مد نظر ما را در ابتدا لیست کرده است و با استفاده از web scrapingمی توانیم موارد تعیین شده را استخراج کنیم برای این منظور از کتابخانه htmlagilitypack استفاده میکنیم، تطبیق دو مورد(لینک) اول جهت دریافت نسخه‌های 32 و 64 بیتی به کمک Regular Expression میسر است و همانطور که در شکل زیر مشاهده میکنید از سمت چپ تاریخ به صورت 8 رقم، سه رقم قسمت دوم و ارقام و حروف قسمت سوم است به اضافه پسوند فایل مشخص است :


public class Download
    {
        static WebClient wc = new WebClient();
        static ManualResetEvent handle = new ManualResetEvent(true);
        
        private DateTime myDate = new DateTime();
        public void Scraping()
        {
            using (WebClient client = new WebClient())
            {
                client.Encoding = System.Text.Encoding.UTF8;
                var doc = new HtmlAgilityPack.HtmlDocument();
                ArrayList result = new ArrayList();
                doc.LoadHtml(client.DownloadString("https://www.symantec.com/security_response/definitions/download/detail.jsp?gid=savce"));
                var tasks = new List<Task>();
                foreach (var href in doc.DocumentNode.Descendants("a").Select(x => x.Attributes["href"]))
                {
                    if (href == null) continue;
                    string s = href.Value;
                    Match m = Regex.Match(s, @"http://definitions.symantec.com/defs/(\d{8}-\d{3}-v5i(32|64)\.exe)");
                    if (m.Success)
                    {
                        Match date = Regex.Match(m.Value, @"(\d{4})(\d{2})(\d{2})");
                        Match filename = Regex.Match(m.Value, @"\d{8}-\d{3}-v5i(32|64)\.exe");
                        int year = Int32.Parse(date.Groups[0].Value);
                        int month = Int32.Parse(date.Groups[1].Value);
                        int day = Int32.Parse(date.Groups[3].Value);

                        myDate = new DateTime(
                                Int32.Parse(date.Groups[1].Value),
                                Int32.Parse(date.Groups[2].Value),
                                Int32.Parse(date.Groups[3].Value));
                        if (myDate == DateTime.Today)
                        {
                            tasks.Add(DownloadUpdate(m.Value, filename.Value));
                        }
                        else
                        {
                            MessageBox.Show("امروز آپدیت موجود نیست");
                        }
                    }
                }
                DownloadTask = Task.WhenAll(tasks);
            }
        }
        private static Task DownloadTask;
        private Task DownloadUpdate(string url, string fileName)
        {
            var wc = new WebClient();
            return wc.DownloadFileTaskAsync(new Uri(url), @"\\10.1.0.15\SymantecUpdate\\" + fileName);
        }

    }
توضیح کدهای فوق :
ابتدا توسط متد LoadHtml خط 14 صفحه مورد نظر که حاوی لینک‌ها می‌باشد رو Load میکنیم، سپس توسط یک حلقه foreach خط 16 مقدار خصوصیت href تمام لینک‌های موجود  در صفحه را استخراج میکنیم مثلا مقدار خصوصیت href در لینک‌ها به صورت  زیر می‌باشد :
 
http://definitions.symantec.com/defs/20130622-007-v5i32.exe

http://definitions.symantec.com/defs/20130622-007-v5i64.exe

همانطور که مشخص است در دو مورد فوق تنها نام فایل متفاوت می‌باشد، همانطور که بحث شد برای نام فایل‌ها هم می‌توانیم یک Pattern را به صورت زیر داشته باشیم :
(\d{8}-\d{3}-v5i(32|64)\.exe)
در خط 20 نیز عملیات تطبیق تمام href‌های موجود در صفحه را توسط Regular Expression فوق تطبیق می‌دهیم، اگر تطبیق با موفقیت انجام پذیرفت باید نام فایل و همچنین تاریخ موجود در نام فایل را نیز توسط دو Regular Expression استخراج کنیم(خط 23 و 24) در ادامه برای جدا کردن مقادیر سال ، ماه ، روز از امکان Groups در RegEx استفاده کرده ایم:
int year = Int32.Parse(date.Groups[0].Value);
int month = Int32.Parse(date.Groups[1].Value);
int day = Int32.Parse(date.Groups[3].Value);
در ادامه تاریخ استخراج شده را با تاریخ روز جاری مقایسه می‌کنیم اگر مساوی بود عملیات دانلود فایل‌ها توسط یک Task تعریف شده به صورت همزمان بر روی سرور مربوطه دانلود می‌شوند.
البته لازم به ذکر است که کدهای فوق مسلما نیاز یه Refactoring دارند منتها هدف از ارائه این مثال آشنایی بیشتر با کتابخانه‌های فوق می‌باشد.
نکته آخر اینکه برنامه فوق به حالت‌های مختلفی می‌تواند اجرا گردد مثل یک برنامه وب  یا یک سرویس ویندوزی و ... ، بهترین حالت یک سرویس ویندوز می‌باشد، ولی در حالت خام در حال حاضر یک ویندوز اپلیکیشن ساده می‌باشد که بر روی سرور RUN شده است که در آینده به صورت یک سرویس ویندوز ارائه خواهد شد.
نظرات اشتراک‌ها
کره‌جنوبی و ژاپن رکورددار بیشترین سرعت اینترنت در جهان
سرعت بیشتر؟! من از داتک اینترنت گرفتم. سه ماهه با 5 گیگ حجم اولیه. قبلا (تا یک ماه قبل) از ساعت 1 بامداد تا 7 صبح آن رایگان بود (در محاسبه حجم حساب نمی‌شد). الان یک ماه هست بدون اطلاع به کاربران، این ساعت شده از 2 بامداد تا 7 صبح. چون اطلاع نداشتم، مثل قبل از ساعت 1 بامداد هر شب دانلود می‌کردم. چند روز قبل دیدم که حجم 5 گیگ اولیه‌ام زودتر از موعد تمام شده! تبدیل این مساله به 2 بامداد، تقریبا زندگی من رو مختل کرده.
اشتراک‌ها
کتاب کار برنامه‌ نویسان

کتاب «Coders at Work  » نوشته پیتر سیبل در سال 2009 به چاپ رسیده و با وجودی که مدّت زیادی از انتشار آن نمی‌گذرد مورد استقبال زیادی از سوی طراحان نرم‌افزار و برنامه‌نویسان قرار گرفته است. پیتر سیبل، که نام کتابش را از کتاب‌های معروف و پیشین «Writers at Work  » و «Founders at Work  » اقتباس کرده، در این کتاب به مصاحبه با پانزده تن از برجسته‌ترین و سرشناس‌ترین برنامه‌نویسان و دانشمندان رایانه، با پیش زمینه تحصیلی و علاقه‌مندی‌های متفاوت در این حوزه، پرداخته و خواننده را با ایده‌های آن‌ها درباره زندگی و برنامه‌نویسی آشنا ساخته است.  

کتاب کار برنامه‌ نویسان
مطالب
استفاده از خواص راهبری در EF Code first جهت ساده سازی کوئر‌ی‌ها
گاهی از اوقات یافتن معادل LINQ کوئری‌های SQLایی که پیشتر به سادگی و بر اساس ممارست، در کسری از دقیقه نوشته می‌شدند، آنچنان ساده نیست. برای مثال فرض کنید یک سری پروژه وجود دارند که به ازای هر پروژه، تعدادی بازخورد ثبت شده است. هر بازخورد نیز دارای وضعیت‌هایی مانند «در حال انجام» و «انجام شد» است. می‌خواهیم کوئری LINQ سازگار با EF ایی را تهیه کنیم که تعداد موارد «در حال انجام» را نمایش دهد.
بر این اساس، کلاس‌های مدل دومین مساله به صورت زیر خواهند بود:
    public class Project
    {
        public int Id { set; get; }
        public string Name { set; get; }

        public virtual ICollection<ProjectIssue> ProjectIssues { set; get; }
    }

    public class ProjectIssue
    {
        public int Id { set; get; }
        public string Body { set; get; }

        [ForeignKey("ProjectStatusId")]
        public virtual ProjectIssueStatus ProjectIssueStatus { set; get; }
        public int ProjectStatusId { set; get; }

        [ForeignKey("ProjectId")]
        public virtual Project Project { set; get; }
        public int ProjectId { set; get; }
    }

    public class ProjectIssueStatus
    {
        public int Id { set; get; }
        public string Name { set; get; }

        public virtual ICollection<ProjectIssue> ProjectIssues { set; get; }
    }
یک پروژه می‌تواند تعدادی Issue ثبت شده داشته باشد. هر Issue نیز دارای وضعیتی مشخص است.
اگر EF Code first را وادار به تهیه جداول و روابط معادل کلاس‌های فوق کنیم:
    public class MyContext : DbContext
    {
        public DbSet<ProjectIssueStatus> ProjectStatus { get; set; }
        public DbSet<ProjectIssue> ProjectIssues { get; set; }
        public DbSet<Project> Projects { get; set; }
    }

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

        protected override void Seed(MyContext context)
        {
            var project1 = new Project { Name = "پروژه جدید" };
            context.Projects.Add(project1);

            var stat1 = new ProjectIssueStatus { Name = "درحال انجام" };
            var stat2 = new ProjectIssueStatus { Name = "انجام شد" };
            context.ProjectStatus.Add(stat1);
            context.ProjectStatus.Add(stat2);

            var issue1 = new ProjectIssue
            {
                Body = "تغییر قلم گزارش",
                ProjectIssueStatus = stat1,
                Project = project1
            };
            var issue2 = new ProjectIssue
            {
                Body = "تغییر لوگوی گزارش",
                ProjectIssueStatus = stat1,
                Project = project1
            };
            context.ProjectIssues.Add(issue1);
            context.ProjectIssues.Add(issue2);

            base.Seed(context);
        }
    }
 به شکل زیر خواهیم رسید:


سابقا برای یافتن تعداد متناظر با هر IssueStatus خیلی سریع می‌شد چنین کوئری را نوشت:


اما اکنون معادل آن با EF Code first چیست؟
    public static class Test
    {
        public static void RunTests()
        {
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Configuration>());

            using (var ctx = new MyContext())
            {
                var projectId = 1;
                var list = ctx.ProjectStatus.Select(x => new
                                                    {
                                                        Id = x.Id,
                                                        Name = x.Name,
                                                        Count = x.ProjectIssues.Count(p => p.ProjectId == projectId)
                                                    }).ToList();
                foreach (var item in list)
                    Console.WriteLine("{0}:{1}",item.Name, item.Count);                
            }
        }
    }
بله. همانطور که ملاحظه می‌کنید در اینجا به کوئری بسیار ساده و واضحی با کمک استفاده از navigation properties (خواص راهبری مانند ProjectIssues) تعریف شده رسیده‌ایم. خروجی SQL تولید شده توسط EF نیز به صورت زیر است:
SELECT [Project1].[Id] AS [Id],
       [Project1].[Name] AS [Name],
       [Project1].[C1] AS [C1]
FROM   (
           SELECT [Extent1].[Id] AS [Id],
                  [Extent1].[Name] AS [Name],
                  (
                      SELECT COUNT(1) AS [A1]
                      FROM   [dbo].[ProjectIssues] AS [Extent2]
                      WHERE  ([Extent1].[Id] = [Extent2].[ProjectStatusId])
                             AND ([Extent2].[ProjectId] = 1 /*@p__linq__0*/)
                  ) AS [C1]
           FROM   [dbo].[ProjectIssueStatus] AS [Extent1]
       ) AS [Project1]

مطالب
آموزش Cache در ASP.NET Core - (قسمت اول : مفاهیم اولیه)
امروزه در وب‌سایت‌های شخصی و تجاری، یکی از مهم‌ترین پارامتر‌ها، سرعت پاسخگویی درخواست‌ها به وب‌سایت است. طبق آمار، کاربران آنلاین کنونی که ما با آن‌ها طرفیم، سطح تحملشان به سه ثانیه در یک صفحه میرسد؛ پس ما باید بتوانیم سرعت وب‌سایت‌های خودمان را تا حد ممکن بهبود بخشیم. از طرفی پارامتر سرعت، روی سئو گوگل هم تاثیر بسزایی دارد و Ranking وب‌سایت شمارا تا حد زیادی افزایش می‌دهد. قطعا همه می‌دانید که سرعت وبسایت و برنامه چقدر مهم هست؛ پس زیاده گویی نمی‌کنیم و می‌رویم سراغ اصل مطلب.
یکی از کارهایی که میتوانیم برای افزایش سرعت برنامه انجام دهیم، استفاده از Cache هست. بطور خیلی ساده، Cache یعنی قرار دادن دیتای پرکاربرد، در یک حافظه‌ی نزدیک‌تر از دیتابیس که هروقت به آن نیاز داشتیم، به آن دسترسی سریعی داشته باشیم و سرعت واکشی اطلاعات، از سرعتی که دیتابیس به ما می‌دهد، بیشتر باشد تا درخواست‌های ما با پاسخ سریع‌تری همراه شوند.
این حافظه، Ram هست و عمل Caching به اینصورت خواهد بود که هر وقت دیتای مورد نظر یکبار از دیتابیس واکشی شود، از دفعات بعد، آن دیتا را در Ram ذخیره میکند و برای درخواست‌های بعدی به دیتابیس Query نمیزند و دیتای مورد نیازش را از Ram میگیرد.
این امر در کنار مزایایی که دارد ، حساسیت بالایی هم بهمراه خواهد داشت؛ چرا که حافظه مورد استفاده Ram، یک حافظه محدود هست همچنین میتواند برای هر سخت افزاری متفاوت باشد. پس پیاده سازی این سیستم نیاز به دو دو تا چهارتا و ساختار درست دارد؛ در غیر اینصورت Cache کردن دیتای غلط میتواند به تنهایی وب‌سایتتان را Down کند؛ پس خیلی باید به این موضوع دقت داشت.

چه زمانی بهتر است از کش استفاده کنیم؟
  • وقتی دیتایی داریم که به تکرار از آن در برنامه استفاده میکنیم.
  • وقتی بعد از گرفتن دیتایی از دیتابیس، محاسباتی بر روی آن انجام میدهیم و پاسخ نهایی محاسبه را به کاربر نمایش میدهیم، میتوانیم یکبار پاسخ را کش کنیم تا از محاسبه‌ی هر باره‌ی آن جلوگیری شود.

آیا تمام اطلاعات را میتوان کش کرد؟
خیر.
  • سخت افزاری که برای کش استفاده میکنیم یعنی Ram، بسیار گران‌تر از دیتابیس برای ما تمام میشود؛ چرا که محدود است.
  • اگر همه دیتاهارا کش کنید، عمل سرچ میان آن زمان بیشتری خواهد برد.
پس اکنون میدانید که میتوانیم داده‌های بی نهایتی را در دیتابیس ذخیره کنیم و فقط با ارزش‌ترین‌ها و پر مصرف‌ترین هارا در حافظه کش، ذخیره میکنیم.

عملیات Cache در Asp.Net Core توسط اینترفیس‌های IMemoryCache و IDistributedCache مدیریت میشود و میتوانید با تزریق این اینترفیس‌ها براحتی از متدهایشان استفاده کنید؛ اما قبل از استفاده لازم است با عملکرد هر یک از آن‌ها آشنا شویم.

روش اول : In-memory Caching (Local Caching)
معمول‌ترین و ابتدایی‌ترین روش برای کش کردن اطلاعات، روش Local Caching و بصورت In-Memory است که اطلاعات را در حافظه Ram همان سروری که برنامه در آن اجرا میشود، کش میکند.

این روش تا زمانیکه برنامه‌ی ما برای اجرا شدن، تنها از یک سرور استفاده کند، بهترین انتخاب خواهد بود؛ چرا که به دلیل نزدیک بودن، سریع‌ترین بازخورد را نیز به درخواست‌ها ارائه میدهد.


اما شرایطی را فرض کنید که برنامه از چندین سرور برای اجرا شدن استفاده میکند و به طبع هر سرور درخواست‌های خودش را داراست که ما باید برای هر یک بصورت جداگانه‌ای یک کش In-Memory را در حافظه Ram هرکدام ایجاد کنیم. 

فرض کنید دیتای ما 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 باشد. بخشی از دیتا در Server 1 کش میشود (1 , 3 , 5 , 9) و بخشی دیگر در Server 2 کش خواهد شد (2 , 4 , 6 ,7 , 8 , 10).


در اینجا مشکلات و ضعف هایی به وجود خواهد آمد :


  • برای مثال اگر Server 1 به هر دلیلی از بین برود یا Down شود، اطلاعات کش درون آن نیز پاک خواهد شد و بعد از راه اندازی باید همه آن را دوباره از دیتابیس بخواند.
  • هر کدام از سرور‌ها کش‌های جدایی دارند و باهم Sync نیستند و امکان وجود یک داده‌ی حیاتی در یکی و عدم وجود آن در دیگری، بالاست. فرض کنید برنامه برای هر درخواست، نیاز به اطلاعات دسترسی کاربری را دارد. دسترسی‌های کاربر، در Server 1 کش شده، اما در Server 2 موجود نیست. در Server 2 به دلیل عدم وجود این کش، برنامه برای درخواست‌های معمول خود و چک کردن دسترسی کاربر یا باید هربار به دیتابیس درخواستی را ارسال کند که این برخلاف خواسته ماست و یا باید دیتای مربوط به دسترسی‌های کاربر را بعد از یکبار درخواست، از دیتابیس در خودش کش کند که این‌هم دوباره کاری به حساب میاید و دوبار کش کردن یک دیتا، امر مطلوبی نخواهد بود.

روش هایی وجود دارد که بتوان از سیستم Local Caching در حالت چند سروری هم استفاده کرد و این مشکلات را از بین برد، اما روش استاندارد در حالت چند سروری، استفاده از Distributed Cache‌ها است.


روش دوم : Distributed Caching

در این روش برنامه‌ی ما برای اجرا شدن از چندین سرور شبکه شده به هم، در حال استفاده هست و Cache برنامه، توسط سرورها به اشتراک گذاشته شده. 

در این حالت سرور‌های ما از یک کش عمومی استفاده میکنند که مزایای آن شامل :

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

■ با خراب شدن یا Down شدن یک سرور، کش موجود در سرور‌های دیگر پاک نمیشود و کماکان قابل استفاده است.

■ به حافظه Ram یک سرور محدود نیست و مشکلات زیادی همچون کمبود سخت افزاری و محدودیت‌های حافظه‌ی Ram را تا حد معقولی کاهش میدهد.


طریقه استفاده از Cache در Asp.Net Core :

  • بر خلاف ASP.NET web forms و ASP.NET MVC در نسخه‌های Core به بعد، Cache بصورت از پیش ثبت شده، وجود ندارد. کش در Asp.Net Core با فراخوانی سرویس‌های مربوطه‌ی آن قابل استفاده است و نیاز است قبل از استفاده، سرویس آن را در کلاس Startup برنامه فراخوانی کنید. 
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddMemoryCache();
}

  • اینترفیس IMemoryCache از سیستم تزریق وابستگی‌ها در Core استفاده میکند و برای استفاده از اینترفیس آن، پس از اضافه کردن MemoryCache به Startup ، باید در کنترلر، عمل تزریق وابستگی (DI) را انجام دهید؛ سپس متد‌های مورد نیاز برای کش، در دسترس خواهد بود. 
public class HomeController : Controller
{
    private readonly IMemoryCache  _cache;
    public HomeController(IMemoryCache  cache)
    {
        _cache = cache;
    }
    ....
}

  • برای ذخیره‌ی کش میتوانید از متد Set موجود در این اینترفیس استفاده کنید. 
public IActionResult Set()
{
  _cache.Set("CacheKey", data , TimeSpan.FromDays(1));
  return View();
}

در پارامتر اول این متد (CacheKey)، یک کلید، برای اطلاعاتی که میخواهیم کش کنیم قرار میدهیم. دقت کنید که این کلید، شناسه‌ی دیتای شماست و باید طوری آن را در نظر گرفت که با صدا زدن این کلید از سرویس کش، همان دیتای مورد نظر را برگشت دهد (هر Object دیتا، باید کلید Unique خود را داشته باشد).


در پارامتر دوم، دیتای مورد نظر را که میخواهیم کش کنیم، به متد میدهیم و در پارامتر سوم نیز زمان اعتبار و تاریخ انقضای دیتای کش شده را وارد میکنیم؛ به این معنا که دیتای کش شده، بعد از مدت زمان گفته شده، از حافظه کش(Ram) حذف شود و برای دسترسی دوباره و کش کردن دوباره اطلاعات، نیاز به خواندن مجدد از دیتابیس باشد.


  • برای دسترسی به اطلاعات کش شده میتوانید از متد Get استفاده کنید. 
public IActionResult Get()
{
  string data = _cache.Get("CacheKey");
  return View(data);
}

تنها پارامتر ورودی این متد، کلید از قبل نسبت داده شده به اطلاعات کش هست که با استفاده از یکسان بودن کلید در ورودی این متد و کلید Set شده از قبل در حافظه Ram، دیتا مربوط به آن را برگشت میدهد.


  • متد TryGetValue برای بررسی وجود یا عدم وجود یک کلید در حافظه کش هست و یک Boolean را خروجی میدهد. 
public IActionResult Set()
  {
        DateTime data;
       // Look for cache key.
       if (!_cache.TryGetValue( "CacheKey" , out data))
       {
              // Key not in cache, so get data.
              data= DateTime.Now;

            // Save data in cache and set the relative expiration time to one day
             _cache.Set( "CacheKey" , data, TimeSpan.FromDays(1));
        }
        return View(data);
  }

این متد ابتدا بررسی میکند که کلیدی با نام "CacheKey" وجود دارد یا خیر؟ در صورت عدم وجود، آن را میسازد و دیتای مورد نظر را به آن نسبت میدهد.


  • با استفاده از متد GetOrCreate میتوانید کار متد‌های Get و Set را باهم انجام دهید و در یک متد، وجود یا عدم وجود کش را بررسی و در صورت وجود، مقداری را return و در صورت عدم وجود، ابتدا ایجاد کش و بعد return مقدار کش شده را انجام دهید. 
 public IActionResult GetOrCreate()
{
         var data = _cache.GetOrCreate( "CacheKey" , entry =>
         entry.SlidingExpiration = TimeSpan.FromSeconds(3);
         return View(data);
});
    return View(data);
}

  • برای مدیریت حافظه‌ی Ram شما باید یک Expiration Time را برای کش‌های خود مشخص کنید؛ تا هم حافظه Ram را حجیم نکنید و هم در هر بازه‌ی زمانی، دیتای بروز را از دیتابیس بخوانید. برای این کار option‌های متفاوتی از جمله absolute expiration و sliding expiration وجود دارند.

در اینجا absolute expiration به این معنا است که یک زمان قطعی را برای منقضی شدن کش‌ها مشخص میکند؛ به عبارتی میگوییم کش با کلید فلان، در تاریخ و ساعت فلان حذف شود. اما در sliding expiration یک بازه زمانی برای منقضی شدن کش‌ها مشخص میکنیم؛ یعنی میگوییم بعد از گذشت فلان دقیقه از ایجاد کش، آن را حذف کن و اگر در طی این مدت مجددا خوانده شد، طول مدت زمان آن تمدید خواهد شد.

این تنظیمات را میتوانید در قالب یک option زمان Set کردن یک کش، به آن بدهید. 

MemoryCacheEntryOptions options = new MemoryCacheEntryOptions();
options.AbsoluteExpiration = DateTime.Now.AddMinutes(1);
options.SlidingExpiration = TimeSpan.FromMinutes(1);
_cache.Set("CacheKey", data, options );

در مثال بالا هردو option اضافه شده یک کار را انجام میدهند؛ با این تفاوت که absolute expiration تاریخ now را گرفته و یک دقیقه بعد را به آن اضافه کرده و تاریخ انقضای کش را با آن تاریخ set میکند. اما sliding expiration از حالا بمدت یک دقیقه اعتبار دارد.


  • یکی از روش‌های مدیریت حافظه Ram در کش‌ها این است که برای حذف شدن کش‌ها از حافظه، اولویت بندی‌هایی را تعریف کنید. اولویت‌ها در چهار سطح قابل دسترسی است: 

  1.  NeverRemove = 3
  2.  High = 2
  3. Normal = 1
  4.  Low = 0 

این اولویت بندی‌ها زمانی کاربرد خواهند داشت که حافظه اختصاصی Ram، برای کش‌ها پر شده باشد و در این حالت سیستم کشینگ بصورت خودمختار، کش‌های با الویت پایین را از حافظه حذف میکند و کش‌های با الویت بیشتر، در حافظه باقی میمانند. این با شماست که الویت را برای دیتا‌های خود تعیین کنید؛ پس باید با دقت و فکر شده این کار را انجام دهید. 

MemoryCacheEntryOptions options = new MemoryCacheEntryOptions();
// Low / Normal / High / NeverRemove
options.Priority = CacheItemPriority.High;
cache.Set("CacheKey", data, options);

به این صورت میتوانید الویت‌های متفاوت را در قالب option به کش‌های خود اختصاص دهید. 

در این مقاله سعی شد مفاهیم اولیه Cache، طوری گفته شود، تا برای افرادی که میخواهند به تازگی این سیستم را بیاموزند و در پروژه‌های خود استفاده کنند، کاربردی باشد و درک نسبی را نسبت به مزایا و محدودیت‌های این سیستم بدست آورند.


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

مطالب
#Defensive Code in C - قسمت چهارم
 Automated Code Test

با توجه به فاکتور‌های موجود در Defensive Coding، یکی از مواردی که کیفیت کد شما را تضمین خواهد کرد، استفاده کردن از (ACT) Automated Code Test می‌باشد. در این قسمت قصد داریم مزایای تست اتوماتیک و Unit Test را به عنوان یکی دیگر از ابعاد Defensive Coding ذکر کنیم.


به عنوان برنامه نویسی که در حال توسعه کد هستید و قابلیت‌های جدیدی را به کد خود اضافه می‌کنید، باید کدی را که در آن تغییر ایجاد می‌کنید، مرتب تست کنید، تا بررسی کنید که آیا به هدف خود رسیده‌اید و توانسته‌اید قابلیت جدیدی را در نرم افزار اضافه کنید. روشی که توسط اکثر برنامه نویسان انجام می‌شود به این صورت است که به‌صورت متوالی برنامه را اجرا می‌کنند، اجرا می‌کنند، اجرا می‌کنند و باز هم اجرا می‌کنند، تا اینکه مطمئن شوند همه چیز درست است. بیشتر زمانیکه در این فرآیند صرف می‌شود، صرف کارهای تکراری می‌شود. این مسئله بدون شک برای بسیار از برنامه نویسان پیش آمده است و ممکن است بعضی از این برنامه نویسان به دنبال راه چاره‌ای بوده باشند و بعضی‌ها هم با انجام دادن این روش تست، هیچ مشکلی نداشته باشند. اما مسئله‌ای که غیر قابل چشم پوشی است، این است که این روش انرژی و زمان بسیار زیادی را از برنامه نویسان می‌گیرد؛ راه حل چیست؟ راه حل همان ACT می‌باشد.

ACT به معنی نوشتن کد، جهت تست قابلیت‌های نرم افزار می‌باشد. به این معنی که شما جهت تست کد خود، یکسری کد می‌نویسید که این کدها وظیفه دارند کد‌های جدیدی را که به نرم افزار خود اضافه کرده اید، تست کنند و اجرای آنها توسط زیرساخت‌های موجود (Test Frameworks) به صورت اتوماتیک انجام می‌شود.

حال قصد داریم اجزای ACT را که در شکل ذیل نمایش داده شده‌اند، تشریح کنیم.


· structured: برای بیان این مسئله، از مفهوم AAA استفاده می‌شود. A اول به معنی Arrange اطلاعاتی است که برای تست مورد نیاز است. A دوم به معنی Act یا اجرای متد در حالت تست است و A سوم بمعنی Assert یا بررسی نتایج تست می‌باشد. این ساختار، ساختاری است که در ادامه برای ایجاد تست‌ها از آن استفاده می‌کنیم.

· Self-documented: ساختار تست به گونه‌ای است که خود مستند می‌باشد و با بررسی کلی ساختار آن می‌توان به هدف تست پی برد.

· Automatic: با استفاده از Test Framework ها، فرآیند تست اتوماتیک می‌شود.

· Repeatable: یکی از مزیت‌های ACT این است که می‌توان آن را برای دفعات مکرر تکرار کرد.

· TARDIS: مخفف  Time And Relative Dimension In Space می‌باشد؛ با توجه به این مسئله ACT از کد شما در میان زمان و فضا محافظت می‌کند. ACT  عملکرد اصلی کد شما را در حال حاضر و در زمانی در آینده  تایید می‌کند؛ زمانیکه کد شما در حال توسعه می‌باشید و هر لحظه قابلیت‌های جدیدی به آن اضافه می‌شود، ACT تضمین می‌کند که این تغییرات، قابلیت‌های موجود در سیستم را تحت تاثیر قرار نمی‌دهند. بنابراین ACT از کد شما در مقابل زمان و فضا محافظت می‌کند.

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

برای رسیدن به این هدف باید کد را به صورت متدهای Clean و Testable نوشت. این متد‌ها قسمت‌های مستقلی از کد هستند که می‌توانند تست شوند. همان طور که در شکل زیر مشاهده می‌کنید، برای هر متد می‌توان تست‌های مختلفی نوشت و حالت‌های مختلف مربوط به ورودی‌های معتبر، ورودی‌های نامعتبر و بروز Exception را تست کرد.


بسیاری از برنامه نویسان و مدیران  پروژه درمقابل مسئله استفاده از Unit Test در توسعه نرم افزار حساسیت‌های خاصی نشان می‌دهند. بسیاری از آنها اظهار می‌کنند که برای این کار زمان کافی نداریم و استفاده کردن از این روش برای ما هزینه بر می‌باشد. اما ما در جواب این دسته از افراد باید موارد زیر را که بیشتر هم بر جنبه زمانی تاکید دارند، بیان کنیم.


· Save time:
  استفاده کردن از Unit Test از هدر رفتن زمان شما جلوگیری می‌کند. هر برنامه نویسی می‌داند که حتی چند خط کد ساده هم نیاز به تست و باز بینی دارد. بنابراین برنامه نویس مجبور است آن ماژول اصلی از نرم افزار را که چند خط کد در آن نوشته است، به گونه ای اجرا کرده و فرآیند بیزینسی این ماژول را برای سناریو‌های مختلف، بصورت دستی تست کند. حال فرض کنید در ادامه‌ی این کار، شخص برنامه نویس مجبور شود کد را بطور مرتب تغییر دهد. بنابراین در این حالت مجبور است این فرآیند را چندین و چند بار تکرار کند (نرم افزار را اجرا کند، به منوی X برود، فرم Y را باز کند، حالت‌های مختلف را در فرم بررسی کند).
راه حلی که Unit Test برای حل این مشکل ارئه می‌دهد این است که برای انجام این فرآیند می‌توان کد نوشت و آن را بارها و بارها اجرا کرد. وظیفه‌ی Unit Test ها این است که اطلاعات مورد نیاز متد یا واحدی که می‌خواهند آن را تست کنند، فرآهم می‌آورند، متد را با اطلاعات فرآهم شده زیر تست می‌برند و سپس نتایج بدست آمده را بررسی خواهند کرد. شما می‌توانید در صورت تغییراتی در متد‌ها یا واحد‌ها، Unit  Test را بارها و بار‌ها برای تست عملکرد صحیح آن متد، بعد از تغییرات اجرا کنید. همان طور که می‌بینید تبدیل کردن این فرآیند دستی به یک فرآیند سیستمی و اتوماتیک می‌تواند در جلوگیری از هدر رفت زمان بسیار تاثیر گذار باشد.

· Find Bugs Faster:
با استفاده از Unit Test شما می‌توانید فرآیند پیدا کردن خطاها را بسیار سریعتر انجام دهید. برای مثال فرض کنید که شما گزارش یکسری خطا‌ها را در نرم افزار، دریافت کرده‌اید. به جای اینکه سعی کنید بصورت دستی، فرآیند‌ها را در نرم افزار مرور کنید تا دوباره شرایط بروز خطا یا شرایطی را که خطا در آن رخ داده است، جهت درک دلیل خطا یا خطا‌ها ایجاد کنید،  با استفاده از Unit Test می‌توانید به راحتی و در سریع‌ترین زمان ممکن و بصورت اتوماتیک خطا‌ها را پیدا کنید.

· Refactor Safely:
Unit Test به شما اجازه می‌دهد که به راحتی کد خود را Refactor کنید. فرض کنید که می‌خواهید کدی را که دارای یکسری پیچیدگی‌ها می‌باشد و نگهداری و توسعه آن سخت است، Refactor کنید. بدون استفاده از Unit Test، این Refactor کردن دارای ریسک بسیار زیادی است و ممکن است منجر به بروز خطاهای زیادی شود؛ در حالیکه با استفاده از Unit Test، بعد از Refactor کردن کد، می‌توان Test ها را اجرا کرده و از عدم وجود خطا در کدها به راحتی مطمئن شد.

· Enhance Your Value:
با نوشتن Unit Test برای کد‌های خود می‌توانید یک ارزش افزوده را به کد‌های خود اضافه کنید. به دلیل اینکه نوشتن Unit Test ویژگی Self-documented کد شما را افزایش می‌دهد و به افرادی که در تیم هستند کمک می‌کنند Business نرم افزار را بهتر درک کنند.

· Minimize Interruptions:
داشتن مجموعه‌ای مناسب از Unit Test ها باعث می‌شود تا Interrupt های ناخواسته در Code شما بوجود نیاید. برای مثال حالتی را در نظر بگیرید که بدلیل ورود داده‌های ناخواسته، نرم افزار دچار خطا می‌شود. دراین وضعیت در صورتیکه از Unit Test استفاده شود، هندل کردن این شرایط ناخواسته و Interrupt، بسیار راحت‌تر خواهد بود.
مطالب
تهیه بک آپ‌های خودکار از SQL Server Express

SQL Server express edition نگارش مجانی و ساده شده‌ی اس کیوال سرور است. این نگارش مجانی فاقد SQL Server agent برای زمان بندی انجام امور تکراری، برای مثال تهیه بک آپ‌های خودکار است. این مورد در کل ایرادی محسوب نمی‌شود زیرا می‌توان این عملیات را با استفاده از سیستم استاندارد scheduled tasks ویندوز نیز پیاده سازی کرد.
برنامه خط فرمان سورس بازی به نام ExpressMaint موجود است که می‌تواند از دیتابیس‌های اس کیوال سرور اکسپرس (و غیر اکسپرس) بک آپ تهیه کند. فقط کافی است این برنامه را به عنوان یک scheduled task ویندوز معرفی کنیم تا در زمان‌های تعیین شده در مکان‌هایی مشخص، بک آپ تهیه کند. همچنین این برنامه فایل‌های بک آپ تهیه شده را نیز تعیین اعتبار می‌کند.
با پارامترهای خط فرمان آن در این‌جا می‌توانید آشنا شوید. خلاصه کاربردی آن را به صورت چند دستور در ادامه مرور خواهیم کرد.

الف) یک فایل bat را با محتوای زیر درست کنید :

C:\backup\expressmaint.exe -S (local)\sqlexpress -D ALL_USER -T DB -R C:\backup -RU WEEKS -RV 2 -B C:\backup -BU DAYS -BV 2 -V -C
توضیحات: در این فایل bat ، مسیر فایل اجرایی برنامه حتما باید دقیقا ذکر شود و گرنه scheduled task ویندوز درست کار نخواهد کرد. همچنین instance اس کیوال سرور اکسپرس در اینجا (local)\sqlexpress فرض شده است. این دستور از تمامی دیتابیس‌های غیرسیستمی در مسیر C:\backup بک آپ می‌گیرد (به ازای هر دیتابیس یک پوشه مجزا درست خواهد کرد و هر فایل را بر اساس تاریخ و ساعت مشخص می‌سازد). همچنین لاگ عملیات را نیز در همان پوشه تهیه می‌کند (نتیجه اعتبار سنجی صورت گرفته بر روی بک آپ‌های تهیه شده در این فایل‌ها ثبت می‌شود). مطابق پارامترهای بکار گرفته شده، بک آپ‌های قدیمی‌تر از دو روز به صورت خودکار حذف شده و لاگ فایل‌ها به مدت 2 هفته نگهداری می‌شوند.

ب) برای اجرای زمان بندی شده‌ی این فایل bat تهیه شده، دستورات زیر را در خط فرمان اجرا کنید (فرض بر این است که فایل bat تهیه شده در مسیر مشخص شده C:\backup\backup.bat قرار دارد) :

AT 23:30 /EVERY:m,t,w,th,f,s,su C:\backup\backup.bat
AT 11:30 /EVERY:m,t,w,th,f,s,su C:\backup\backup.bat
به این صورت ویندوز هر روز، دوبار در طول روز از کلیه دیتابیس‌ها به صورت خودکار بک آپ تهیه می‌کند.

روشی که در این‌جا ذکر شد منحصر به نگارش express نیست و با کلیه نگارش‌های SQL Server سازگار است.


مطالب
چگونه کدها را مستند سازی کنیم؟
یکی از مهمترین مسائل، به خصوص در کارهای تیمی یا پروژه‌های اشتراکی، قرار دادن کامنت‌ها یا اصطلاحا مستند نویسی است که بسیاری از برنامه نویسان با اینکه نظریه آن‌را به شدت قبول دارند، ولی از انجام آن سرباز می‌زنند که به دو عامل تنبلی و عدم دانش نحوه‌ی مستند نویسی بر می‌گردد. در این مقاله قصد داریم به سوالات زیر پاسخ دهیم:
  • چرا به کامنت گذاری یا مستند نویسی نیاز داریم؟
  • چگونه کامنت بنویسیم؟
  • انواع کامنت‌ها چیست؟
  • چه کامنت‌هایی اشتباه هستند؟

همانطور که بیان کردیم، کامنت گذاری یکی از مهم‌ترین کارهایی است که یک برنامه نویس انجام می‌دهد. به خصوص زمانیکه به صورت تیمی کار می‌کنید، این امر مهم‌تر از قبل خود را نشان می‌دهد. بسیاری از برنامه نویسان که بیشتر دلیل آن تنبلی است، از این کار سرباز می‌زنند و ممکن است آن را اتلاف وقت بدانند. ولی با کامنت گذاری فهم و درک کد، در آینده بالا‌تر می‌رود. در مقاله‌ی تخصصی «هنر کامنت نویسی» نوشته‌ی «برنهارد اسپویدا» بهانه‌های جالبی از برنامه نویسان را برای سرباز زدن از اینکار، ذکر شده است؛ به عنوان نمونه:
من کدم را به خوبی متوجه می‌شوم.
کد خوب، خودش گویای همه چیز هست.
وقتی برای کامنت نویسی وجود ندارد. باید چسبید به کد.


سوالات زیر نیز دلیل این را که چرا باید کامنت گذاری کرد، مشخص می‌کنند:
  • شاید امروز معنای یک کد را متوجه شوید، ولی آیا در آینده، مثلا یک سال بعد هم چنین خواهد بود؟
  •  آیا می‌توانید هر سیستمی را که طراحی می‌کنید، به خاطر بسپارید که فعالیت‌هایش را به چه نحوی انجام می‌دهد؟
  • اگر در یک کار تیمی باشید، شاید شما متوجه کدتان می‌شوید، ولی آیا تضمینی وجود دارد که دیگران هم متوجه روش شما شوند؟
  • آیا کدی که شما بر روی آن فکر کرده‌اید و در ذهن خود روش انجام آن را ترسیم کرده‌اید، می‌تواند برای برنامه نویسی که کد شما را می‌بیند هم رخ دهد؟ 
  • اگر شما به صورت تیمی کاری را انجام دهید و یکی از برنامه نویس‌های شما از تیم جدا شود، چگونه می‌توانید کار او را دنبال کنید؟
  • اگر برای برنامه نویسی اتفاق یا حادثه‌ای پیش بیاید که دسترسی شما به او ممکن نباشد چه؟
کدی که مستند نشود، یا خوب مستند نشود، در اولین مرحله وقت شما یا هر فردی را که روی این کد کار می‌کند، برای مدتی طولانی می‌گیرد. در مرحله‌ی بعدی احتمالا مجبور هستید، کد را خط به خط دنبال کرده تا تاثیر آن را بر ورودی‌ها و خروجی‌ها ببینید. تمام این‌ها باعث هدر رفتن وقت شما شده و ممکن است این اتفاق برای هر تکه کدی رخ بدهد.


سطوح کامنت نویسی بر سه نوع هستند:

Documentary Comments:
این مستند سازی در سطح یک سند مثل فایل یا به خصوص یک پروژه رخ می‌دهد که شامل اطلاعات و تاریخچه‌ی آن سند است که این اطلاعات به شرح زیر هستند:

 File Name   نام سند
 File Number/Version Number   شماره نسخه آن سند
 Creation Date   تاریخ ایجاد آن
 Last Modification Date
 تاریخ آخرین تغییر سند
 Author's Name
 سازنده‌ی سند
 Copyright Notice
 اطلاعاتی در مورد کپی رایت سند
 Purpose Of Program
 هدف کاری برنامه. یک خلاصه از آن چه برنامه انجام می‌دهد.
 Change History
 لیستی از تغییرات مهمی که در جریان ایجاد آن رخ داده است.
 Dependencies  وابستگی‌های سند. بیشتر در سطح پروژه معنا پیدا می‌کند؛ مانند نمونه‌ی آن برای سایت جاری که به صورت عمومی منتشر شده است.
 Special Hardware Requirements
 سخت افزار مورد نیاز برای اجرای برنامه. حتی قسمتی می‌تواند شامل نیازمندی‌های نرم افزاری هم باشد.
نمونه ای از این مستند سازی برای برنامه ای که به زبان پاسکال نوشته شده است:
PCMBOAT5.PAS*************************************************************
**
File: PCMBOAT5.PAS
Author: B. Spuida
Date: 1.5.1999
Revision: 
1.1 
PCM-DAS08 and -16S/12 are supported.
Sorting routine inserted.
Set-files are read in and card as well as
amplification factor are parsed.

1.1.1
Standard deviation is calculated.

1.1.2
Median is output. Modal value is output.

1.1.4
Sign in Set-file is evaluated.
Individual values are no longer output.
(For tests with raw data use PCMRAW.EXE)

To do:
outliers routine to be revised.
Statistics routines need reworking.
Existing Datafile is backed up.

Purpose: 
Used for measurement of profiles using the
Water-SP-probes using the amplifier andthe PCM-DAS08-card, values are acquired
with n = 3000. Measurements are taken in 1
second intervals. The values are sorted using
Quicksort and are stacked "raw" as well as after
dismissing the highest and lowest 200 values as
'outliers'.


Requirements:
The Card must have an A/D-converter.
Amplifier and probes must be connected.
Analog Signal must be present.
CB.CFG must be in the directory specified by the
env-Variable "CBDIREC" or in present directory.

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


Functional Comments: کامنت نویسی در سطح کاربردی به این معنی نیست که شما اتفاقاتی را که در یک متد یا کلاس یا هر بخشی روی می‌دهد، خط به خط توضیح دهید؛ بلکه چرخه‌ی کاری آن شی را هم توضیح بدهید کفایت می‌کند. این مورد می‌تواند شامل این موارد باشد:
  • توضیحی در مورد باگ‌های این قسمت
  • یادداشت گذاری برای دیگر افراد تیم
  • احتمالاتی که برای بهبود ویژگی‌ها و کارایی کد وجود دارد.


Explanatory Comment: کامنت گذاری توصیفی در سطح کدنویسی رخ می‌دهد و شامل توضیح در مورد کارکرد یک شیء و توضیح کدهای شیء مربوطه می‌گردد. برای قرار دادن کامنت الزامی نیست که کدها را خط به خط توضیح دهید یا اینکه خطوط ساده را هم تشریح کنید؛ بلکه کامنت شما همینقدر که بتواند نحوه‌ی کارکرد هر چند خط کد مرتبط به هم را هم توضیح دهد، کافی است. این توضیح‌ها بیشتر شامل موارد زیر می‌شوند:

  • کدهای آغازین
  • کدهای خروجی
  • توضیح کوتاه از آنچه که این شیء ، متد یا ... انجام می‌دهد. 
  • حلقه‌های طولانی یا پیچیده
  • کدهای منطقی عجیب و پیچیده
  • Regular Expression

کدهای آغازین شروع خوبی برای تمرین خواهند بود. به عنوان نمونه اینکه توضیحی در مورد ورودی و خروجی یک متد بدهید که آرگومان‌های ورودی چه چیزهایی هستند و چه کاربری داردند و در آغاز برنامه، برنامه چگونه آماده سازی و اجرا می‌شود. مقادیر پیش فرض چه چیزهایی هستند و پروژه چگونه تنظیم و مقداردهی می‌شود.

کدهای خروجی هم به همین منوال است. خروجی‌های نرمال و غیرنرمال آن چیست؟ کدهای خطایی که ممکن است برگرداند و ... که باید به درستی توضیح داده شوند.

توضیح اشیاء و متدها و ... شامل مواردی چون: هدف از ایجاد آن، آرگومان هایی که به آن پاس می‌شوند و خروجی که می‌دهد و اینکه قالب یا فرمت آن‌ها چگونه است و چه محدودیت‌هایی در مقادیر قابل انتظار وجود دارند. ذکر محدودیت‌ها، مورد بسیاری مهمی است و دلیل بسیاری از باگ‌ها، عدم توجه یا اطلاع نداشتن از وجود این محدودیت هاست. مثلا محدوده‌ی خاصی برای آرگومان‌های ورودی وجود دارد؟ چه اتفاقی می‌افتد اگر به یک بافر 128 کاراکتری، 1024 کاراکتر را ارسال کنیم؟

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

سیستم کامنت گذاری
هر زبانی از یک سیستم خاص برای کامنت گذاری استفاده می‌کند. به عنوان مثال پرل از سیستم (POD (Plain Old Documentation استفاده می‌کند یا برای Java سیستم JavaDoc یا برای PHP از سیستم PHPDoc  (+ ) که پیاده سازی از JavaDoc می‌باشد استفاده می‌کنند. این سیستم برای سی شارپ استفاده از قالب XML است. کد زیر نمونه‌ای از استفاده از این سیستم است:
// XMLsample.cs
// compile with: /doc:XMLsample.xml
using System;
/// <summary>
/// Class level summary documentation goes here.</summary>
/// <remarks>
/// Longer comments can be associated with a type or member
/// through the remarks tag</remarks>
public class SomeClass
{
    /// <summary>
    /// Store for the name property</summary>
    private string myName = null;

    /// <summary>
    /// The class constructor. </summary>
    public SomeClass()
    {
        // TODO: Add Constructor Logic here
    }

    /// <summary>
    /// Name property </summary>
    /// <value>
    /// A value tag is used to describe the property value</value>
    public string Name
    {
        get
        {
            if (myName == null)
            {
                throw new Exception("Name is null");
            }
            return myName;
        }
    }

    /// <summary>
    /// Description for SomeMethod.</summary>
    /// <param name="s"> Parameter description for s goes here</param>
    /// <seealso cref="String">
    /// You can use the cref attribute on any tag to reference a type or member
    /// and the compiler will check that the reference exists. </seealso>
    public void SomeMethod(string s)
    {
    }
}

دستورات سیستم کامنت گذاری سی شارپ

در سایت جاری، دو مقاله زیر اطلاعاتی در رابطه با نحوه‌ی کامنت گذاری ارئه داده‌اند.
- در مقاله «زیباتر کد بنویسیم» چند مورد آن به این موضوع اختصاص دارد.
- مقاله «وادار کردن خود به کامنت نوشتن» گزینه‌ی کامنت گذاری اجباری در ویژوال استودیو را معرفی می‌کند.
مطالب
تهیه گزارشات Crosstab به کمک LINQ

در گزارشات Crosstab، ردیف‌های یک گزارش، تبدیل به ستون‌های آن می‌شوند؛ به همین جهت به آن‌ها Pivot tables هم می‌گویند.
برای مثال فرض کنید که قصد دارید گزارش تعداد ساعت کارکرد را به ازای هر پروژه در طول چند ماه تعیین کنید. گزارش متداول از این نوع اطلاعات، یک لیست بلند بالای بی‌مفهوم است. این گزارش تشکیل شده از صدها رکورد به ازای کارکنان مختلف در پروژه‌های مختلف و ... هیچ ارزش آماری خاصی ندارد. یک گزارش بدوی است. زمانیکه این گزارش را تبدیل به حالت crosstab می‌کنیم، اولین ستون فقط یک شماره پروژه خواهد بود و ستون‌های بعدی، مثلا نام ماه‌ها و مقادیر آن‌ها هم جمع کارکرد افراد بر روی یک پروژه مشخص.

مثال اول) تهیه گزارش Crosstab جمع هزینه‌های واحدهای مختلف به تفکیک ماه

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

using System;

namespace Pivot.Sample1
{
public class Expense
{
public DateTime Date { set; get; }
public string Department { set; get; }
public decimal Expenses { set; get; }
}
}

با توجه به این کلاس، یک منبع داده آزمایشی جهت تهیه گزارشات، می‌تواند به صورت زیر باشد:

using System;
using System.Collections.Generic;

namespace Pivot.Sample1
{
public class ExpenseDataSource
{
public static IList<Expense> ExpensesDataSource()
{
return new List<Expense>
{
new Expense { Date = new DateTime(2011,11,1), Department = "Computer", Expenses = 100 },
new Expense { Date = new DateTime(2011,11,1), Department = "Math", Expenses = 200 },
new Expense { Date = new DateTime(2011,11,1), Department = "Physics", Expenses = 150 },

new Expense { Date = new DateTime(2011,10,1), Department = "Computer", Expenses = 75 },
new Expense { Date = new DateTime(2011,10,1), Department = "Math", Expenses = 150 },
new Expense { Date = new DateTime(2011,10,1), Department = "Physics", Expenses = 130 },

new Expense { Date = new DateTime(2011,9,1), Department = "Computer", Expenses = 90 },
new Expense { Date = new DateTime(2011,9,1), Department = "Math", Expenses = 95 },
new Expense { Date = new DateTime(2011,9,1), Department = "Physics", Expenses = 100 }
};
}
}
}

و اگر این لیست را به همین شکلی که هست نمایش دهیم، خروجی زیر را خواهیم داشت:


که ... خروجی مطلوبی نیست. در اینجا ما فقط 9 رکورد داریم؛ اما در عمل به ازای هر روز، یک رکورد می‌تواند وجود داشته باشد و این لیست طولانی، هیچ ارزش آماری خاصی ندارد. می‌خواهیم سرستون‌های گزارش ما مطابق جدول زیر باشند:


یعنی اگر سه ماه را در نظر بگیریم با هر تعداد رکورد، فقط سه ردیف به ازای هر ماه باید حاصل شود و ستون‌های دیگر هم نام بخش‌ها یا واحدهای موجود باشند.
برای رسیدن به این خروجی Crosstab، می‌توان کوئری LINQ زیر را به کمک امکانات گروه بندی اطلاعات آن تهیه کرد:

using System.Collections;
using System.Linq;

namespace Pivot.Sample1
{
public class PivotTable
{
public static IList ExpensesCrossTab()
{
return ExpenseDataSource
.ExpensesDataSource()
.GroupBy(t =>
new
{
Year = t.Date.Year,
Month = t.Date.Month
})
.Select(myGroup =>
new
{
//Year = myGroup.Key.Year,
Month = myGroup.Key.Month,
ComputerDepartment = myGroup.Where(x => x.Department == "Computer").Sum(x => x.Expenses),
MathDepartment = myGroup.Where(x => x.Department == "Math").Sum(x => x.Expenses),
PhysicsDepartment = myGroup.Where(x => x.Department == "Physics").Sum(x => x.Expenses)
})
.ToList();
}
}
}

که اینبار خروجی زیر را تولید می‌کند.


اگر علاقمند باشید که مثال فوق را در برنامه‌ی LINQPad آزمایش کنید، این فایل را دریافت نموده و در آن برنامه باز نمائید.


مثال دوم) تهیه لیست Crosstab حضور و غیاب افراد در طول یک هفته

کلاس StudentStat را جهت ثبت اطلاعات حضور یک دانشجو، می‌توان به شکل زیر تعریف کرد:

using System;

namespace Pivot.Sample2
{
public class StudentStat
{
public int Id { set; get; }
public string Name { set; get; }
public DateTime Date { set; get; }
public bool IsPresent { set; get; }
}
}

و بر همین اساس یک منبع داده فرضی جهت انجام گزارشات می‌تواند به نحو زیر تهیه شود:

using System;
using System.Collections.Generic;

namespace Pivot.Sample2
{
public class StudentsStatDataSource
{
public static IList<StudentStat> CreateMonthlyReportDataSource()
{
var result = new List<StudentStat>();
var rnd = new Random();

for (int day = 1; day < 6; day++)
{
for (int student = 1; student < 6; student++)
{
result.Add(new StudentStat
{
Id = student,
Date = new DateTime(2011, 11, day),
IsPresent = rnd.Next(-1, 1) == 0 ? true : false,
Name = "student " + student
});
}
}

return result;
}
}
}

خروجی این گزارش هم در این حالت ساده با 5 دانشجو و فقط 5 روز، 25 رکورد خواهد بود:


که ... این هم آنچنان از لحاظ آماری مطلوب و مفهوم نیست. می‌خواهیم سطرهای این گزارش همانند لیست واقعی حضورغیاب، فقط از نام افراد تشکیل شود و همچنین ستون‌ها مثلا شماره یا نام روزهای یک هفته یا ماه باشند. مثلا به شکل زیر:


برای رسیدن به این خروجی Crosstab، مثلا می‌توان از کوئری LINQ زیر کمک گرفت که بر اساس شماره دانشجویی اطلاعات را گروه بندی کرده است:

using System.Collections;
using System.Linq;

namespace Pivot.Sample2
{
public class PivotTable
{
public static IList StudentsStatCrossTab()
{
return StudentsStatDataSource
.CreateWeeklyReportDataSource()
.GroupBy(x =>
new
{
x.Id
})
.Select(myGroup =>
new
{
myGroup.Key.Id,
Name = myGroup.First().Name,
Day1IsPresent = myGroup.Where(x => x.Date.Day == 1).First().IsPresent,
Day2IsPresent = myGroup.Where(x => x.Date.Day == 2).First().IsPresent,
Day3IsPresent = myGroup.Where(x => x.Date.Day == 3).First().IsPresent,
Day4IsPresent = myGroup.Where(x => x.Date.Day == 4).First().IsPresent,
Day5IsPresent = myGroup.Where(x => x.Date.Day == 5).First().IsPresent,
PresentsCount = myGroup.Where(x => x.IsPresent).Count(),
AbsentsCount = myGroup.Where(x => !x.IsPresent).Count()
})
.ToList();
}
}
}

و این کوئری خروجی زیر را تولید می‌کند که از هر لحاظ نسبت به لیست قبلی مفهوم‌تر است:


فایل LINQPad این مثال را می‌توانید از اینجا دریافت کنید.

نظرات مطالب
الگوی طراحی Factory Method به همراه مثال
اینکه کلاس مشتق شده‌ی از آن تنها یک متد دارد در اینجا و یا کلا در طراحی شیءگرا، اهمیتی ندارد. کاربرد اصلی طراحی بر اساس اینترفیس‌ها یا کلاس‌های abstract، در تزریق وابستگی‌ها و همچنین unit testing است. زمانیکه بر اساس اینترفیس‌ها کار می‌کنید، می‌توانید پیاده سازی‌های مختلفی را در اختیار استفاده کنندگان قرار دهید (الگوی استراتژی)، وابستگی مستقیم لایه‌های مختلف برنامه را نسبت به یکدیگر کاهش دهید (تزریق وابستگی‌ها) و همچنین ساده‌تر می‌توانید عملیات mocking را پیاده سازی کنید (سهولت unit testing). در طراحی کتابخانه‌های شما حتی اگر نیازی به الگوی استراتژی هم نباشد، وجود اینترفیس‌ها، آینده‌نگری است جهت سهولت عملیات unit testing قسمت‌های مختلف آن. همچنین حتی اگر خود شما در کتابخانه‌ای که ارائه می‌کنید از الگوی استراتژی استفاده نکنید، در آینده ممکن است شخصی از پیاده سازی قسمتی از کتابخانه‌ی شما رضایت نداشته باشد و در این حالت به سهولت می‌تواند با تغییر تنظیمات تزریق وابستگی‌های شما، پیاده سازی دیگری را به کتابخانه‌ای که دیگر توسعه پیدا نمی‌کند، اعمال کند (قسمتی از آن‌را به سلیقه‌ی خودش تغییر دهد و جایگزین کند)؛ به عبارتی کتابخانه‌ی شما «افزونه پذیر» می‌شود.