مطالب
بررسی تفصیلی رابطه Many-to-Many در EF Code first
رابطه چند به چند در مطالب EF Code first سایت جاری، در حد تعریف نگاشت‌های آن بررسی شده، اما نیاز به جزئیات بیشتری برای کار با آن وجود دارد که در ادامه به بررسی آن‌ها خواهیم پرداخت:


1) پیش فرض‌های EF Code first در تشخیص روابط چند به چند

تشخیص اولیه روابط چند به چند، مانند یک مطلب موجود در سایت و برچسب‌های آن؛ که در این حالت یک برچسب می‌تواند به چندین مطلب مختلف اشاره کند و یا برعکس، هر مطلب می‌تواند چندین برچسب داشته باشد، نیازی به تنظیمات خاصی ندارد. همینقدر که دو طرف رابطه توسط یک ICollection به یکدیگر اشاره کنند، مابقی مسایل توسط EF Code first به صورت خودکار حل و فصل خواهند شد:
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Data.Entity.ModelConfiguration;

namespace Sample
{
    public class BlogPost
    {
        public int Id { set; get; }

        [StringLength(maximumLength: 450, MinimumLength = 1), Required]
        public string Title { set; get; }

        [MaxLength]
        public string Body { set; get; }

        public virtual ICollection<Tag> Tags { set; get; } // many-to-many

        public BlogPost()
        {
            Tags = new List<Tag>();
        }
    }

    public class Tag
    {
        public int Id { set; get; }

        [StringLength(maximumLength: 450), Required]
        public string Name { set; get; }

        public virtual ICollection<BlogPost> BlogPosts { set; get; } // many-to-many

        public Tag()
        {
            BlogPosts = new List<BlogPost>();
        }
    }

    public class MyContext : DbContext
    {
        public DbSet<BlogPost> BlogPosts { get; set; }
        public DbSet<Tag> Tags { get; set; }
    }

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

        protected override void Seed(MyContext context)
        {
            var tag1 = new Tag { Name = "Tag1" };
            context.Tags.Add(tag1);

            var post1 = new BlogPost { Title = "Title...1", Body = "Body...1" };
            context.BlogPosts.Add(post1);

            post1.Tags.Add(tag1);

            base.Seed(context);
        }
    }

    public static class Test
    {
        public static void RunTests()
        {
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Configuration>());

            using (var ctx = new MyContext())
            {
                var post1 = ctx.BlogPosts.Find(1);
                if (post1 != null)
                {
                    Console.WriteLine(post1.Title);
                }
            }
        }
    }
}
در این مثال، رابطه بین مطالب ارسالی در یک سایت و برچسب‌های آن به صورت many-to-many تعریف شده است و همینقدر که دو طرف رابطه توسط یک ICollection به هم اشاره می‌کنند، رابطه زیر تشکیل خواهد شد:


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


2) تنظیم ریز جزئیات روابط چند به چند در EF Code first

تنظیمات پیش فرض انجام شده آنچنان نیازی به تغییر ندارند و منطقی به نظر می‌رسند. اما اگر به هر دلیلی نیاز داشتید کنترل بیشتری بر روی جزئیات این مسایل داشته باشید، باید از Fluent API جهت اعمال آن‌ها استفاده کرد:
    public class TagMap : EntityTypeConfiguration<Tag>
    {
        public TagMap()
        {
            this.HasMany(x => x.BlogPosts)
                .WithMany(x => x.Tags)
                .Map(map =>
                    {
                        map.MapLeftKey("TagId");
                        map.MapRightKey("BlogPostId");
                        map.ToTable("BlogPostsJoinTags");
                    });
        }
    }

    public class MyContext : DbContext
    {
        public DbSet<BlogPost> BlogPosts { get; set; }
        public DbSet<Tag> Tags { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new TagMap());
            base.OnModelCreating(modelBuilder);
        }
    }
در اینجا توسط متد Map، نام کلیدهای تعریف شده و همچنین جدول واسط تغییر داده شده‌اند:


3) حذف اطلاعات چند به چند

برای حذف تگ‌های یک مطلب، کافی است تک تک آن‌ها را یافته و توسط متد Remove جهت حذف علامتگذاری کنیم. نهایتا با فراخوانی متد SaveChanges، حذف نهایی انجام و اعمال خواهد شد.
            using (var ctx = new MyContext())
            {
                var post1 = ctx.BlogPosts.Find(1);
                if (post1 != null)
                {
                    Console.WriteLine(post1.Title);
                    foreach (var tag in post1.Tags.ToList())
                        post1.Tags.Remove(tag);
                    ctx.SaveChanges();
                }
            }
در اینجا تنها اتفاقی که رخ می‌دهد، حذف اطلاعات ثبت شده در جدول واسط BlogPostsJoinTags است. Tag1 ثبت شده در متد Seed فوق، حذف نخواهد شد. به عبارتی اطلاعات جداول Tags و BlogPosts بدون تغییر باقی خواهند ماند. فقط یک رابطه بین آن‌ها که در جدول واسط تعریف شده است، حذف می‌گردد.

در ادامه اینبار اگر خود post1 را حذف کنیم:
                var post1 = ctx.BlogPosts.Find(1);
                if (post1 != null)
                {
                    ctx.BlogPosts.Remove(post1);
                    ctx.SaveChanges();
                }
علاوه بر حذف post1، رابطه تعریف شده آن در جدول BlogPostsJoinTags نیز حذف می‌گردد؛ اما Tag1 حذف نخواهد شد.
بنابراین دراینجا cascade delete ایی که به صورت پیش فرض وجود دارد، تنها به معنای حذف تمامی ارتباطات موجود در جدول میانی است و نه حذف کامل طرف دوم رابطه. اگر مطلبی حذف شد، فقط آن مطلب و روابط برچسب‌های متعلق به آن از جدول میانی حذف می‌شوند و نه برچسب‌های تعریف شده برای آن.
البته این تصمیم هم منطقی است. از این لحاظ که اگر قرار بود دو طرف یک رابطه چند به چند با هم حذف شوند، ممکن بود با حذف یک مطلب، کل بانک اطلاعاتی خالی شود! فرض کنید یک مطلب دارای سه برچسب است. این سه برچسب با 20 مطلب دیگر هم رابطه دارند. اکنون مطلب اول را حذف می‌کنیم. برچسب‌های متناظر آن نیز باید حذف شوند. با حذف این برچسب‌ها طرف دوم رابطه آن‌ها که چندین مطلب دیگر است نیز باید حذف شوند!


4) ویرایش و یا افزودن اطلاعات چند به چند

در مثال فوق فرض کنید که می‌خواهیم به اولین مطلب ثبت شده، تعدادی تگ جدید را اضافه کنیم:
                var post1 = ctx.BlogPosts.Find(1);
                if (post1 != null)
                {
                    var tag2 = new Tag { Name = "Tag2" };                    
                    post1.Tags.Add(tag2);
                    ctx.SaveChanges();
                }
در اینجا به صورت خودکار، ابتدا tag2 ذخیره شده و سپس ارتباط آن با post1 در جدول رابط ذخیره خواهد شد.

در مثالی دیگر اگر یک برنامه ASP.NET را درنظر بگیریم، در هربار ویرایش یک مطلب، تعدادی Tag به سرور ارسال می‌شوند. در ابتدای امر هم مشخص نیست کدامیک جدید هستند، چه تعدادی در لیست تگ‌های قبلی مطلب وجود دارند، یا اینکه کلا از لیست برچسب‌ها حذف شده‌اند:
                //نام تگ‌های دریافتی از کاربر  
                var tagsList = new[] { "Tag1", "Tag2", "Tag3" };

                //بارگذاری یک مطلب به همراه تگ‌های آن
                var post1 = ctx.BlogPosts.Include(x => x.Tags).FirstOrDefault(x => x.Id == 1);
                if (post1 != null)
                {
                    //ابتدا کلیه تگ‌های موجود را حذف خواهیم کرد
                    if (post1.Tags != null && post1.Tags.Any())
                        post1.Tags.Clear();

                    //سپس در طی فقط یک کوئری بررسی می‌کنیم کدامیک از موارد ارسالی موجود هستند
                    var listOfActualTags = ctx.Tags.Where(x => tagsList.Contains(x.Name)).ToList();
                    var listOfActualTagNames = listOfActualTags.Select(x => x.Name.ToLower()).ToList();

                    //فقط موارد جدید به تگ‌ها و ارتباطات موجود اضافه می‌شوند
                    foreach (var tag in tagsList)
                    {
                        if (!listOfActualTagNames.Contains(tag.ToLowerInvariant().Trim()))
                        {
                            ctx.Tags.Add(new Tag { Name = tag.Trim() });
                        }
                    }
                    ctx.SaveChanges(); // ثبت موارد جدید

                    //موارد قبلی هم حفظ می‌شوند
                    foreach (var item in listOfActualTags)
                    {
                        post1.Tags.Add(item);
                    }
                    ctx.SaveChanges();
                }
در این مثال فقط تعدادی رشته از کاربر دریافت شده است، بدون Id آن‌ها. ابتدا مطلب متناظر، به همراه تگ‌های آن توسط متد Include دریافت می‌شود. سپس نیاز داریم به سیستم ردیابی EF اعلام کنیم که اتفاقاتی قرار است رخ دهد. به همین جهت تمام تگ‌های مطلب یافت شده را خالی خواهیم کرد. سپس در یک کوئری، بر اساس نام تگ‌های دریافتی، معادل آن‌ها را از بانک اطلاعاتی دریافت خواهیم کرد؛ کوئری tagsList.Contains به where in در طی یک رفت و برگشت، ترجمه می‌شود:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name]
FROM [dbo].[Tags] AS [Extent1]
WHERE [Extent1].[Name] IN (N'Tag1',N'Tag2',N'Tag3')
 آن‌هایی که جدید هستند به بانک اطلاعاتی اضافه شده (بدون نیاز به تعریف قبلی آن‌ها)، آن‌هایی که در لیست قبلی برچسب‌های مطلب بوده‌اند، حفظ خواهند شد.
لازم است لیست موارد موجود را (listOfActualTags) از بانک اطلاعاتی دریافت کنیم، زیرا به این ترتیب سیستم ردیابی EF آن‌ها را به عنوان رکوردی جدید و تکراری ثبت نخواهد کرد.


5) تهیه کوئری‌های LINQ بر روی روابط چند به چند

الف) دریافت یک مطلب خاص به همراه تمام تگ‌های آن:
 ctx.BlogPosts.Where(p => p.Id == 1).Include(p => p.Tags).FirstOrDefault()
ب) دریافت کلیه مطالبی که شامل Tag1 هستند:

var posts = from p in ctx.BlogPosts
                 from t in p.Tags
                 where t.Name == "Tag1"
                 select p;
و یا :
 var posts = ctx.Tags.Where(x => x.Name == "Tag1").SelectMany(x => x.BlogPosts);
مطالب
مهارت‌های تزریق وابستگی‌ها در برنامه‌های NET Core. - قسمت دهم - پیاده سازی الگوی Decorator
الگوی decorator، امکان محصور کردن یک شیء مفروض را با لایه‌ای بر فراز آن میسر می‌کند. برای مثال بجای اینکه در تمام متدهای سرویسی از try/catch استفاده کنیم، می‌توانیم این متدها را با یک ExceptionHandlingDecorator مزین کنیم و یا از این دست اعمال تکراری می‌توان به لاگ کردن ورودی و خروجی‌های یک متد و یا کش کردن اطلاعات آن‌ها نیز اشاره کرد. حتی عملیاتی مانند تشخیص خواص تغییر یافته‌ی یک شیء در Entity framework نیز به کمک همین مزین کننده‌ها که شیء اصلی در حال استفاده را با ایجاد لایه‌ای بر روی آن‌ها محصور می‌کنند، انجام می‌شود. به این عملیات Aspect oriented programming و یا AOP نیز می‌گویند؛ در اینجا واژه‌ی Aspect به اعمال مشترک و متداول موجود در برنامه اشاره می‌کند. در این مطلب قصد داریم نمونه‌ای از این تزئین کننده‌ها را به کمک سیستم تزریق وابستگی‌های NET Core. پیاده سازی کنیم.


پیاده سازی الگوی Decorator به کمک سیستم تزریق وابستگی‌های NET Core.

مثال زیر را در نظر بگیرید که در آن یک سرویس تعریف شده‌است و در این بین استثنائی رخ داده‌است.
    public interface ITaskService
    {
        void Run();
    }

    public class MyTaskService : ITaskService
    {
        public void Run()
        {
            throw new InvalidOperationException("An exception from the MyTaskService!");
        }
    }
می‌خواهیم بدون تغییری در کدهای این کلاس، به متدهای آن در حین اجرای نهایی، یک try/catch را به همراه logging، اضافه کنیم. به همین جهت نیاز خواهیم داشت تا یک محصور کننده (تزئین کننده یا decorator در اینجا) را برای آن طراحی کنیم:
using System;
using Microsoft.Extensions.Logging;
namespace CoreIocServices
{
    public class MyTaskServiceDecorator : ITaskService
    {
        private readonly ILogger<MyTaskServiceDecorator> _logger;
        private readonly ITaskService _decorated;

        public MyTaskServiceDecorator(
            ILogger<MyTaskServiceDecorator> logger,
            ITaskService decorated)
        {
            _logger = logger;
            _decorated = decorated;
        }

        public void Run()
        {
            try
            {
                _decorated.Run();
            }
            catch (Exception ex)
            {
                _logger.LogCritical(ex, "An unhandled exception has been occurred.");
            }
        }
    }
}
این محصور کننده نیز دقیقا همان ITaskService را پیاده سازی می‌کند؛ اما در سازنده‌ی آن یک ITaskService را نیز دریافت می‌کند. علت اینجا است که توسط آن بتوان متدهای ITaskService تزریقی را اجرا کرد و بر روی آن اعمالی مانند کش کردن، لاگ کردن و مدیریت استثناءها و غیره را انجام داد. برای مثال در متد Run آن مشاهده می‌کنید که متد Run همان وهله‌ی تزریقی اجرا شده‌است؛ اما درون یک try/catch به همراه لاگ کردن جزئیات استثنای رخ داده.
مزیت این‌کار، پیاده سازی اصل DRY یا Don't repeat yourself است. کاری که برای رفع این مشکل قرار است انجام دهیم، استفاده از یک تزئین کننده (محصور کننده)، کپسوله سازی اعمال تکراری و سپس اتصال آن به قسمت‌های مختلف برنامه است. همچنین در این حالت اصل open closed principle نیز بهتر رعایت خواهد شد. از این جهت که کدهای تکراری برنامه به یک لایه‌ی دیگر منتقل شده‌اند و دیگر نیازی نیست برای تغییر آن‌ها، کدهای قسمت‌های اصلی برنامه را تغییر داد (کدهای برنامه باز خواهند بود برای توسعه و بسته برای تغییر).

پس از طراحی این تزئین کننده، اکنون نوبت به معرفی آن به سیستم تزریق وابستگی‌های NET Core. است:
namespace CoreIocSample02
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<MyTaskService>();
            services.AddTransient<ITaskService>(serviceProvider =>
                new MyTaskServiceDecorator(
                     serviceProvider.GetService<ILogger<MyTaskServiceDecorator>>(),
                     serviceProvider.GetService<MyTaskService>())
            );
روش انجام اینکار را نیز در «قسمت ششم - دخالت در مراحل وهله سازی اشیاء توسط IoC Container» بیشتر بررسی کرده‌ایم.
در اینجا هم می‌توان در صورت نیاز اصل کلاس MyTaskService را بدون هیچ نوع تزئین کننده‌ای از سیستم تزریق وابستگی‌ها دریافت کرد و یا اگر وهله‌ای از سرویس ITaskService را از آن درخواست کردیم، ابتدا شیء MyTaskServiceDecorator وهله سازی شده و سپس توسط آن یک نمونه‌ی محصور شده و تزئین شده‌ی MyTaskService به فراخوان بازگشت داده خواهد شد.


ساده سازی معرفی تزئین کننده‌ها به سیستم تزریق وابستگی‌های NET Core. به کمک Scrutor

در «قسمت هشتم - ساده سازی معرفی سرویس‌ها توسط Scrutor» با کتابخانه‌ی Scrutor آشنا شدیم. یکی دیگر از قابلیت‌های آن، امکان ساده سازی تعریف تزئین کنند‌ها است:
namespace CoreIocSample02
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<ITaskService, MyTaskService>();
            services.Decorate<ITaskService, MyTaskServiceDecorator>();
در اینجا معادل کدهایی را که با روش factory خود NET Core. نوشتیم، ملاحظه می‌کنید. ابتدا نیاز است خود سرویس اصلی غیر تزئین شده، به نحو متداولی به سیستم معرفی شود. سپس متد الحاقی جدید <,>Decorate را با همان اینترفیس و اینبار با Decorator مدنظر معرفی می‌کنیم. کاری که Scrutor در اینجا انجام می‌دهد، یافتن سرویس ITaskService معرفی شده‌ی پیشین و تعویض آن با MyTaskServiceDecorator می‌باشد. بنابراین نیاز است تعریف services.AddTransient پیش از تعریف services.Decorate انجام شده باشد. این روش تمیزتر از روش قبلی به نظر می‌رسد و شامل وهله سازی مستقیم MyTaskServiceDecorator به همراه فراهم آوردن تمام پارامترهای سازنده‌ی آن توسط ما نیست.
مطالب
چگونه کدها را مستند سازی کنیم؟
یکی از مهمترین مسائل، به خصوص در کارهای تیمی یا پروژه‌های اشتراکی، قرار دادن کامنت‌ها یا اصطلاحا مستند نویسی است که بسیاری از برنامه نویسان با اینکه نظریه آن‌را به شدت قبول دارند، ولی از انجام آن سرباز می‌زنند که به دو عامل تنبلی و عدم دانش نحوه‌ی مستند نویسی بر می‌گردد. در این مقاله قصد داریم به سوالات زیر پاسخ دهیم:
  • چرا به کامنت گذاری یا مستند نویسی نیاز داریم؟
  • چگونه کامنت بنویسیم؟
  • انواع کامنت‌ها چیست؟
  • چه کامنت‌هایی اشتباه هستند؟

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


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


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

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)
    {
    }
}

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

در سایت جاری، دو مقاله زیر اطلاعاتی در رابطه با نحوه‌ی کامنت گذاری ارئه داده‌اند.
- در مقاله «زیباتر کد بنویسیم» چند مورد آن به این موضوع اختصاص دارد.
- مقاله «وادار کردن خود به کامنت نوشتن» گزینه‌ی کامنت گذاری اجباری در ویژوال استودیو را معرفی می‌کند.
مطالب
مروری بر مفاهیم مقدماتی NoSQL
هدف از این مبحث، آشنایی با مفاهیم پایه‌ای اغلب بانک‌های اطلاعاتی NoSQL است که به صورت مشترکی در تمام آن‌ها بکار رفته است. برای مثال بانک‌های اطلاعاتی NoSQL چگونه مباحث یکپارچگی اطلاعات را مدیریت می‌کنند؟ نحوه ایندکس نمودن اطلاعات در آن‌ها چگونه است؟ چگونه از اطلاعات کوئری می‌گیرند؟ الگوریتم‌های محاسباتی مانند MapReduce چیستند و چگونه در اینگونه بانک‌های اطلاعاتی بکار رفته‌‌اند؟ همچنین الگوهای Sharding و Partitioning  که در اغلب بانک‌های اطلاعاتی NoSQL مشترکند، به چه نحوی پیاده سازی شده‌اند.


لیست مشترکات بانک‌های اطلاعاتی NoSQL

قبل از اینکه بخواهیم وارد ریز جزئیات بانک‌های اطلاعاتی NoSQL شویم، نیاز است لیست و سرفصلی از مفاهیم اصلی و مشترک بین اینگونه بانک‌های اطلاعاتی را تدارک ببینیم که شامل موارد ذیل می‌شود:

الف) Non-Relational یا غیر رابطه‌ای
از کلمه NoSQL عموما اینطور برداشت می‌شود که در اینجا دیگر خبری از SQL نویسی نیست که در عمل برداشت نادرستی است. شاید جالب باشد که بدانید، تعدادی از بانک‌های اطلاعاتی NoSQL از زبان SQL نیز به عنوان اینترفیسی برای نوشتن کوئری‌های مرتبط، پشتیبانی می‌کنند.
کلمه NoSQL بیشتر به Non-Relational یا غیر رابطه‌ای بودن اینگونه بانک‌های اطلاعاتی بر می‌گردد. مباحثی مانند مدل‌های داده‌ای نرمال شده، اتصالات و Join جداول، در دنیای NoSQL وجود خارجی ندارند.

ب) Non-schematized/schema free یا بدون اسکیما
مفهوم مهم و مشترک دیگری که در بین بانک‌های اطلاعاتی NoSQL وجود دارد، بدون اسکیما بودن اطلاعات آن‌ها است. به این معنا که با حرکت از رکورد یک به رکورد دو،  ممکن است با دو ساختار داده‌ای متفاوت مواجه شوید.

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

د) Open source یا منبع باز بودن
اغلب بانک‌های اطلاعاتی NoSQL موجود، منبع باز هستند که علاوه بر بهره بردن از مزایای اینگونه پروژه‌ها، استفاده کنندگان سورس باز دیگری را نیز ترغیب به استفاده از آن‌ها کرده‌اند.

ه) Distributed یا توزیع شده
هرچند امکان پیاده سازی توزیع شده بانک‌های اطلاعاتی رابطه‌ای نیز وجود دارد، اما نیاز به تنظیمات قابل توجهی برای حصول این امر می‌باشد. در دنیای NoSQL، توزیع شده بودن جزئی از استاندارد تهیه اینگونه بانک‌های اطلاعاتی است و بر اساس این مدل ذهنی شکل گرفته‌اند. به این معنا که اطلاعات را می‌توان بین چندین سیستم تقسیم کرد، که حتی این سیستم‌ها ممکن است فواصل جغرافیایی قابل توجهی نیز با یکدیگر داشته باشند.

و) Web scale یا مناسب برای برنامه‌های تحت وب پر کاربر
امروزه بسیاری از کمپانی‌های بزرگ اینترنتی، برای مدیریت تعداد بالایی از کاربران همزمان خود، مانند فیس‌بوک، یاهو، گوگل، Linkedin، مایکروسافت و غیره، نیاز به بانک‌های اطلاعاتی پیدا کرده‌اند که باید در مقابل این حجم عظیم درخواست‌ها و همچنین اطلاعاتی که دارند، بسیار بسیار سریع پاسخ دهند. به همین جهت بانک‌های اطلاعاتی NoSQL ابداع شده‌اند تا بتوان برای این نوع سناریوها پاسخی را ارائه داد.
و نکته مهم دیگر اینجا است که خود این کمپانی‌های بزرگ اینترنتی، بزرگترین توسعه دهنده‌های بانک‌های اطلاعاتی NoSQL نیز هستند.



نحوه مدیریت یکپارچگی اطلاعات در بانک‌های اطلاعاتی NoSQL

مدیریت یکپارچگی اطلاعات بانک‌های اطلاعاتی NoSQL به علت ذات و طراحی توزیع شده آن‌ها، با نحوه مدیریت یکپارچگی اطلاعات بانک‌های اطلاعاتی رابطه‌ای متفاوت است. اینجا است که تئوری خاصی به نام CAP مطرح می‌شود که شامل یکپارچگی یا Consistency به همراه Availability یا دسترسی پذیری (همیشه برقرار بودن) و partition tolerance یا توزیع پذیری است. در تئوری CAP مطرح می‌شود که هر بانک اطلاعاتی خاص، تنها دو مورد از سه مورد مطرح شده را می‌تواند با هم پوشش دهد.
به این ترتیب بانک‌های اطلاعاتی رابطه‌ای عموما دو مورد C و P یا یکپارچگی (Consistency) و partition tolerance یا میزان تحمل تقسیم شدن اطلاعات را ارائه می‌دهند. اما بانک‌های اطلاعاتی NoSQL از این تئوری، تنها دو مورد A و P را پوشش می‌دهند (دسترسی پذیری و توزیع پذیری مطلوب).
بنابراین مفهومی به نام ACID که در بانک‌های اطلاعاتی رابطه‌ای ضامن یکپارچگی اطلاعات آن‌ها است، در دنیای NoSQL وجود خارجی ندارد. کلمه ACID مخفف موارد ذیل است:
Atomicity، Consistency، Isolation و Durability
ACID در بانک‌های اطلاعاتی رابطه‌ای تضمین شده است. در این نوع سیستم‌ها، با ایجاد تراکنش‌ها، مباحث ایزوله سازی و یکپارچگی اطلاعات به نحو مطلوبی مدیریت می‌گردد؛ اما دنیای NoSQL، دسترسی پذیری را به یکپارچگی ترجیح داده است و به همین جهت پیشتر مطرح شد که مفهوم «Eventual consistency یا عاقبت یک دست شدن» در این نوع بانک‌های اطلاعاتی در پشت صحنه بکار گرفته می‌شود. یک مثال دنیای واقعی از عاقبت یک دست شدن اطلاعات را حتما در مباحث DNS مطالعه کرده‌اید. زمانیکه یک رکورد DNS اضافه می‌شود یا به روز خواهد شد، اعمال این دستورات در سراسر دنیا به یکباره و همزمان نیست. هرچند اعمال این اطلاعات جدید در یک نود شبکه ممکن است آنی باشد، اما پخش و توزیع آن در سراسر سرورهای DNS دنیا، مدتی زمان خواهد برد (گاهی تا یک روز یا بیشتر).
به همین جهت است که بانک‌های اطلاعاتی رابطه‌ای در حجم‌های عظیم اطلاعات و تعداد کاربران همزمان بالا، کند عمل می‌کنند. حجم اطلاعات بالا است، مدتی زمان خواهد برد تا تغییرات اعمال شوند، و چون مفهوم ACID در این نوع بانک‌های اطلاعاتی تضمین شده است، کاربران باید مدتی منتظر بمانند و نمونه‌ای از آن‌ها را با dead lockهای شایع، احتمالا پیشتر بررسی یا تجربه کرده‌اید. در مقابل، بانک‌های اطلاعاتی NoSQL بجای یکپارچگی، دسترسی پذیری را اولویت اول خود می‌دانند و نه یکپارچگی اطلاعات را. در یک بانک اطلاعاتی NoSQL، دستور ثبت اطلاعات دریافت می‌شود (این مرحله آنی است)، اما اعمال نهایی آن آنی نیست و مدتی زمان خواهد برد تا تمام اطلاعات در کلیه سرورها یک دست شوند.



نحوه مدیریت Indexing اطلاعات در بانک‌های اطلاعاتی NoSQL

اغلب بانک‌های اطلاعاتی NoSQL تنها بر اساس اطلاعات کلیدهای اصلی جداول آن‌ها index می‌شوند (البته نام خاصی به نام «جدول»، بسته به نوع بانک اطلاعاتی NoSQL ممکن است متفاوت باشد، اما منظور ظرف دربرگیرنده تعدادی رکورد است در اینجا). این ایندکس نیز از نوع clustered است. به این معنا که اطلاعات به صورت فیزیکی، بر همین مبنا ذخیره و مرتب خواهند شد.
یک مثال: بانک اطلاعاتی NoSQL خاصی به نام Hbase که بر فراز Hadoop distributed file system طراحی شده است، دقیقا به همین روش عمل می‌کند. این فایل سیستم، تنها از روش Append only برای ذخیره سازی اطلاعات استفاده می‌کند و در آن مفهوم دسترسی اتفاقی یا random access پیاده سازی نشده است. در این حالت، تمام نوشتن‌ها در بافر، لاگ می‌شوند و در بازه‌های زمانی متناوب و مشخصی سبب باز تولید فایل‌های موجود و مرتب سازی مجدد آن‌ها از ابتدا خواهند شد. دسترسی به این اطلاعات پس از تکمیل نوشتن، به علت مرتب سازی فیزیکی که صورت گرفته، بسیار سریع است. همچنین مصرف کننده سیستم نیز چون بلافاصله پس از ثبت اطلاعات در بافر سیستم، کنترل را به دست می‌گیرد، احساس کار با سیستمی را خواهد داشت که بسیار سریع است.
به علاوه Indexهای دیگری نیز وجود دارند که بر اساس کلیدهای اصلی جداول تولید نمی‌شوند و به آن‌ها ایندکس‌های ثانویه یا secondary indexes نیز گفته می‌شود و تنها تعداد محدودی از بانک‌های اطلاعاتی NoSQL از آن‌ها پشتیبانی می‌کنند. این مساله هم از اینجا ناشی می‌شود که با توجه به بدون اسکیما بودن جداول بانک‌های اطلاعاتی NoSQL، چگونه می‌توان اطلاعاتی را ایندکس کرد که ممکن است در رکورد دیگری، ساختار متناظر با آن اصلا وجود خارجی نداشته باشد.



نحوه پردازش Queries در بانک‌های اطلاعاتی NoSQL

بانک‌های اطلاعاتی NoSQL عموما از زبان کوئری خاصی پشتیبانی نمی‌کنند. در اینجا باید به اطلاعات به شکل فایل‌هایی که حاوی رکوردها هستند نگاه کرد. به این ترتیب برای پردازش و یافتن اطلاعات درون این فایل‌ها، نیاز به ایجاد برنامه‌هایی است که این فایل‌ها را گشوده و بر اساس منطق خاصی، اطلاعات مورد نظر را استخراج کنند. گاهی از اوقات زبان SQL نیز پشتیبانی می‌شود ولی آنچنان عمومیت ندارد. الگوریتمی که در این برنامه‌ها بکار گرفته می‌شود، Map Reduce نام دارد.
Map Reduce به معنای نوشتن کدی است، با دو تابع. اولین تابع اصطلاحا Map step یا مرحله نگاشت نام دارد. در این مرحله کوئری به قسمت‌های کوچکتری خرد شده و بر روی سیستم‌های توزیع شده به صورت موازی اجرا می‌شود. مرحله بعد Reduce step نام دارد که در آن، نتیجه دریافتی حاصل از کوئری‌های اجرا شده بر روی سیستم‌های مختلف، با هم یکی خواهند شد.
این روش برای نمونه در سیستم Hadoop بسیار مرسوم است. Hadoop دارای یک فایل سیستم توزیع شده است (که پیشتر در مورد آن بحث شد) به همراه یک موتور Map Reduce توکار. همچنین رده دیگری از بانک‌های اطلاعاتی NoSQL، اصطلاحا Wide column store نام دارند (مانند Hbase) که عموما به همراه Hadoop بکارگرفته می‌شوند. موتور Map Reduce متعلق به Hadoop بر روی جداول Hbase اجرا می‌شوند.
به علاوه Amazon web services دارای سرویسی است به نام Elastic map reduce یا EMR که در حقیقت مجموعه‌ی پردازش ابری است که بر مبنای Hadoop کار می‌کند. این سرویس قادر است با بانک‌های اطلاعاتی NoSQL دیگر و یا حتی بانک‌های اطلاعاتی رابطه‌ای نیز کار کند.
بنابراین MapReduce، یک بانک اطلاعاتی نیست؛ بلکه یک روش پردازش اطلاعات است که فایل‌ها را به عنوان ورودی دریافت کرده و یک فایل را به عنوان خروجی تولید می‌کند. از آنجائیکه بسیاری از بانک‌های اطلاعاتی NoSQL کار عمده‌اشان، ایجاد و تغییر فایل‌ها است، اغلب جداول اطلاعات آن‌ها ورودی و خروجی‌های معتبری برای یک موتور Map reduce به حساب می‌آیند.
در این بین، افزونه‌ای برای Hadoop به نام Hive طراحی شده است که با ارائه HiveSQL، امکان نوشتن کوئری‌هایی SQL مانند را بر فراز موتور‌های Map reduce ممکن می‌سازد. این افزونه با Hive tables خاص خودش و یا با Hbase سازگار است.



آشنایی مقدماتی با مفاهیمی مانند الگوهای Sharding و Partitioning در بانک‌های اطلاعاتی NoSQL

Sharding (شاردینگ تلفظ می‌شود) یک الگوی تقسیم اطلاعات بر روی چندین سرور است که اساس توزیع شده بودن بانک‌های اطلاعاتی NoSQL را تشکیل می‌دهد. این نوع تقسیم اطلاعات، از کوئری‌هایی به نام Fan-out پشتیبانی می‌کند. به این معنا که شما کوئری خود را به نود اصلی ارسال می‌کنید و سپس به کمک موتور‌های Map reduce، این کوئری بر روی سرورهای مختلف اجرا شده و نتیجه نهایی جمع آوری خواهد شد. به این ترتیب تقسیم اطلاعات، صرفا به معنای قرار دادن یک سری فایل بر روی سرورهای مختلف نیست، بلکه هر کدام از این سرورها به صورت مستقل نیز قابلیت پردازش اطلاعات را دارند.
امکان تکثیر و همچنین replication هر کدام از سرورها نیز وجود دارد که قابلیت بازیابی سریع و مقاومت در برابر خرابی‌ها و مشکلات را افزایش می‌دهند.
از آنجائیکه Shardها را می‌توان در سرورهای بسیار متفاوت و گسترده‌ای از لحاظ جغرافیایی قرار داد، هر Shard می‌تواند همانند مفاهیم CDN نیز عمل کند؛ به این معنا که می‌توان Shard مورد نیاز سروری خاص را در محلی نزدیک‌تر به او قرار داد. به این ترتیب سرعت عملیات افزایش یافته و همچنین بار شبکه نیز کاهش می‌یابد.
مطالب
آشنایی با قابلیت FileStream اس کیوال سرور 2008 - قسمت اول

مطلبی چندی قبل در مورد "ذخیره سازی فایل‌ها در دیتابیس یا استفاده از فایل سیستم متداول؟" منتشر گردید، جهت برشمردن فواید ذخیره سازی فایل‌ها در دیتابیس (+). اما معایب این نوع ذخیره سازی بررسی نشدند:

الف) اختصاص یافتن قسمتی از بافر SQL Server به این امر.
ب) با توجه به قرار گرفتن داده‌های BLOB‌ در دیتابیس ، transaction log قابل توجهی تولید خواهد شد. (+)
ج) بیش از 2GB را نمی‌توان در فیلدهایی از نوع varbinary(max) ذخیره کرد.
د) به روز رسانی BLOB ها سبب ایجاد fragmentation می‌شود.

مایکروسافت برای رفع این مشکلات در SQL Server 2008 قابلیت جدیدی را ارائه داده است به نام FileStream که در طی مقالاتی به بررسی آن خواهیم پرداخت.

FILESTREAM موتور دیتابیس اس کیوال سرور را با سیستم فایل NTFS یکپارچه می‌کند؛ به این صورت که داده‌های BLOB از نوع varbinary(max) را به صورت فایل بر روی سیستم ذخیره خواهد کرد. سپس با استفاده از دستورات T-SQL می‌توان این فایل‌ها را ثبت، حذف، به روز رسانی، جستجو و بک آپ گیری کرد. این قابلیت نیز از فیلدهای varbinary(max) استفاده می‌کند؛ اما اکنون ویژگی و برچسب FILESTREAM به این نوع فیلدها الصاق خواهد شد. FILESTREAM data باید در FILESTREAM filegroups ذخیره شوند. FILESTREAM filegroups در حقیقت همان پوشه‌های فایل سیستم می‌باشند. به آن‌ها data containers نیز گفته می‌شوند که مرزی هستند بین ذخیره سازی داده‌ها در فایل سیستم و در دیتابیس.

مزایای سیستم FileStream چیست؟
الف) سیستم transaction مختص به خود را داشته، به همین جهت سبب رشد غیر منطقی حجم فایل transaction log دیتابیس اصلی نمی‌شوند.
ب) هنگام به روز رسانی فیلدهایی از این دست، صرفا ایجاد یا حذف یک فایل مد نظر است؛ بنابراین fragmentation ایجاد شده در این حالت بسیار کمتر از روش استفاده از فیلدهایی از نوع varbinary(max) می‌باشد.
ج) استفاده از NT system cache جهت کش کردن اطلاعات که سبب بالا بردن بازدهی بانک اطلاعاتی خواهد شد.
د) از buffer pool اس کیوال سرور در این حالت استفاده نشده (مطابق قسمت ج) و این حافظه جهت امور روزمره‌ی اس کیوال سرور کاملا مهیا خواهد بود.
ه) محدودیت 2GB فیلدهایی از نوع varbinary(max) با توجه به ذخیره سازی این نوع BLOBs در فایل سیستم، دیگر وجود نخواهد داشت.

چه زمانی بهتر است از FileStream استفاده شود؟
الف) فایل‌هایی که ذخیره می‌شوند به طور متوسط بیش از یک مگابایت حجم داشته باشند. (برای کمتر از این مقدار varbinary(max) BLOBs کارآیی بهتری را ارائه می‌دهند). هر چند این مرز یک مگابایت مطابق اطلاعات books online است اما تجربیات کاری نشان می‌دهند که این سقف را باید 256 کیلوبایت درنظر گرفت.
ب) قابلیت خواندن سریع اطلاعات فایل‌ها مد نظر باشد (بررسی کارآیی مطابق تصویر زیر از MSDN). سیستم NTFS نسبت به SQL Server‌ در خواندن فایل‌های حجیم سریعتر عمل می‌کند.
ج) اگر از یک معماری middle tier در برنامه‌های خود در حال استفاده‌اید.
د) زمانیکه نیاز باشد تا اطلاعات relational و non-relational در یک تراکنش مورد استفاده قرار گیرند.



نکاتی را که باید هنگام ذخیره سازی اطلاعات در FileStream در نظر داشت
الف) هنگامی که یک جدول حاوی فیلدی از نوع FileStream می‌باشد، باید دارای فیلد ID منحصربفرد نیز باشد.
ب) data containers ایی که پیش از این در مورد آن‌ها صحبت شد، نباید تو در تو باشند.
ج) FILESTREAM filegroups بر روی درایوهای فشرده شده نیز می‌توانند قرار داشته باشند.

FileStream از دیدگاه امنیت
امنیت داده‌های FileStream در اس کیوال سرور دقیقا همانند امنیت سایر اطلاعات ذخیره شده در دیتابیس است (دسترسی در حد جدول و یا فیلد). اگر کاربری دسترسی به فیلد FileStream در یک جدول داشته باشد، می‌تواند آن‌ فایل را گشوده و استفاده کند. رمزنگاری بر روی این ستون‌ها پشتیبانی نمی‌شود. تنها اکانتی که اس کیوال سرور تحت آن در حال اجرا است دسترسی به FILESTREAM container دارد. همچنین توصیه شده است که به هیچ اکانت دیگری این دسترسی داده نشود. زمانیکه یک دیتابیس آغاز و مشغول به کار می‌شود، اس کیوال سرور دسترسی به FILESTREAM data container را محدود خواهد کرد و دسترسی به این اطلاعات تنها از طریق دستورات T-SQL و یا OpenSqlFilestream API میسر خواهد بود. بدیهی است زمانیکه اس کیوال سرور متوقف شود، این اطلاعات بدون هیچگونه محدودیتی قابل دسترسی بوده و تنها محدودیت‌های سیستمی به آن‌ها اعمال خواهند شد (که این مورد باید مد نظر باشد).

نگهداری FileStream
FileStream به صورت فیلدهای varbinary(max) یکپارچه با دیتابیس ذخیره می‌شود؛ بنابراین نحوه‌ی تهیه پشتیبان از آن‌ها همانند روش‌های متداول است بدون هیچگونه تغییری (و این اطلاعات در بک آپ دیتابیس لحاظ می‌شوند). اگر نیاز بود هنگام تهیه پشتیبان از این نوع داده‌ها بک آپ گرفته نشود، می‌توان از partial backup با پارامترهای مربوطه استفاده کرد.


ادامه دارد ...

مطالب دوره‌ها
الگوی معکوس سازی کنترل چیست؟
معکوس سازی کنترل (Inversion of Control) الگویی است که نحوه پیاده سازی اصل معکوس سازی وابستگی‌ها (Dependency inversion principle) را بیان می‌کند. با معکوس سازی کنترل، کنترل چیزی را با تغییر کنترل کننده، معکوس می‌کنیم. برای نمونه کلاسی را داریم که ایجاد اشیاء را کنترل می‌کند؛ با معکوس سازی آن به کلاسی یا قسمتی دیگر از سیستم، این مسئولیت را واگذار خواهیم کرد.
IoC یک الگوی سطح بالا است و به روش‌های مختلفی به مسایل متفاوتی جهت معکوس سازی کنترل، قابل اعمال می‌باشد؛ مانند:
- کنترل اینترفیس‌های بین دو سیستم
- کنترل جریان کاری برنامه
- کنترل بر روی ایجاد وابستگی‌ها (جایی که تزریق وابستگی‌ها و DI ظاهر می‌شوند)


سؤال: بین IoC و DIP چه تفاوتی وجود دارد؟

در DIP (قسمت قبل) به این نتیجه رسیدیم که یک ماژول سطح بالاتر نباید به جزئیات پیاده سازی‌های ماژولی سطح پایین‌تر وابسته باشد. هر دوی این‌ها باید بر اساس Abstraction با یکدیگر ارتباط برقرار کنند. IoC روشی است که این Abstraction را فراهم می‌کند. در DIP فقط نگران این هستیم که ماژول‌های موجود در لایه‌های مختلف برنامه به یکدیگر وابسته نباشند اما بیان نکردیم که چگونه.


معکوس سازی اینترفیس‌ها

هدف از معکوس سازی اینترفیس‌ها، استفاده صحیح و معنا دار از اینترفیس‌ها می‌باشد. به این معنا که صرفا تعریف اینترفیس‌ها به این معنا نیست که طراحی صحیحی در برنامه بکار گرفته شده است و در حالت کلی هیچ معنای خاصی ندارد و ارزشی را به برنامه و سیستم شما اضافه نخواهد کرد.
برای مثال یک مسابقه بوکس را درنظر بگیرید. در اینجا Ali یک بوکسور است. مطابق عادت معمول، یک اینترفیس را مخصوص این کلاس ایجاد کرده، به نام IAli و مسابقه بوکس از آن استفاده خواهد کرد. در اینجا تعریف یک اینترفیس برای Ali، هیچ ارزش افزوده‌ای را به همراه ندارد و متاسفانه عادتی است که در بین بسیاری از برنامه نویس‌ها متداول شده است؛ بدون اینکه علت واقعی آن‌را بدانند و تسلطی به الگوهای طراحی برنامه نویسی شیءگرا داشته باشند. صرف اینکه به آن‌ها گفته شده است تعریف اینترفیس خوب است، سعی می‌کنند برای همه چیز اینترفیس تعریف کنند!
تعریف یک اینترفیس تنها زمانی ارزش خواهد داشت که چندین پیاده سازی از آن ارائه شود. در مثال ما پیاده سازی‌های مختلفی از اینترفیس IAli بی‌مفهوم است. همچنین در دنیای واقعی، در یک مسابقه بوکس، چندین و چند شرکت کننده وجود خواهند داشت. آیا باید به ازای هر کدام یک اینترفیس جداگانه تعریف کرد؟ ضمنا ممکن است اینترفیس IAli متدی داشته باشد به نام ضربه، اینترفیس IVahid متد دیگری داشته باشد به نام دفاع.
کاری که در اینجا جهت طراحی صحیح باید صورت گیرد، معکوس سازی اینترفیس‌ها است. به این ترتیب که مسابقه بوکس است که باید اینترفیس مورد نیاز خود را تعریف کند و آن هم تنها یک اینترفیس است به نام IBoxer. اکنون Ali، Vahid و سایرین باید این اینترفیس را جهت شرکت در مسابقه بوکس پیاده سازی کنند. بنابراین دیگر صرف وجود یک کلاس، اینترفیس مجزایی برای آن تعریف نشده و بر اساس معکوس سازی کنترل است که تعریف اینترفیس IBoxer معنا پیدا کرده است. اکنون IBoxer دارای چندین و چند پیاده سازی خواهد بود. به این ترتیب، تعریف اینترفیس، ارزشی را به سیستم افزوده است.
به این نوع معکوس سازی اینترفیس‌ها، الگوی provider model نیز گفته می‌شود. برای مثال کلاسی که از چندین سرویس استفاده می‌کند، بهتر است یک IService را ایجاد کرده و تامین کننده‌هایی، این IService را پیاده سازی کنند. نمونه‌ای از آن در دنیای دات نت، Membership Provider موجود در ASP.NET است که پیاده سازی‌های بسیاری از آن تاکنون تهیه و ارائه شده‌اند.




معکوس سازی جریان کاری برنامه

جریان کاری معمول یک برنامه یا Noraml flow، عموما رویه‌ای یا Procedural است؛ به این معنا که از یک مرحله به مرحله‌ای بعد هدایت خواهد شد. برای مثال یک برنامه خط فرمان را درنظر بگیرید که ابتدا می‌پرسد نام شما چیست؟ در مرحله بعد مثلا رنگ مورد علاقه شما را خواهد پرسید.
برای معکوس سازی این جریان کاری، از یک رابط کاربری گرافیکی یا GUI استفاده می‌شود. مثلا یک فرم را درنظر بگیرید که در آن دو جعبه متنی، کار دریافت نام و رنگ را به عهده دارند؛ به همراه یک دکمه ثبت اطلاعات. به این ترتیب بجای اینکه برنامه، مرحله به مرحله کاربر را جهت ثبت اطلاعات هدایت کند، کنترل به کاربر منتقل و معکوس شده است.


معکوس سازی تولید اشیاء

معکوس سازی تولید اشیاء، اصل بحث دوره و سری جاری را تشکیل می‌دهد و در ادامه مباحث، بیشتر و عمیق‌تر بررسی خواهد گردید.
روش متداول تعریف و استفاده از اشیاء دیگر درون یک کلاس، وهله سازی آن‌ها توسط کلمه کلیدی new است. به این ترتیب از یک وابستگی به صورت مستقیم درون کدهای کلاس استفاده خواهد شد. بنابراین در این حالت کلاس‌های سطح بالاتر به ماژول‌های سطح پایین، به صورت مستقیم وابسته می‌گردند.
برای اینکه این کنترل را معکوس کنیم، نیاز است ایجاد و وهله سازی این اشیاء وابستگی را در خارج از کلاس جاری انجام دهیم. شاید در اینجا بپرسید که چرا؟
اگر با الگوی طراحی شیءگرای Factory آشنا باشید، همان ایده در اینجا مدنظر است:
Button button;
switch (UserSettings.UserSkinType)
{
   case UserSkinTypes.Normal:
      button = new Button();
      break;
   case UserSkinTypes.Fancy:
      button = new FancyButton();
      break;
}
در این مثال بر اساس تنظیمات کاربر، قرار است شکل دکمه‌های نمایش داده شده در صفحه تغییر کنند.
حال در این برنامه اگر قرار باشد کار و کنترل محل وهله سازی این دکمه‌ها معکوس نشود، در هر قسمتی از برنامه نیاز است این سوئیچ تکرار گردد (برای مثال در چند ده فرم مختلف برنامه). بنابراین بهتر است محل ایجاد این دکمه‌ها به کلاس دیگری منتقل شود مانند ButtonFactory و سپس از این کلاس در مکان‌های مختلف برنامه استفاده گردد:
 Button button = ButtonFactory.CreateButton();
در این حالت علاوه بر کاهش کدهای تکراری، اگر حالت دکمه جدیدی نیز طراحی گردید، نیاز نخواهد بود تا بسیاری از نقاط برنامه بازنویسی شوند.
بنابراین در مثال فوق، کنترل ایجاد دکمه‌ها به یک کلاس پایه قرار گرفته در خارج از کلاس جاری، معکوس شده است.


انواع معکوس سازی تولید اشیاء

بسیاری شاید تصور کنند که تنها راه معکوس سازی تولید اشیاء، تزریق وابستگی‌ها است؛ اما روش‌های چندی برای انجام اینکار وجود دارد:
الف) استفاده از الگوی طراحی Factory (که نمونه‌ای از آن‌را در قسمت قبل مشاهده کردید)
ب) استفاده از الگوی Service Locator
 Button button = ServiceLocator.Create(IButton.Class)
در این الگو بر اساس یک سری تنظیمات اولیه، نوع خاصی از یک اینترفیس درخواست شده و نهایتا وهله‌ای که آن‌را پیاده سازی می‌کند، به برنامه بازگشت داده می‌شود.
ج) تزریق وابستگی‌ها
Button button = GetTheButton();
Form1 frm = new Form1(button);
در اینجا نوع وابستگی مورد نیاز کلاس Form1 در سازنده آن تعریف شده و کار تهیه وهله‌ای از آن وابستگی در خارج از کلاس صورت می‌گیرد.

به صورت خلاصه هر زمانیکه تولید و وهله سازی وابستگی‌های یک کلاس را به خارج از آن منتقل کردید، کار معکوس سازی تولید وابستگی‌ها انجام شده است.
مطالب
مهارت‌های تزریق وابستگی‌ها در برنامه‌های NET Core. - قسمت اول - تزریق وابستگی‌ها در برنامه‌های کنسول
پیشتر با مقدمات تزریق وابستگی‌ها در برنامه‌های ASP.NET Core آشنا شده‌ایم:
در ادامه در طی چند مطلب می‌خواهیم نکات و سناریوهای تکمیلی مرتبط با امکانات تزریق وابستگی‌های توکار برنامه‌های مبتنی بر NET Core. را بررسی کنیم.


تزریق وابستگی‌ها در برنامه‌های کنسول مبتنی بر NET Core.

تزریق وابستگی‌ها، یکی از پرکاربردترین الگوهای طراحی برنامه‌های مدرن است. در نگارش‌های قبلی ASP.NET، به کمک DependencyResolver آن، کتابخانه‌های ثالث کمکی تزریق وابستگی‌ها می‌توانستند خودشان را به سیستم متصل کنند. اینبار ASP.NET Core به همراه IoC Container توکار خودش ارائه شده‌است که این کتابخانه، در خارج از آن، مانند برنامه‌های کنسول نیز قابل استفاده است.


سرویس نمونه‌‌ای برای تزریق آن در یک برنامه‌ی کنسول NET Core.

در پوشه‌ی جدید CoreIocServices، دستور dotnet new classlib را صادر می‌کنیم تا یک پروژه‌ی class library جدید را ایجاد کند. سپس اینترفیس ITestService و یک نمونه پیاده سازی آن‌را به این پروژه اضافه می‌کنیم تا در ادامه بتوانیم تنظیمات تزریق وابستگی‌های آن‌را در یک پروژه‌ی کنسول، ایجاد کنیم:
using System;
using Microsoft.Extensions.Logging;

namespace CoreIocServices
{
    public interface ITestService
    {
        void Run();
    }

    public class TestService : ITestService
    {
        private readonly ILogger<TestService> _logger;

        public TestService(ILogger<TestService> logger)
        {
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }

        public void Run()
        {
            _logger.LogWarning("A Warning from the TestService!");
        }
    }
}
در اینجا این سرویس نمونه نیز دارای یک وابستگی تزریق شده‌ی در سازنده‌ی آن است. این وابستگی، همان امکانات توکار logging مربوط به ASP.NET Core است که در برنامه‌های کنسول نیز قابل استفاده است. برای اینکه پروژه قابل کامپایل باشد، نیاز است وابستگی Microsoft.Extensions.Logging را نیز به آن افزود:
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
  </ItemGroup>

</Project>


دسترسی به سرویس TestService از طریق تزریق وابستگی‌ها در یک برنامه‌ی کنسول

در ادامه، یک پوشه‌ی جدید را به نام CoreIocSample01 ایجاد کرده و دستور dotnet new console را در آن اجرا می‌کنیم تا یک برنامه‌ی کنسول جدید را ایجاد کند.
سپس اولین قدم برای استفاده‌ی از سرویس TestService از طریق تزریق وابستگی‌ها، افزودن وابستگی‌های مورد نیاز آن است:
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.2</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\CoreIocServices\CoreIocServices.csproj" />
  </ItemGroup>

</Project>
در اینجا بسته‌ی Microsoft.Extensions.DependencyInjection جهت دسترسی به امکانات تزریق وابستگی‌های NET Core. به پروژه اضافه شده و همچنین ارجاعی نیز به پروژه‌ی class library که پیشتر ایجاد کردیم، افزوده شده‌است.
اکنون می‌توانیم همان روشی را که در یک برنامه‌ی ASP.NET Core با ارائه‌ی متد ConfigureServices به صورت از پیش آماده شده برای ما مهیا است، در اینجا نیز پیاده سازی کنیم:
using CoreIocServices;
using Microsoft.Extensions.DependencyInjection;

namespace CoreIocSample01
{
    class Program
    {
        static void Main(string[] args)
        {
            var serviceCollection = new ServiceCollection();
            ConfigureServices(serviceCollection);
            var serviceProvider = serviceCollection.BuildServiceProvider();

            var testService = serviceProvider.GetService<ITestService>();
            testService.Run();
        }

        private static void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<ITestService, TestService>();
        }
    }
}
کار با تعریف یک ServiceCollection جدید شروع می‌شود. سپس در متد ConfigureServices، همانند کاری که در برنامه‌های ASP.NET Core انجام می‌دهیم، ارتباطات اینترفیس‌ها و پیاده سازی‌های آن‌ها، به همراه طول عمر آن‌ها را تعریف می‌کنیم.
سپس نیاز است بر روی این ServiceCollection، متد BuildServiceProvider فراخوانی شود تا بتوانیم به IServiceProvider دسترسی پیدا کنیم. به آن Dependency Management Container نیز می‌گویند. این Container است که امکان دسترسی به وهله‌ای از ITestService و سپس فراخوانی متد Run آن‌را میسر می‌کند.


مشکل! برنامه‌ی کنسول اجرا نمی‌شود!

اگر سعی کنیم مثال فوق را اجرا کنیم، با استثنای زیر برنامه خاتمه می‌یابد:
Exception has occurred: CLR/System.InvalidOperationException
An unhandled exception of type 'System.InvalidOperationException' occurred in Microsoft.Extensions.DependencyInjection.dll:
'Unable to resolve service for type 'Microsoft.Extensions.Logging.ILogger`1[CoreIocServices.TestService]'
while attempting to activate 'CoreIocServices.TestService'.'
عنوان می‌کند که وابستگی تزریق شده‌ی در سازنده‌ی کلاس TestService را نمی‌تواند پیدا کند. علت اینجا است که هرچند ILogger را به سازنده‌ی کلاس سرویس خود اضافه کرده‌ایم، اما هنوز پیاده سازی کننده‌ی آن‌را مشخص نکرده‌ایم. به همین جهت امکان وهله سازی از این کلاس وجود ندارد. عموما در برنامه‌های ASP.NET Core نیازی به تنظیم زیر ساخت logging آن نیست؛ چون این مورد نیز به صورت پیش‌فرض انجام شده‌است. اما در اینجا خیر. به همین جهت دو وابستگی جدید Microsoft.Extensions.Logging.Console و Microsoft.Extensions.Logging.Debug را به پروژه‌ی کنسول اضافه می‌کنیم:
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.2</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.2.0" />
    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.2.0" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\CoreIocServices\CoreIocServices.csproj" />
  </ItemGroup>

</Project>
پس از آن متد ConfigureServices ما جهت تعریف logging در دو حالت دیباگ و کنسول، به صورت زیر تغییر می‌کند:
private static void ConfigureServices(IServiceCollection services)
{
   services.AddLogging(configure => configure.AddConsole().AddDebug());
   services.AddTransient<ITestService, TestService>();
}
اکنون اگر برنامه را اجرا کنیم، خروجی زیر قابل مشاهده خواهد بود:
 CoreIocServices.TestService:Warning: A Warning from the TestService!


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید: CoreDependencyInjectionSamples-01.zip
نظرات مطالب
فعال سازی عملیات CRUD در Kendo UI Grid
قطعا در این سایت به این صورت نبوده و به اندازه‌ی کافی در این مورد بحث شده:
به عنوان مثال هم اکثر پروژه‌های این سایت، این مفاهیم را پیاده سازی کرده‌اند؛ مانند «فروشگاه IrisStore» و «سیستم Decision» و ...  
مطالب
یک سرویس (میکروسرویس) چیست؟ و چگونه آن را مستند کنیم؟ (قسمت اول)
معماری میکروسرویس (یا به اختصار: میکروسرویس) یک سبک معماری نرم افزار می‌باشد که در آن یک نرم افزار، به مجموعه‌ای از سرویس‌ها خرد می‌شود؛ به نحوی که هر سرویس مسئولیت انجام بخشی از منطق کسب و کار را به عهده داشته باشد.
این تقسیم بندی مزایای متعددی را به همراه دارد که نهایتا پیاده سازی و توسعه راحت‌تر نرم افزار‌های بزرگ و پیچیده را ممکن می‌نماید. از جمله مزایای این معماری می‌توان به راحت‌تر شدن مباحث continuous delivery/deployment، مقیاس پذیری بهتر، تحمل خطا، مهاجرت به (و یا استفاده از) تکنولوژی‌های جدید در بخش‌های مختلف نرم افزار و ... اشاره نمود.

مهم‌ترین بخش و تصمیمات شما به عنوان یک معمار نرم افزار، هنگام طراحی با استفاده از این معماری، شناسایی بخش‌های مختلف کسب و کار، جدا سازی و مرزبندی نمودن آنها و نهایتا طراحی سرویس‌ها و تعیین نحوه همکاری آنها با یکدیگر می‌باشد. لذا در هنگام استفاده از معماری میکروسرویس، مرکز توجهات باید کسب و کار باشد و نه مسائل تکنیکال و موضوعاتی مانند Docker, Kubernetes , Serverless و ... . (DDD می‌تواند به شما جهت مرزبندی بخش‌های مختلف کسب و کار و شناسایی سرویس‌ها کمک نماید)

تا اینجا متوجه شدیم که میکروسرویس در واقع یک سبک معماری نرم افزار محسوب می‌گردد و در واقع میکروسرویس (در اینجا و ادامه مقاله، منظور از میکروسرویس، معماری میکروسرویس می‌باشد) از چندین سرویس مجزا و مستقل تشکیل شده‌است که هر سرویس معمولا مسئولیت بخشی از منطق کسب و کار را بر عهده خواهد داشت.

مشخصات یک سرویس
هر سرویس در معماری میکروسرویس دارای چندین ویژگی اصلی به شرح زیر می‌باشد:
- Loosely coupled with other services - باید به طور مستقل از سایر سرویس‌ها عمل کند. به این معنا که تغییر و توسعه سایر سرویس‌ها موجب اختلالی در عملکرد این سرویس نگردد و برعکس، تغییر و توسعه این سرویس نباید عملکرد سایر سرویس‌ها را مختل نماید.
- Independently deployable - تیم توسعه دهنده سرویس قادر باشد تا بدون نیاز به هماهنگی با سایر تیم‌ها، خدمات خود (شامل ویژگی‌های جدید و تغییرات) را مستقر (Deploy) نماید.
- Capable of being developed by a small team – سرویس، امکان توسعه توسط یک تیم کوچک را داشته باشد. این مورد به جهت جلوگیری از سربار زیاد ناشی از هماهنگی در تیم‌های بزرگ، ضرورت دارد.
- Highly maintainable and testable – سرویس بسیار قابل نگهداری و قابل آزمایش باشد؛ امکان توسعه، تست و استقرار سریع را داشته باشد.

ساختار یک سرویس
حال که با ویژگی‌ها و مشخصات اصلی یک سرویس آشنا شدیم، در دیاگرام زیر، ساختار درونی یک سرویس را که از معماری هگزاگون (hexagonal architecture) استفاده می‌نماید، بررسی میکنیم. در این معماری، هسته سرویس، منطق کسب کار (Business logic) می‌باشد که توسط چندین آداپتور (جهت ارتباط با سایر سرویس‌ها) احاطه شده است.

بیایید با دقت به هر یک از بخش‌های یک سرویس (با توجه به دیاگرام فوق) نگاه کنیم

هر سرویس  احتمالا دارای یک یا چندین API می‌باشد
از دید مصرف کنندگان یک سرویس (Consumers)، تنها مورد با اهمیت یک سرویس، APIهای آن سرویس می‌باشد. APIهای یک سرویس نیز (با توجه به تصویر فوق) شامل عملیات یا Operations و وقایع منتشر شده یا Published events می‌باشند. که در ادامه این انواع را بررسی میکنیم.

- عملیات (Operations)
به صورت کلی و همانطور که در دیاگرام فوق قابل مشاهده می‌باشد، عملیات به دو نوع دستورات (Commands) و جستارها (Queries) تقسیم می‌شوند. دستورات نوعی از عملیات می‌باشند که موجب تغییر داده‌ها می‌شود؛ اما در مقابل جستارها، عملیاتی در جهت واکشی داده‌ها می‌باشند. برای مثال یک سرویس  ثبت سفارش (OrderService) را در نظر بگیرید. عملیاتی مانند ثبت سفارش ()CreateOrder، انصراف از سفارش ثبت شده  ()CancelOrder و ... عملیاتی از نوع دستورات هستند و عملیاتی مانند یافتن یک سفارش خاص ()FindOrder که هیچ دیتایی را تغییر نمیدهد، از نوع جستارها می‌باشند.
عملیات ارائه شده توسط یک سرویس میتواند از ترکیبی از پروتکل‌های همزمان (Synchronous protocols) مانند REST یا gRPC و پروتکل‌های غیر همزمان (Asynchronous protocols) مانند messaging باشند.
پروتکل‌های همزمان، به ویژه REST، بیشتر در مواردی که قصد ارائه API به کلاینت‌های خارجی (External clients) را داریم، مانند موبایل اپلیکیشن‌ها و یا نرم افزارهای تک صفحه‌ای (SPA) کاربرد دارند.
از پروتکل‌های غیر همزمان مانند messaging نیز بیشتر در مواردی که میخواهیم الگوی ساگا (SAGA) را پیاده سازی نماییم و به روز نگه داشتن داده‌ها را بین سرویس‌های مختلف حفظ کنیم، نیاز به استفاده داریم. برای مثال در همان سیستم ثبت سفارش، عملیات ()CreateOrder به صورت Rest و با متد Post در Endpoint ای مانند /Order پیاده سازی می‌شود و پس از فراخوانی، یک عملیات غیرهمزمان مانند CreateOrderSaga را نیز به صورت messaging آغاز میکند.

- وقایع (Events)
سرویس‌ها، اغلب وقایعی (Event) را نیز منتشر میکنند. منظور از وقایع یا events معمولا همان مفهوم domain event درDDD می‌باشد که در همان ادبیات DDD وقایع توسط aggregate‌ها در زمان هایی مانند ایجاد، ویرایش، حذف و یا سایر مفاهیم موجود در منطق کسب و کار منتشر می‌شوند. سرویس نیز معمولا این وقایع را روی یک کانال ارتباطی (message channel) که توسط یک message broker (مانند RabbitMQ, Apache Kafka, ActiveMQ و ...) پیاده سازی شده است، منتشر میکند. و علاقمندان به دریافت این وقایع می‌توانند وقایع را پس از انتشار، بر روی کانال ارتباطی دریافت نمایند.


منطق کسب و کار (Business Logic)
منطق کسب و کار، قلب هر سرویس و دلیل وجود آن سرویس می‌باشد که API هایی را در قالب عملیات (Opertaions) پیاده سازی و همچنین مواردی را در قالب وقایع (Events) منتشر می‌نماید. همچنین منطق کسب و کار می‌تواند بنا بر نیاز خود، عملیات مربوط به سایر سرویس‌ها را فراخوانی و یا در کانال‌های ارتباطی (channels) مربوط به وقایع آنها، مشترک (Subscribes) شود و نهایتا داده‌ها را در دیتابیس خود نگهداری نماید.

نحوه همکاری سرویس‌ها با یکدیگر (Services Collaborations)
با توجه به مفاهیم فوق، زمانی که صحبت از همکاری (collaborate) بین سرویس‌ها می‌شود، معمولا منظور، ارتباط آنها از طریق APIهای یکدیگر (شامل عملیات و وقایع که پیش‌تر توضیح داده شد) می‌باشد (به جای خواندن و نوشتن مستقیم در دیتابیس‌های مربوط به یکدیگر می‌باشد).
برای مثال یک سرویس ممکن است عملیات مربوط به ایجاد سفارش ()CreateOrder را از سرویس ثبت سفارش (OrderService) فراخوانی نماید و یا برعکس خود سرویس ثبت سفارش (OrderService) ممکن است بر حسب نیاز منطق کسب و کار خود، عملیات ارائه شده توسط سرویس انبار را فراخوانی نماید.
همچنین یک سرویس جهت همکاری با دیگر سرویس‌ها میتواند در وقایع منتشر شده (Published events) توسط آنها مشترک (Subscribes) شود. برای مثال سرویس ثبت سفارش احتمالا در وقایع منتشر شده از سوی سرویس رستوران مشترک می‌شود.

دیتابیس اختصاصی
معمولا هر سرویس دارای یک یا چند دیتابیس می‌باشد که دیتای اختصاصی مربوط به منطق کسب و کار خود و در مواردی بخشی از دیتای مربوط به سایر سرویس‌ها را در آن‌(ها) نگهداری میکند. برای مثال اطلاعات سفارش‌ها را هم سرویس ثبت سفارش و هم سرویس رستوران، هر دو نگهداری میکنند و عملا این دیتا ابتدا در سرویس رستوران و سپس در سرویس ثبت سفارش، مجددا نگهداری می‌شود و به نوعی دیتای فوق Replicate شده و تکراری می‌باشد. اما به جهت اطمینان از کاهش وابستگی (loose coupling) این تکرار داده‌ها انجام می‌شود. در مجموع استفاده از یک دیتابیس مشترک (منظور table مشترک می‌باشد) بین سرویس‌ها ایده‌ی بدی می‌باشد و سرویس‌ها باید از طریق API‌های یکدیگر باهم همکاری نمایند.

نتیجه
در این مقاله عنوان شد که میکروسرویس یک سبک معماری می‌باشد و در این معماری، نرم افزار و منطق کسب و کار، به چندین سرویس مختلف  تقسیم می‌شود. مشخصات کلیدی که هر سرویس باید در این سبک معماری (microservice architecture) داشته باشد و همچنین ساختار درونی هر سرویس بررسی شد.
در قسمت بعدی این مقاله، در مورد نحوه مستند سازی این سرویس‌ها صحبت می‌شود. چرا که با زیاد شدن تعداد سرویس‌ها، در صورت عدم وجود یک مستندات مناسب (documents)، ارتباط و هماهنگی تیم‌ها با یکدیگر خود موجب سربار خواهد شد.

منابع
برگرفته شده از مقاله آقای ریچاردسون (whats-a-service
مطالب
نحوه استفاده از ViewModel در ASP.NET MVC

یک Model چیست؟

·   قسمتی از Application است که Domain Logic را پیاده سازی می‌کند.

·   همچنین با عنوان Business Logic نیز شناخته می‌شود.

·   Domain Logic داده‌هایی را که بین UI و دیتابیس پاس داده می‌شود، مدیریت می‌کند.

·   برای مثال، در یک سیستم انبار،Model   کارش ذخیره سازی اقلام در حافظه و Logic تعیین کننده موجود بودن یک آیتم در انبار میباشد.


یک ViewModel چیست؟

·   ViewModel به ما این امکان را میدهد تا از چندین Entity، یک شیء واحد بسازیم.

·   بهینه شده برای استفاده و رندر توسط یک View

 

چرا باید از ViewModel استفاده کنیم؟

  • اگر شما بخواهید چندین Model را به یک View پاس دهید، استفاده از ViewModel ایده بسیار خوبی است.
  • همچنین امکان Validate کردن ViewModel را به روشی متفاوت‌تر از Domain Model برای سناریوهای اعتبارسنجی attribute-based را در اختیارمان قرار میدهد.
  • برای قالبندی داده‌ها نیز استفاده میشود.
        o برای مثال به یک داده یا مقدار پولی قالبندی شده به یک روش خاص نیاز داریم؟
ViewModel بهترین مکان برای انجام این کار است.
  • استفاده از ViewModel تعامل بین Model و View را ساده‌تر می‌کند.  


مکان قرارگیری ViewModel در پروژه بهتر است کجا باشد؟

  1. می‌تواند در یک پوشه با نام ViewModel در ریشه پروژه باشد.

  2. به صورت یک dll ارجاع داده شده به پروژه. 
  3. در یک پروژه جدا به عنوان یک Service Layer.


Best Practice هایی در زمان استفاده از ViewModelها 

  1. داده‌هایی را که قرار است در View رندر شوند، داخل ViewModel قرار دهید.
  2. View باید خصوصیاتی را که نیاز دارد، مستقیما از ViewModel بدست بیاورد.
  3. وقتی که ViewModel شما پیچیده‌تر می‌شود، بهتر است از یک Mapper استفاده کنید.

اجازه دهید مثالی را در این رابطه براتون بیارم :

در یک View می‌خواهیم هم لیست اخبار سایت و هم لیست سخنرانان سایت (مثال مربوط به یک پروژه برای همایش است) را نمایش دهیم. خوب طبق مطالب فوق استفاده از ViewModel بهترین راه حل است. ViewModel مربوطه به صورت زیر تعریف شده است :

public class SpeakerAndNewsViewModel
    {
        public IEnumerable<News> News { get; set; }
        public IEnumerable<Speaker> Speakers { get; set; }
    }
و در اکشن متد Index هم بدین صورت Model‌ها را به ViewModel ساخته شده پاس می‌دهیم :
public ActionResult Index()
        {
            var news = db.News.OrderBy(r=>r.Date)
                        .Take(3);
            var speakers = db.Speakers.Take(4);
            var model = new SpeakerAndNewsViewModel { 
                News=news,
                Speakers=speakers
            };
            return View(model);
        }
و یک View را به صورت Strongly Typed از نوع ViewModel خود که در اینجا SpeakerAndNewsViewModel  است ایجاد می‌کنیم :

و View مربوطه را به شکل زیر تغییر می‌دهیم :  
@model  Project.Models.SpeakerAndNewsViewModel
@{
    ViewBag.Title = "Home Page";
}
@section news
{
@foreach (var item in Model.News)
{
    //..
}
}
@section speakers
{
@foreach (var item in Model.Speakers)
{
    //...
}
}