<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>پست‌های انتشارات SoranRad.com</title>
        <link>https://virgool.io/20devs/feed</link>
        <description>من تجربیاتم را در طی سالهای کاری در ویرگول منتشر می کنم. اگر به برنامه نویسی علاقه دارید این بلاگ را مانند خانه خود بدانید.</description>
        <language>fa</language>
        <pubDate>2026-06-16 11:45:51</pubDate>
        <image>
            <url>https://files.virgool.io/upload/publication/atbd592bnmsy/xwcb3q.png</url>
            <title>SoranRad.com</title>
            <link>https://virgool.io/20devs</link>
        </image>

                    <item>
                <title>استفاده از منوهای آبشاری کافیه</title>
                <link>https://virgool.io/20devs/%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%D9%85%D9%86%D9%88%D9%87%D8%A7%DB%8C-%D8%A2%D8%A8%D8%B4%D8%A7%D8%B1%DB%8C-%DA%A9%D8%A7%D9%81%DB%8C%D9%87-hbjv32cjs3o9</link>
                <description>مقدمهاین مقاله درباره منوها و نوار ابزارها در برنامه های ویندوزی است و مطالب آن درباره روش های طراحی منو در برنامه های ویندوزی است که خیلی کمتر به آن پرداخته شده است. بسیاری از برنامه های کاربردی ویندوزی کنترل های سفارشی خود را دارند که از لحاظ شکل و شمایل یا تم ظاهری (Theme) با سایر اجزای برنامه کاربردی هماهنگ می باشد و سفارشی سازی شده است. این مقاله روش هایی را برای سفارشی سازی منو ها معرفی می کند که با کمک آن می توانید تم کلاسیک برنامه را تغییر دهید و تجربه کاری متفاوتی را به کاربر بدهید. ابتدا به نحوه چیدمان آیتم های منو می پردازد و پس از آن روشی برای سفارشی کردن Theme  منوها یا رندر Rendering کردن آنها در برنامه های ویندوزی معرفی می کند.در ادامه منویی که در تصویر زیر مشاهده می کنید را در یک برنامه ویندوزی (Winforms) پیاده سازی می کنیم. این کار به کمک خاصیت LayoutStyle امکان پذیر است. هدف معرفی کلاس TableLayoutSettings  و استفاده از نوع ToolStripLayoutStyle.Table برای LayoutStyle است.منوی سفارشی که در این مقاله طراحی می کنیم.بعد از آن به سراغ ToolStripControlHost می رویم و همان منو را به روشی دیگر پیاده سازی می کنیم. هدف از معرفی این کلاس آشنایی با روش های دیگر برای پیاده سازی منو هایی با طراحی متفاوت و پیچیده تر است و اینکه چطور می توانیم یک کامپوننت سفارشی (UserControl) را به صورت منو یا اصطلاحا Popup نمایش دهیم. در انتها نیز نحوه سفارشی سازی یک رندرینگ حرفه ای (Rendering) برای پیاده سازی تم ظاهری (Theme) مخصوص را معرفی می کنم.کاربرد ToolStripControlHost کاربرد این که بخواهیم یک کنترل سفارشی  (User Control) به صورت Popup نمایش دهیم کجاست؟ فرض کنید که شما یک کنترل ساعت درست کرده اید وقتی که کاربر روی آن کلیک می کند یک فرم انتخاب ساعت برایش نمایش داده می شود که بعد از انتخاب ساعت مقدار آن در TextBox مربوط به آن نمایش داده می شود دقیقا مانند شکل زیر. این کار به کمک ToolStripControlHost به سادگی امکان پذیر است. کامپوننت سفارشی برای انتخاب ساعت بسیاری از ما منو برنامه های کاربردی را که به صورت یک لیست پشت سرهم که از بالا به پایین قرار گرفته اند میشناسیم. این ساختار با نام منوی آبشاری معروف است. یک روال بسیار کلیشه ای که برخی از برنامه ها این روند را تغییر داده اند و منوهایی با ظاهری جالب تر و زیباتر را توسعه داده اند که برای کاربر  ملال آور و خسته کننده نباشد. ولی در عوض بسیاری هم با پیروی از سبک های کلاسیک یا مینیمالیست این کلیشه را تغییر نداده اند.در ادامه نگاهی کوتاه به کلاس ToolStrip می اندازیم و به کمک این کلاس و  کلاس هایی که از آن ارث بری کرده اند ساختار آبشاری منوها را تغییر می دهیم. همانطور که در شکل مشاهده می کنید. بسیار از کنترل هایی که برای نمایش منوها از آنها استفاده می کنیم از کلاس ToolStrip ارث بری می کنند.کلاس هایی که از ToolStrip ارث بری کرده اند.کلاس ToolStripDropDown کنترل مناسبی برای ایجاد یک منوی سفارشی و کاربر پسند است. با ارث بری از این کلاس می توان قابلیت های جالبی را به آن اضافه کرد. در این کلاس خصوصیتی به نام LayoutStyle  وجود دارد که به کمک آن میتوان نحوه چیدمان و آرایش آیتم های داخل منو (کنترل های منو) را تغییر داد. به کمک ارث بری و سفارشی سازی می توان رفتاری های پیش فرض یک کنترل را تغییر داد و قابلیت های آن را ارتقا داد؛ کاری که بیشتر شرکت هایی که در زمنیه کامپوننت های برنامه نویسی فعالیت دارند؛ انجام می دهند مثل DevExpress ، Telerik ، Krypton و غیره.شروع کدنویسی در ابتدا ما یک منوی سفارشی را با ارث بری از ToolStripDropDown  درست می کنیم. هدف از این کار این است که منوی بازشونده ای درست کنیم که بتوانیم تعدادی آیتم (ToolStripItem) را به صورت دلخواه در یک ساختار جدول مانند روی آن قرار دهیم.public class GridMenu : ToolStripDropDown {
           public GridMenu(){
                     InitializeComponent();
           }
}حالا به کمک خاصیت LayoutStyle که مقدار آن را به ToolStripLayoutStyle.Table تنظیم می کنیم. سطح منو را به صورت یک جدول یا گرید که مجموعه ای از سطرها و ستون ها است در می آوریم و آیتم های منو را در هر خانه قرار می دهیم. در این حالت سطح منو به صورت شکل زیر در میاد.سطح منو به صورت جدول یا  گریدخاصیت LayoutStyle  به سه صورت Flow, Stack و  Table  می باشد. در اینجا ما از Table  استفاده می کنیم تا حالت منوی آبشاری را تغییر دهیم. برای مطالعه بیشتر درباره  دوتا حالت دیگر می توانید به مستندات مایکروسافت در رابطه با چیدمان عناصر مراجعه کنید.public class GridMenu : ToolStripDropDown
{
        private Size                                  _tileSize                 = new Size(50, 50);
        private TableLayoutSettings     _tableSettings      = null; 
        public GridMenu ()
        {
            InitializeComponent();
        }
        private void InitializeComponent()
        {
            this.SuspendLayout();
            LayoutStyle         	      = ToolStripLayoutStyle.Table;
            _tableSettings                 = base.LayoutSettings as TableLayoutSettings; 
            this.ResumeLayout(false);
        }
}وقتی که خاصیت LayoutStyle  را به صورت  ToolStripLayoutStyle.Table تنظیم می کنیم در این حالت می توانیم LayoutSettings  را به کلاس TableLayoutSettings تبدیل کنیم (Casting) و برای هر آیتم محل چیدمان را مشخص کنیم. این کار به کمک کلاس TableLayoutPanelCellPosition  امکان پذیر است. به متد زیر دقت کنید. public void AddItem()
{ 
      this.Size = new Size(            
        (_columns * _tileSize.Width)  + (_margin.Left + _margin.Right)  * _columns + _margin.Right,            
        (_rows * _tileSize.Height) + (_margin.Top + _margin.Bottom) * _rows + _margin.Bottom         
       );  
    var btnGoogle = new ToolStripButton (&amp;quotGoogle&amp;quot, Properties.Resources.google, null, &amp;quotbtnGoogle&amp;quot); 
     Items.Add(btnGoogle);  
     var btnGoogleCellPos = new TableLayoutPanelCellPosition(0, 0);
     _tableSettings.SetCellPosition(btnGoogle, btnGoogleCellPos);
}در این متد یک آیتم از نوع ToolStripButton را به منو اضافه کرده ایم و به کمک کلاس TableLayoutPanelCellPosition  مشخص می کنیم که در کدام خانه جدول از منو قرار بگیرد به کد دقت کنید. در کد بالا ابتدا سایز کل سطح منو را مشخص می کنیم ( با توجه به تعداد سطرها و ستون ها و اینکه هر خانه از جدول چه ابعادی دارد و اینکه فاصله یا Margin هر خانه از همدیگر چقدر باشد). سپس یک آیتم از نوع ToolStripButton تعریف کرده ایم و به لیست آیتم های موجود اضافه کرده ایم. در نهایت به کمک کلاس TableLayoutPanelCellPosition مشخص کرد ایم که در اولین خانه از جدول قرار بگیرد. در صورتی که بخواهیم که یک آیتم در منو به صورت افقی یا عمودی کشیده شود یا اینکه چند خانه را با هم دربر بگیرد از متدهای SetColumnSpan و SetRowSpan  استفاده می کنیم. تا اینجا ما فقط منوی مورد نظر را ایجاد کرده ایم ولی برای اینکه بتوانیم در یک فرم از آن استفاده کنیم کافی است که بعد از قرار دادن منوی روی فرم آنرا به یک MenuStrip یا ToolStrip اختصاص بدهیم. به کد زیر دقت کنید که در سازنده فرم نوشته شده است :public partial class FormMain : Form    {
        public FormMain()
        {
            InitializeComponent();
            gridMenu2.AddItem(); 
            toolStripDropDownButton1.DropDown = gridMenu2; 
        }
  }در کد بالا بعد از فراخوانی متد AddItem  که آیتم های منو را اضافه می کند و هر کدام را در جای خودش قرار میدهد(اصطلاحا Initializing) ؛ منویی را که درست کرده ایم را به یک toolStripDropDownButton1 اختصاص داده ایم تا در صورتی که روی آن کلیک شد منوی مورد نظر ما را نمایش دهد. نتیجه را در تصویر زیر می توانید ببینید.نتیجه طراحی منو به صورت گریدلینک کامل سورس برنامه برای نمایش منو در حالتهای مختلف را در گیت هاب من می توانید مشاهده کنید در صورتی که سوال داشتید در قسمت Issues مطرح کنید.GitHub: https://github.com/SoranRad/GridMenuاستفاده از ToolStripControlHost  این قسمت به کلاس ToolStripControlHost می پردازد و به کمک آن همان منوی قبلی را طراحی می کند. استفاده از کلاس ToolStripControlHost روش دوم برای ساخت منوی دلخواه  با چیدمانی متفاوت  است . مثل روش قبل یک کلاس را از ToolStripDropDown ارث بری می کنیم :    public class PopupControl : ToolStripDropDown
    {
        public PopupControl()
        {
            InitializeComponent();
        }
    }حالا باید یک UserControl درست کنیم و کنترل هایی رو که می خواهیم روی آن قرار دهیم. به شکل زیر دقت کنید. چند کنترل دکمه را روی یک UserControl قرار داده ایم.کنترل سفارشی (UserControl) برای نمایش در یک منو حالا کافی است که متد AddItem  را همانند روش قبل به صورت زیر تغییر بدهیم.public void AddItem()
        {
            var control = new CustomControl()
            {
                Name = &amp;quotpopupControl1&amp;quot,
                Size = new Size(304, 258)
            };   
            Host = new ToolStripControlHost(control)
            {
                Margin              = Padding.Empty,
                Padding             = Padding.Empty,
                AutoSize            = false,
                AutoToolTip         = false,
                DoubleClickEnabled  = true,
            };
            this.Size = new Size(
                control.Width+10,
                control.Width+10
            );
            Host.Size = new Size(control.Size.Width, control.Size.Height);
            control.Anchor = AnchorStyles.Bottom
                             | AnchorStyles.Left
                             | AnchorStyles.Right
                             | AnchorStyles.Top;
            this.Items.Clear();
            this.Items.Add(Host);
        }در کد بالا ابتدا یک نمونه از کنترل سفارشی UserControl  را نمونه سازی کرده ایم و بعد از آن به کمک ToolStripControlHost  این کنترل را به مجموعه آیتم های منو اضافه کرده ایم در اصل از منو خواسته ایم که کنترل سفارشی مورد نظر را به صورت Popup نمایش دهد. کلاس ToolStripControlHost  یک Host یا Container برای کنترل سفارشی ما محسوب می شود و وظیفه آن گرفتن یک کنترل و قرار دادن آن به عنوان یک آیتم منو در لیست آیتم هاست. نتیجه کد بالا را می توانید در تصویر زیر ببینید.نمایش یک کنترل سفارشی در منوسورس کامل مربوط به این بخش را هم می توانید در آدرس گیت هابی که قبلا ذکر شد پیدا کنید. در این ریپازیتوری من مثال های خوبی از نحوه ساخت و استفاده از منوهای سفارشی رو ارائه کرده ام و می توانید از مثال های آن استفاده کنید.نتیجه گیری بسیاری از برنامه ها دیگر از منوهای آبشاری و کلاسیک استفاده نمی کنند و برای آنکه تجربه متفاوتی به کاربر حین استفاده از برنامه بدهند بسیاری از کنترل های پیش فرض فریمورک دات نت را مجددا توسعه و سفارشی سازی می کنند. با نگاهی گذرا به منوها و ToolBars و ToolStrip های برنامه هایی مانند FireFox , GoogleChrome ,  Office 365   و بسیاری دیگر از برنامه ها مشاهده می کنید که منوهای آن ها آیتم های بسیار متفاوتی را نمایش می دهند که کاربر در استفاده از آنها بسیار راحت تر است. در این مقاله سعی کرده ام به دو روش ساده، طراحی این سبک از منو ها را آموزش دهم.قدم بعدی در انتها قصد دارم که به دو نکته اساسی دیگر درباره آیتم های منو اشاره کنم که می تواند ظاهر برنامه ها را بسیار تحت تاثیر قرار دهد .همانطور که در تصویر بالا مشاهده کردید کلاس ToolStripItem والد تمام آیتم هایی است که در منو نمایش داده می شود حالا چه آنها در  MenuStrip  یا ToolStrip  یا حتی ToolStripDropDown  قرار گرفته باشند. پس شما راحت می توانید با ارث بری از ToolStripItem یا حتی هر کدام از کلاس زیر مجموعه آن یک منوی کاملا سفارشی مختص به برنامه خود را داشته باشید و در برنامه های تان از آنها استفاده کنید.همانطور که من این کار در گیت هاب این مقاله انجام داده ام به آدرس زیر بروید.https://github.com/SoranRad/GridMenu/blob/main/src/DotNetFramework/Components/TdToolStripButton.csنحوه ی رندر کردن منوهای برنامه خود را می توانید بسیار بیشتر سفارشی سازی کنید و شکل و شمایل منوها را در حالت های انتخابی یا هاور شده(Hover) یا غیرفعال شده(Disable) را سفارشی کنید. به شکل های زیر دقت کنید.انواع تم  ظاهری با زندینگ حرفه ایهر کدام از تصاویر بالا روش رندر Rendering مختص به خود را دارد؛ برای پیاده سازی یک منو با ظاهر فوق می توانید یک کلاس سفارشی شده از ToolStripProfessionalRenderer پیاده سازی کنید و در برنامه تان از آنها استفاده کنید. بحث درباره این کلاس در حوصله این مقاله نمی باشد و خود نیاز به مطالب بیشتری دارد ولی برای مطالعه بیشتر می توانید به لینک های زیر مراجعه کنید.مستندات مایکروسافت برای رندرینگ حرفه ایبرای مثال یک نمونه رندر سفارشی شده را در آدرس زیر می توانید مشاهده کنید.https://github.com/SoranRad/Desktop-Application-Component-libraries/tree/main/src/MS_Control/Render</description>
                <category>SoranRad.com</category>
                <author>SoranRad.com</author>
                <pubDate>Sat, 11 Mar 2023 10:19:14 +0330</pubDate>
            </item>
                    <item>
                <title>بررسی روش های معماری نرم افزار</title>
                <link>https://virgool.io/20devs/%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%B1%D9%88%D8%B4-%D9%87%D8%A7%DB%8C-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%DB%8C-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-hlc1750ke44o</link>
                <description>مقدمهدر این مقاله به تشریح معماری های نرم افزار می پردازیم و لایه های معماری را در آنها بررسی می کنیم. تاریخچه ای از هر معماری را ارائه می دهیم و سیر تکامل معماری ها را تا رسیدن به معماری Clean بررسی می کنیم. در پایان به اختصار هر سه معماری را با هم مقایسه می کنیم.در بیشتر وبلاگ ها، برنامه نویسان تنها بر روی یک معماری تمرکز می کنند، آن را تشریح و سپس پیاده سازی می کنند و از نظر من این کار نقص بزرگی در ارائه و حتی شناخت یک معماری است چون ابتدا باید تاریخچه از معماری را ارائه کرد و سپس سیر تکامل آنها تا رسیدن به معماری های امروزی درک کرد. هدف من از نوشتن این مقاله شفاف سازی این مسئله بود. بسیاری از پیاده سازی هایی که برای هر معماری ارائه می شود بسیار شبیه به هم بوده و می توان گفت مربوط به معماری Clean می باشند و این ناشی از عدم درک درست برنامه نویس از آن معماری است.معماری هایی که در این مقاله بررسی می شود شامل موارد زیر است :معماری لایه ای (Layered)معماری شش ضلعی (Hexagonal)معماری پیازی (Onion)معماری تمیز (Clean)در ادامه به شرح هر کدام می پردازیم ولی سه معماری آخر را با هم بررسی خواهیم کرد.معماری لایه ای (Layered)لایه بندی روش رایجی است برای جداسازی و سازماندهی کدها بر اساس نقش و مسئولیتی که دارند. بدین صورت که کدها را به واحدهایی تقسیم می کنیم و این واحد از لحاظ وظیفه ای که بر عهده دارند در یک لایه قرار می گیرند. در نهایت به این نتیجه می رسیم که هر لایه مسئولیت هایی جدا از سایر لایه ها دارد. در این معماری هر لایه با لایه های زیرین خود در ارتباط است و به صورت مستقل از لایه های بالایی کار می کند.نمایی از معماری 3 لایه سیر تحولی معماری لایه ای تا قبل از سال 2003 بسیار مفصل است و در حوصله این مقاله نمی باشد ولی در آن سال آقای اریک ایوانس با انتشار کتاب  Domain-Driven Design: Tackling Complexity in the Heart of Software.   علاوه بر بیان بسیاری از مفاهیم کلیدی در رابطه با دامین ها ، نسخه هایی از لایه بندی  یک سیستم نرم افزاری را نیز بیان کرد.به صورت کلی هر سیستم نرم افزاری شامل 3 لایه می باشد. لایه Presentationلایه Bussinessلایه DataAccessکه البته این لایه ها در کتاب آقای ایوانس به صورت زیر تغییر پیدا کرد  و این به دلیل مطرح شدن برنامه های تجاری ، افزایش تعداد کاربران ، مفاهیم ابری و به وجود آمدن پیچیدگی در برنامه ها و لایه های Infrarstucture بود.البته لایه User Interface با نام لایه Presentation  هم شناخته می شود. این لایه ها در تمامی معماری ها وجود دارند ولی ممکن است که این اسامی در آنها کمی متفاوت باشد یا اینکه بعضی از لایه های با هم ترکیب شده اند. در ادامه به تشریح وظیفه هر لایه و اینکه چه نوع کدی در هر لایه قرار می گیرد می پردازیم.لایه Presentation این لایه وظیفه تعامل با کاربران را دارد و دستورات کاربران را به لایه Application  می رساند و کدهایی نظیر HTML یا فرم های ویندوزی در این لایه قرار می گیرند. البته ارتباط با سیستم های خارجی مانند API نیز در این لایه قرار می گیرند و مانند یک دروازه یا درگاه عمل می کند. برای مثال در یک برنامه تحت وب درخواست های کاربر در این لایه از کاربر گرفته می شود و پاسخ به آن در این لایه شکل دهی می شود و به کاربر نمایش داده می شود. امکاناتی که برنامه ارائه می دهد در این لایه قرار دارد.کدهای Controller  و Dto  و Api و در برخی برنامه ها صفحات HTML در این لایه قرار می گیرند. البته با رشد برنامه های تجاری و تعداد کاربران آنها ، این لایه نیز پیچیدگی های خود را خواهد داشت و ممکن است که در یک Solution جدا توسعه داده شود.لایه Applicationدوست دارم این لایه را همانند رهبر ارکستر تعریف کنم و که وظیفه آن ایجاد هماهنگی بین لایه های دیگر است. نقش یک رهبر ارکستر را دارد. در برخی از سناریوها نیز که ممکن چند موجودیت در حین اجرای یک درخواست کاربر دچار تغییراتی شوند در این لایه می توان محدوده تراکنش را تعیین کرد. به عنوان مثالی دیگر ممکن است برای پاسخ به یک درخواست کاربر یک سری متوالی از دستورات صورت گیرد؛ در این لایه است که این توالی دستورات اجرا می شود.کدهایی را در این لایه قرار دهید که نه به لایه Presentation  و نه به لایه  Domain  تعلق دارند و وظیفه هماهنگی کننده را دارند و در ضمن حاوی هیچ گونه Bussiness Logic یا همان قوانین تجاری نیستند.لایه Domainاین لایه در واقع لایه ای است که آقای ایوانس بیشترین مانور را در آن داده است و کلیه قوانین تجاری در آن قرار دارد. این لایه هسته سیستم و به تعبیری قلب سیستم نامیده می شود. قوانین تجاری، قوانینی هستند برای حل مسائلی که در دامنه مطرح می شوند. این لایه به هیچ یک از لایه های اصلی دیگر ارجاعی ندارد و مستقل از آنها عمل می کند.کدهایی که در این لایه قرار دارند شامل Entities ، Events ، Aggregates ، Domain Services ، Factories ،  Interfaces و Value Object ها و سایر کلاس هایی که به قوانین تجاری مربوط می شوند.لایه Infrastructureقابلیت های تکنیکالی سیستم مانند ارتباط با سرویس های خارجی در این لایه پیاده سازی می شوند. کدهای مربوط به بانک اطلاعاتی ، ارسال ایمیل یا SMS ، کار با فایلها ، ثبت لاگهای سیستم، فراخوانی API سیستم های خارجی و سرویس های ارسال پیام(Messaging Services) نظیر RabbitMq در این لایه پیاده سازی می شوند.کدهایی که در این لایه قرار می گیرند که یکی از وظایف بالا را برعهده دارند. اینترفیس هایی که در لایه Domain یا Application برای دسترسی به بانک اطلاعاتی یا سایر سرویس های دیگر تعریف می شوند در این لایه پیاده سازی می شوند.  به عنوان مثالی دیگر ارتباط با سرویس ارسال پیام کوتاه در این لایه پیاده سازی می شود.در ادامه خواهید دید که هر معماری چه نوع نگرشی نسبت به لایه ها دارد و چگونه آنها را در کنار هم قرار می دهد و چه الگوی جدیدی را به دنیای نرم افزار ارائه می دهد. تاریخچه ای از هر معماری را ارائه خواهیم کرد و درنهایت به بررسی معماری را در کنارهم خواهیم پرداخت.معماری Hexagon(Porta &amp; Adapters)این معماری در سال 2005 توسط Alistair Cockburn معرفی شد. این معماری به برنامه اجازه می دهد تا بطور یکسان توسط کاربران یا برنامه های دیگر و اسکریپت های آزمایشی مورد استفاده قرار گیرد و از طرفی دیگر جدا از دستگاه و بانک اطلاعاتی در زمان اجرا  توسعه داده شود یا آزمایش گردد. در ادامه هر بار که در باره معماری Hexagonal صحبت کردیم بدانید که در واقع منظور از لایه Application  اشاره به دو لایه Application  و Domain در کنار هم است که شامل Business Logic  می باشند.منظور بند قبلی این است که لایه Application  و Domain جدا از  لایه های Infrastructure  و Presentation  توسعه داده شود و آزمایش شود و از طرفی دیگر کاربران که از برنامه استفاده می کنند یا اسکریپت هایی که برنامه را تست می کنند به طور یکسان از لایه های Application  و  Domain استفاده کنند. تلاش این معماری بر دولایه بیرونی یعنی Infrastructure  و Presentation است به طوری که لایه Application را از جزئیات بیرونی بدور نگه دارد. این کار را با استفاده از Port ها و Adaptor ها انجام داده است. این معماری تمرکز خود را بر لایه های بیرونی قرار داده است.معماری  Hexagonal  یا   Port&amp;Adapters اصطلاحاتی در این معماری رایج است به طور مختصر به شرح زیر است:ابتدا بیاید در باره دو فضا که در این معماری بسیار مهم هستند صحبت کنیم که عبارتند از :Primary/Driving Secondary/Drivenفضای Driving  در واقع امکاناتی است که برنامه ارائه می کند و  به هسته برنامه می گویند که چکاری را انجام دهد. همانطور که از شکل پیداست لایه Presentation در این فضا قرار می گیرد.فضای  Driven نیز امکانات و ابزارهایی است که برنامه از آنها استفاده می کند و آنها را فراخوانی می کند همانند سرویس های ارسال ایمیل و بانک اطلاعاتی و غیره و همانطور که از شکل پیداست لایه Infrastructure  در این فضا قرار دارد.حال بیاید به مفهوم Port بپردازیم. پورت چیست؟ Port ها چیزی بیشتر از Interface ها نیستند یعنی تعریف کننده مشخصاتی است که دو فضای معماری چگونه می توانند از هسته برنامه (لایه های Application و Domain) استفاده کنند و همچنین هسته برنامه (لایه های Application و Domain)  چگونه از این فضاها استفاده کند. همانطور که در شکل می بینید Port ها متعلق به لایه های داخلی یعنی Bussiness Logic یا لایه Application است و باید مطابق با نیازهای آن تعریف شود.برای مثال الگوی Repository را در نظر بگیرید که اینترفیس آن یک پورت است و پیاده سازی آن در لایه Infrastructure است که یک آداپتور است و در لایه Application ممکن است که به آن نیاز داشته باشیم.منظور از Adaptor  چیست ؟  واحدهایی از کد هستند مثل کلاس که هسته برنامه (لایه های Application و Domain) را به لایه های بیرونی متصل می کنند و می توانند پیاده سازی هایی از اینترفیس های Port باشند. آداپتورها در لایه های بیرونی برنامه قراردارند (لایه Presentation  و Infrastructure ) در صورتی که در فضای Primary/Driving باشند به برنامه می گویند که چکاری انجام دهد و در صورتی که در فضای Secondary/Driven باشند این هسته برنامه است که به لایه بیرونی می گوید که چه کار را انجام دهد.  ویژگی این معماری این است که Adaptor ها تنها به یک ابزار خاص وابسته نمی شوند و حتی محدود به یک Port نیز نمی شوند و این یعنی اینکه شما می توانید از Adaptor های مختلفی استفاده کنید مثلا برای بانک اطلاعاتی از ابزارهای مختلفی استفاده کنید. ولی در طرف دیگر هسته برنامه تنها به Port یا اینترفیس وابسته است یعنی به انتزاع وابسته است و این یعنی اصل آزادی در انتخاب یا تغییر ابزارهای پیاده سازی شده با Adaptor ها که محدود به نوع خاصی نیست.  مزیت استفاده از این معماری این است که هسته برنامه کاملا از جزئیات پیاده سازی در لایه های بیرونی دور نگهدارشته می شود و می تواند به صورت مجزا توسعه و تست شود.معماری Onion  این معماری در سال 2008 توسط Jeffrey Palermo معرفی شد. ایده اصلی آن استفاده از Domain  و Domain Service  در هسته برنامه بود. و به عنوان انقلابی بر معماری قبلی قرار گرفت. چون مفاهیم دنیای DDD را به هسته برنامه منتقل کرد و باعث شد که لایه های Infrastructure  و Presentation  بیشتر به بیرون رانده شوند. این معماری باعث شد که دو مفهوم Application Service و  Domain Service  را از هم جدا شوند.این معماری هم همانند معماری قبل به برنامه این امکان را می دهد که بدون نیاز به لایه های Infrastructure و Presentation بتواند اجرا شود و این قابلیت مناسبی برای تست کردن برنامه است. این لایه تمرکز خود را بر لایه Domain برنامه قرار داد و مفاهیم دنیای DDD را بیشتر در این لایه وارد کرد. معماری   Onion  بر گرفته از وبلاگ Jeffrey Palermoمعماری Cleanاین معماری در سال 2012 توسط Robert C.Martin منتشر شد و موضوعات اصلی در این معماری همانند معماری های دیگر است. مستقل از ابزارهای پیاده سازی و قابلیت تست پذیری به صورت جدا.در نمودار پایین تلاش شده است تا ایده های معماری جدید را در یک ایده جمع کند. در واقع این معماری به گونه ای تکمیل شده معماری های دیگر است که در آن  سعی شده است تا مفاهیم جدید دنیای نرم افزار در یک معماری جمع شود. معماری Clean  از وبلاگ Robert C.Martinتکامل معماری ها تا اینجا شرح مختصری از هر معماری ارائه گردید در ادامه توضیحاتی را برای جمع بندی مقاله ارائه می دهیم. همانطور که در توضیحات اشاره شد معماری ها برای سازماندهی کد و جداسازی مفاهیم و ابزارهای پیاده سازی از لایه بندی استفاده می کنند و واحدهای کد را در لایه های خود جای می دهند. هر معماری تمرکز خود را بر روی یک یا چند لایه متمرکز می کند و سعی دارد تا حالتی بهینه و کارآمد ارائه دهد. از لحاظ تست پذیری، توسعه پذیری هر معماری الگوهایی را ارائه می دهد.زیر بنای همه معماری ها بر معماری  Hexagonal قرار دارد که خود بر روی معماری لایه ای استوار است. این معماری تمرکز خود را به کمک Adaptor و Port به جداسازی لایه Application از لایه های بیرونی یعنی  Presentation و Infrastructure گذاشته است و مانع از وابستگی لایه Application به جزئیات پیاده سازی و مکانیسم های تحویل می شود در این معماری بیشتر به لایه های خارجی توجه شده است. بعد از آن معماری Onion به معماری قبلی لایه های دیگری را با الهام از مبانی DDD اضافه کرد و تمرکز خود را در لایه های داخلی یعنی لایه های Business یا لایه Application  و Domain قرار داد و البته از قواعدی که درباره لایه های خارجی معماری Hexagonal وضع شده بود پیروی کرد. شاید لایه Domain Service بیشترین مانوری باشد که این معماری از خود نشان داده است. اما درباره معماری تمیز Clean این معماری بیشتر سعی می کند تا ایده های معماری های اخیر را در یک معماری واحد جمع کند و ایده هایی که در لایه های مختلف به وجود آمده اند در این معماری جایگاهی پیدا کرده اند. مانند انواع روشها در پیاده سازی رابط کاربری و با انواع پیاده سازی در بانک های اطلاعاتی مختلف و انواع دستگاه هایی که برنامه بر روی آنها قرار می گیرد یا Design Pattern هایی که ممکن است در لایه Application استفاده شود. همه این ایده ها در معماری تمیز مکان مختص به خود را دارند.  در رابطه با جهت وابستگی یا همان (Dependencies Direction) باید گفت که در همه معماری ها جهت آن به سمت داخل می باشد بدین معنا که لایه های داخلی درباره جزئیاتی که در لایه های بیرونی پیاده سازی می شود به هیچ عنوان اطلاعاتی ندارند و هنگامی که داده ای را از یک محدوده به سمت داخل عبور می دهیم برای مثال برای پاسخ دادن به یک درخواست کاربر این اطلاعات در قالبی که برای لایه های داخلی راحت تر است فرستاده می شود. در کل همه معماری ها اصل  Dependency Inversion Principle  را در سطح معماری خود به خوبی معرفی کرده اند. در معماری Hexagonal در واقع باید بگوییم که ما دو لایه بیشتر نداریم  و آن لایه داخل  Application یا Business و لایه خارج از Application.  و همانطور که قبلا گفته شد این معماری بر جداسازی مفاهیم پیاده سازی و مکانیسم های تحویل از منطق تجاری برنامه بیشتر تاکید دارد. معماری Onion  با تاکید بر معماری قبلی لایه Application را با تعریف های مبانی DDD متحول کرد و Application Service و Domain Service  را به معماری قبلی اضافه کرد. در معماری Clean هنگامی که با معماری Onion مقایسه می کنیم اسامی کمی تغییر می کنند و Application Service به Use Case تغییر می کند و Domain Service به Entities تغییر می کند البته Entities مفهوم گسترده تری طبق گفته C.Martin دارد و آن شامل مجموعه ای از  Data Structure  ها و Function ها می شود. همان تعریف هایی که در DDD قبلا گفته شده بود. از لحاظ تست پذیری همه معماری ها با جداسازی لایه های Application و Domain از سایر لایه ها شرایطی را فراهم کرده اند که می توان این دو لایه را به صورت مجزا تست کرد و ابزارهای بیرونی را به سادگی mock کرد.نتیجه گیری هدف از این مقاله این بود که شرح اجمالی از تاریخ معماری نرم افزار و لایه بندی هر کدام ارائه گردد و اینکه در هر لایه از یک معماری چه نوع کدهایی زندگی می کنند و در پروژه ها هنگامی که قصد داشتیم کدی را به پروژه اضافه کنیم با علم و اطمینان کافی بدانیم که آن کد را به کدام لایه اضافه کنیم.همچنین برای درک یک معماری لازم است تا تاریخچه معماری ها را بدانیم و سیر تکاملی آنها را تا رسیدن به معماری Clean به خوبی درک کنیم. عملا بررسی یک معماری ممکن است کار بیهوده و ناقصی به نظر برسد چون هر معماری با تکیه بر مبانی معماری قبلی الگوهایی را به آن اضافه کرده است. بنابراین شاید این درست به نظر نرسد که یک معماری را به تنهایی مورد بررسی قرار داد. ناگفته نماند بیشتر پیاده سازی هایی که من در گیت هاب برای پیاده سازی معماری ها در Net. مشاهده کردم یکسان بوده و اغلب برای معماری تمیز بوده است. کاربران زیادی از نمودارهایی برای توصیف یک معماری استفاده می کردند که هیچ ربطی به آن معماری نداشت و هرگز به وبلاگ سازنده معماری ارجاعی نداده بودند. درنهایت باید بگویم که معماری تمیز در واقع بهترین گزینه برای پیاده سازی یک پروژه است چون در آن جایگاه بسیاری از Pattern های جدید در معماری مشخص شده است و این معماری بر شانه های معماری های قبلی ایستاده است و صادقانه باید بگویم که الگوی جدیدی را به معماری های قبلی خود اضافه نکرده است. شاید بهتر باشد که بگوییم معماری تمیز در واقع یک کشف است از الگوهایی که در بسیاری از لایه ها وجود داشته اند و حالا در یک معماری واحد جمع شده اند.منابعhttps://alistair.cockburn.ushttps://jeffreypalermo.com/2008/07/the-onion-architecture-part-1https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.htmlhttps://herbertograca.com/2017/07/03/the-software-architecture-chronicleshttps://dzone.com/articles/clean-architecture-is-screaminghttps://ademcatamak.medium.com/layers-in-ddd-projects-bd492aa2b8aa</description>
                <category>SoranRad.com</category>
                <author>SoranRad.com</author>
                <pubDate>Wed, 19 Oct 2022 13:19:38 +0330</pubDate>
            </item>
                    <item>
                <title>اعتبارسنجی فایل بر اساس محتوا در  FluentValidation</title>
                <link>https://virgool.io/20devs/%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D9%81%D8%A7%DB%8C%D9%84-%D8%A8%D8%B1-%D8%A7%D8%B3%D8%A7%D8%B3-%D9%85%D8%AD%D8%AA%D9%88%D8%A7-%D8%AF%D8%B1-fluentvalidation-tsopbje2f3ga</link>
                <description>مقدمهاین مقاله پایان سری مقالات اعتبارسنجی فایل بر اساس محتوا در net. است و در این قسمت به بررسی اعتبارسنجی فایل به کمک کتابخانه FluentValidation  خواهیم پرداخت.کتابخانه  FluentValidation برای ساخت قوانین اعتبارسنجی کاربرد دارد. شاید قبلا با برخی از Attribute  های فضای نام  System.ComponentModel.DataAnnotations  کار کرده باشید برای نمونه Required. این کلاس ها به DataAnnotation معروفند و به صورت Attribute تعریف می شوند که در بالای Property قرار می گیرند و نحوه اعتبارسنجی داده را مشخص می کنند.ولی در کتابخانه FluentValidation در واقع این قوانین اعتبارسنجی به صورت توابع Fluent  اعمال می شوند یک نمونه از کاربرد آن را در زیر می توانید ببینید و برای توضیحات بیشتر می توانید به وب سایت خود کتابخانه    https://fluentvalidation.net مراجعه کنید.public class CustomerValidator : AbstractValidator&lt;Customer&gt;
{
  public CustomerValidator()
  {
    RuleFor(x =&gt; x.Surname).NotEmpty();
    RuleFor(x =&gt; x.Forename).NotEmpty().WithMessage(&amp;quotPlease specify a first name&amp;quot);
    RuleFor(x =&gt; x.Discount).NotEqual(0).When(x =&gt; x.HasDiscount);
    RuleFor(x =&gt; x.Address).Length(20, 250);
    RuleFor(x =&gt; x.Postcode).Must(BeAValidPostcode).WithMessage(&amp;quotPlease specify a valid 
                  postcode&amp;quot);
  }
  private bool BeAValidPostcode(string postcode)
  {
    // custom postcode validating logic goes here
   }
}دقت کنید در عمل تفاوت FluentValidation و DataAnnotations  در این است که FluentValidation قواعد اعتبارسنجی را برای یک کلاس تعریف می کند ولی برای DataAnnotation  اغلب ما این قواعد را برای یک Property تعریف می کنیم.این کتابخانه دارای کلاسی به اسم PropertyValidator  می باشد که با کمک آن می توانید یک قاعده اعتبارسنجی را تعریف کنید و سپس در هر جایی که لازم بود بارها از آن استفاده کنید.حال می خواهیم قاعده ای را برای اعتبارسنجی فایل ها همانند مقاله قبل تعریف کنیم که از نظر انداره و محتوا اعتبارسنجی را به کمک کتابخانه MimetypeDetective انجام دهیم.ابتدا باید کلاسی را که از PropertyValidator ارث بری می کند تعریف کنیم: public class FileValidation&lt;T, TProperty&gt; : PropertyValidator&lt;T, TProperty&gt;
{
}همانطور که می بینید این کلاس به صورت جنریک می باشد و اساس کار این کتابخانه دقیقا به این صورت که به کلاس وابسته است و در واقع اعتبارسنجی را برای یک strongly-typed انجام می دهد. بعد از تعریف کلاس باید متد IsValid  را Override  کنیم یعنی مجددا پیاده سازی کنیم. قبل از این کار تعدادی Property   تعریف می کنیم تا قواعد را مشخص کنیم و پیغام مناسب برای هر کدام را برگردانیم.در نهایت کد بالا به صورت زیر تغییر می کند :public class FileValidation&lt;T, TProperty&gt; : PropertyValidator&lt;T, TProperty&gt;
    {
        public bool          IsRequied                                                { get; set; } = false;
        public string       RequiredErrorMessage                          { get; set; }
        public int             MaximumFileSize                                   { get; set; }
        public string       MaximumFileSizeErrorMessage           { get; set; }
        public string       ExtensionAcceptable                              { get; set; }
        public string       ExtensionAcceptableErrorMessage     { get; set; }

        private IEnumerable&lt;string&gt; Extensions
        {
            get
            {
                return ExtensionAcceptable.Split(&#039;,&#039;)
                    .Select(x =&gt;
                        x.Trim().StartsWith(&#039;.&#039;)
                            ? x.Trim().ToLower()
                            : &amp;quot.&amp;quot + x.Trim().ToLower()
                    );
            }
        }
        public override bool IsValid(ValidationContext&lt;T&gt; context, TProperty value)
        {
            var file = value as IFormFile;
            if (file == null || file.Length == 0)
            {
                context.AddFailure(context.PropertyName,RequiredErrorMessage);
                return false;
            }
            if (file.Length &gt; MaximumFileSize)
            {
                context.AddFailure(context.PropertyName, MaximumFileSizeErrorMessage);
                return false;
            }
            using (var stream = file.OpenReadStream())
            {
                var mimetype = stream.GetMimeType();
                if (mimetype == null)
                {
                    context.AddFailure(context.PropertyName, ExtensionAcceptableErrorMessage);
                    return false;
                }
                if (!Extensions.Contains(mimetype.Extension))
                {
                    context.AddFailure(context.PropertyName, ExtensionAcceptableErrorMessage);
                    return false;
                }
            }
            return true;
        }
        public override string Name { get; }
        protected override string GetDefaultMessageTemplate(string errorCode)  =&gt; &amp;quot&amp;quot
    }در کد بالا ما تعدادی پراپرتی تعریف کرده ایم که فایل را بر اساس داشتن محتوا ، اندازه فایل و همچنین نوع فایل قابل پذیرش بررسی می کند و به ازای هر کدام هم پیغام مناسب را به کاربر در صورت بروز خطا برمیگرداند.مقدار برگشتی تابع  IsValid   از نوع bool  می باشد بدین معنا که اگر قاعده ای نقض شد مقدار False را بر می گردانیم و متن خطا را در Context به صورت زیر اضافه می کنیم.context.AddFailure(context.PropertyName, ExtensionAcceptableErrorMessage);دقت کنید که در این کلاس ما پسوندهایی که برای اعتبارسنجی قابل پذیرش هستند را به صورت پسوند و با کاما از هم جدا کرده ایم.حالا می خواهیم برای کلاس زیر که فرم اطلاعات را از کاربر دریافت می کند یک کلاس اعتبارسنجی بنویسیم.public class FileValidationViewModel
    {
        [DataType(DataType.Upload)]
        public IFormFile Logo { get; set; }
    }برای این کار باید یک کلاس اعتبارسنجی تعریف کنیم که از AbstractValidator ارث بری میکند :public class FileValidationViewModelValidator : AbstractValidator&lt;FileValidationViewModel&gt;    {
        public FileValidationViewModelValidator()
        {
            var fileValidator = new FileValidation&lt;FileValidationViewModel, IFormFile&gt;()
            {
                IsRequied                                               = false,
                RequiredErrorMessage                        = &amp;quotPlease, select your logo&amp;quot,
                MaximumFileSize                                 = 2 * 1024 * 1024,
                MaximumFileSizeErrorMessage        = &amp;quotYour file is too big&amp;quot,
                ExtensionAcceptable                           = &amp;quotjpg,bmp,png&amp;quot,
                ExtensionAcceptableErrorMessage  = &amp;quotYour logo not supported&amp;quot
            };
            RuleFor(x =&gt; x.Logo).SetValidator(fileValidator);
        }
    }همانطور که مشاهده می کنید کلاس FileValidationViewModelValidator در اصطلاح یک کلاس Validator  می باشد و کار آن تعریف قواعد اعتبارسنجی برای کلاس FileValidationViewModel می باشد و از AbstractValidator ارث بری کرده است که به صورت جنریک به کلاس FileValidationViewModel اشاره دارد.در ادامه کد همانطور که مشاهده می کنید از کلاس FileValidation نمونه سازی کرده ایم و به کمک تابع SetValidator  آن را به Property مربوطه آن یعنی Logo  اختصاص داده ایم. بدین صورت محتوای فایل اعتبارسنجی می شود.آدرس GitHubکد کامل مقاله فعلی را می توانید از آدرس گیت هاب مشاهده کنید و نحوه استفاده از آن را در یک پروژه کامل مشاهده کنید.سخن آخردر طول این مقالات سعی شد که نحوه ی اعتبارسنجی یک فایل بر اساس محتوا را بررسی کنیم؛ با mimetype  آشنا شدیم و در کدهایی که برای اعتبارسنجی نوشتیم از آن استفاده کردیم؛ از کتابخانه های MimetypeDetective و کتابخانه محبوب  FluentValidation  در کدهایی که نوشتیم استفاده کردیم.امیدوارم که این مقالات برای شما مفید و قابل استفاده بوده باشند. در ادامه آدرس سایر مقالات را مشاهده می کنید.نحوه تشخیص Mimetype یک فایل بر اساس محتوای آن در Net.اعتبارسنجی فایل بر اساس محتوا در Net.توجه داشته باشید که این مقاله ممکن است به مرور زمان تغییر کند یا اصطلاحاتی در آن صورت گیرد.</description>
                <category>SoranRad.com</category>
                <author>SoranRad.com</author>
                <pubDate>Thu, 08 Sep 2022 11:31:28 +0430</pubDate>
            </item>
                    <item>
                <title>اعتبارسنجی فایل بر اساس محتوا در Net.</title>
                <link>https://virgool.io/20devs/%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D9%81%D8%A7%DB%8C%D9%84-%D8%A8%D8%B1-%D8%A7%D8%B3%D8%A7%D8%B3-%D9%85%D8%AD%D8%AA%D9%88%D8%A7-%D8%AF%D8%B1-net-v2d26wqtrpxu</link>
                <description>مقدمه این مقاله به بررسی اعتبارسنجی فایل بر اساس محتوا می پردازد. در مقاله قبلی، نحوه تشخیص ماهیت یک فایل یا  Mimetype  آن را بررسی کردیم. یکی از کاربردهای تشخیص Mimetype  ، اعتبارسنجی فایل بر اساس محتوا است برای مثال وقتی که فقط فایل های تصویری قابل پذیرش هستند.در ادامه برای اعتبارسنجی فایل یک کلاس به اصطلاح Validator می نویسیم و از آن به صورت یک Attribute  در بالای هر Property استفاده می کنیم.کدهای زیر در زمان نگارش این مقاله بر مبنای 6 Net Core.  نوشته شده است.نمونه ای  از خطای  عدم پذیرش نوع فایل برای شروع ابتدا باید یک Attribute  برای انجام اعتبارسنجی تعریف کنیم. کلاسی تعریف می کنیم که از  System.Attribute  و همچنین از اینترفیس IModelValidator ارث بری کند. ترکیب این دو باعث می شود که ما بتوانیم Attributeی تعریف کنیم که در هنگام اعتبارسنجی به آن مراجعه شود و برای  Bind کردن اطلاعات به یک Property ، درستی اطلاعات بررسی شود. کلاس را به صورت زیر تعریف می کنیم :public class FileValidate :Attribute, IModelValidator
{
}دقت کنید که اینترفیس IModelValidator  در فضای نام زیر قرار دارد:Microsoft.AspNetCore.Mvc.ModelBinding.Validation.IModelValidator در ادامه این کلاس به عنوان یک Attribute استفاده می شود. حال یک ViewModel برای دریافت فرم اطلاعات از کاربر به صورت زیر تعریف می کنیم :public class FileValidationViewModel
{
        public string            Title         { get; set; }
	public IFormFile      Logo       { get; set; }
}برای استفاده از Attributeی  که در بالا تعریف کرده ایم و با توجه به اینکه می خواهیم برای اعتبارسنجی فایل ها از آن استفاده کنیم کد کلاس FileValidationViewModel  به صورت زیر تغییر خواهد کرد.public class FileValidationViewModel
{
	public string Title { get; set; }

	[FileValidate]
        [DataType(DataType.Upload)]
        public IFormFile { get; set; }
{	برای توسعه بیشتر کلاس FileValidate  ابتدا باید متد Validate متعلق به انترفیس IModelValidator  پیاده سازی کنیم. در این متد محتوای فایل را از لحاظ داشتن مقدار ، حداکثر اندازه فایل و همچنین فرمت یا Mimetype  فایل را بررسی می کنیم و در صورت بروز هر گونه خطایی در اعتبارسنجی آن را گزارش می دهیم (ModelState).ابتدا چند Property  به کلاس اضافه می کنیم تا مشخص شود که چطور خطاها را بررسی کنیم. public class FileValidate :Attribute, IModelValidator
{
        public bool     IsRequied                           		          { get; set; } = false;
        public string   RequiredErrorMessage                 	  { get; set; }
        public int        MaximumFileSize                     		  { get; set; }
        public string   MaximumFileSizeErrorMessage        { get; set; }
        public string   ExtensionAcceptable                 	  { get; set; }
        public string   ExtensionAcceptableErrorMessage  { get; set; }
}برای هر حالتی از اعتبارسنجی پیغام مناسب آن را در ModelState  قرار می دهیم. حال نوبت خود متد Validate   می رسد که به شکل زیر پیاده سازی می شود.public IEnumerable&lt;ModelValidationResult&gt; Validate(ModelValidationContext context)
        {
            var file = context.Model as IFormFile;
            //IsRequired Validation
            if (file == null || file.Length ==0 )
            {
                if (IsRequied)
                {
                    return new List&lt;ModelValidationResult&gt;()
                    {
                        new ModelValidationResult
                            (
                                memberName  : context.ModelMetadata.PropertyName,
                                message     : RequiredErrorMessage
                            ),
                    };
                }
                else
                {
                    return Enumerable.Empty&lt;ModelValidationResult&gt;();
                }
            }
            // File size validation
            if (file.Length &gt; MaximumFileSize)
            {
                return new List&lt;ModelValidationResult&gt;()
                {
                    new ModelValidationResult
                    (
                        memberName  : context.ModelMetadata.PropertyName,
                        message     : MaximumFileSizeErrorMessage
                    ),
                };
            }
            // Mimetype validation
           using (var stream = file.OpenReadStream())
            {
                var mimetype = stream.GetMimeType();
               if (mimetype == null)
                {
                    return new List&lt;ModelValidationResult&gt;()
                    {
                        new ModelValidationResult
                        (
                            memberName  : context.ModelMetadata.PropertyName,
                            message     : ExtensionAcceptableErrorMessage
                        ),
                    };
                }
                if (!Extensions.Contains(mimetype.Extension))
                {
                    return new List&lt;ModelValidationResult&gt;()
                    {
                        new ModelValidationResult
                        (
                            memberName  : context.ModelMetadata.PropertyName,
                            message     : ExtensionAcceptableErrorMessage
                        ),
                    };
                }
            }
            return new List&lt;ModelValidationResult&gt;();
        }در کد بالا ابتدا خالی بود فایل بررسی می شود و پس از آن اندازه فایل مورد بررسی قرار می گیرد و در نهایت به کمک کتابخانه  MimetypeDetective  که در مقاله قبلی به آن اشاره کرده بودیم، درستی محتوای آن را بررسی می شود.نکته قابل اشاره در کد بالا می تواند این باشد که ما به محض برخورد با یک خطا لیستی از نوع ModelValidationResult را بر می گردانیم که در آن مشخص می کنیم که متن خطا چیست و خطا متعلق به چه Property  می باشد. return new List&lt;ModelValidationResult&gt;()                    {                        new ModelValidationResult                        (                            memberName   : context.ModelMetadata.PropertyName,                            message            : ExtensionAcceptableErrorMessage                        ),                    };در صورتی که خطایی در اعتبارسنجی ثبت شود مقدار ModelState.IsValid برابر False  خواهد شد.  در انتها نحوه استفاده از Attribute  به شکل زیر تغییر خواهد کرد.public class FileValidationViewModel
    {
        [FileValidate(
            IsRequied = false, 
            RequiredErrorMessage = &amp;quotPlease, select your logo&amp;quot,
            MaximumFileSize = 2 * 1024 * 1024, //in bytes
            MaximumFileSizeErrorMessage = &amp;quotYour file is too big&amp;quot,
            ExtensionAcceptable = &amp;quotjpg,bmp,png&amp;quot, 
            ExtensionAcceptableErrorMessage = &amp;quotYour logo not supported&amp;quot)]
        [DataType(DataType.Upload)]
        public IFormFile     Logo        { get; set; }
    }روش دیگر روش دیگری نیز برای نوشتن یک Attribute جهت بررسی اعتبارسنجی وجود دارد و آن هم استفاده از کلاس ValidationAttribute  می باشد. نحوه استفاده بدین صورت است که یک کلاس را تعریف می کنیم که از آن ارث بری کند و سپس متد IsValid آن را Override می کنیم بدین صورت کدهای ما کمی خواناتر خواهد شد. بقیه موارد مثل کدهایی است که در بالا اشاره کردیم. public class OtherFileValidate : ValidationAttribute
{
        public bool     IsRequied                       	                { get; set; } = false;
        public string   RequiredErrorMessage                      { get; set; }
        public int        MaximumFileSize                 	        { get; set; }
        public string   MaximumFileSizeErrorMessage      { get; set; }
        public string   ExtensionAcceptable             		{ get; set; }
        public string   ExtensionAcceptableErrorMessage { get; set; }
	protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
         }
}فقط دقت داشته باشید که متد IsValid  مقداری از نوع  ValidationResult را برمیگرداند. در صورتی که ما با خطای اعتبارسنجی مواجه شدیم کافی است که متن خطا را در قالب شیء از این نوع به شکل زیر برگردانیم :return new ValidationResult(ExtensionAcceptableErrorMessage);و در صورتی که هیچ خطایی موجود نباشد می توانیم از کد زیر هم استفاده کنیم:return ValidationResult.Success;آدرس GitHub برای مشاهده کد کامل این مقاله می توانید به آدرس گیت هاب پروژه مراجعه کنید و کد کامل آن را مشاهده نمایید.سخن آخر در این مقاله دو روش را برای اعتبارسنجی فایل به کمک نوشتن یک Attribute  سفارشی و با استفاده از کتابخانه MimeTypeDetective را بررسی کردیم. هر کدام از روش ها می تواند به صورت عملی در یک پروژه بکار گرفته شود. در ادامه این سری از مقالات روش دیگری را با استفاده از کتابخانه محبوب FluentValidation  برای  اعتبارسنجی یک فایل  شرح خواهیم داد.در زیر لینک سایر مقالات این سری را مشاهده می کنید:نحوه تشخیص Mimetype یک فایل بر اساس محتوای آن در Net.اعتبارسنجی فایل بر اساس محتوا در FluentValidationتوجه داشته باشید که این مقاله ممکن است به مرور زمان تغییر کند یا اصطلاحاتی در آن صورت گیرد.</description>
                <category>SoranRad.com</category>
                <author>SoranRad.com</author>
                <pubDate>Tue, 06 Sep 2022 12:30:07 +0430</pubDate>
            </item>
                    <item>
                <title>نحوه تشخیص Mimetype  یک فایل بر اساس محتوای آن در Net.</title>
                <link>https://virgool.io/20devs/%D9%86%D8%AD%D9%88%D9%87-%D8%AA%D8%B4%D8%AE%DB%8C%D8%B5-mimetype-%DB%8C%DA%A9-%D9%81%D8%A7%DB%8C%D9%84-%D8%A8%D8%B1-%D8%A7%D8%B3%D8%A7%D8%B3-%D9%85%D8%AD%D8%AA%D9%88%D8%A7%DB%8C-%D8%A2%D9%86-%D8%AF%D8%B1-net-iixigejwy4s4</link>
                <description>مقدمهدر این مقاله به بررسی Mimetype  می پردازیم و اینکه چگونه بر اساس اطلاعات داخل فایل بتوانیم Mimetype  آن را بدست آوریم. به صورت کلی در ظاهر با مشاهده پسوند یک فایل می توان mimetype  آنرا بدست آورد ولی در صورتی پسوند تغییر کند نباید ماهیت فایل عوض شود برای این منظور بهتر است که با توجه به محتوای فایل،  mimetype  آنرا بدست آوریم.تعریفات   در اصطلاح Mimetype متنی  است که از دوبخش تشکیل می شود و ماهیت فایل در بستر اینترنت را مشخص می کند. برای مثال “image/jpeg” , ”image/gif” و ” audio/mp4” چند نمونه از متداول ترین موارد هستند. هر فایلی، Mimetype  و پسوند خاص خود را دارد که بیانگر ماهیت فایل است و Mimetype  با پسوند فایل رابطه مستقیم دارد. با کمک پسوند فایل می توان Mimetype  آن را پیدا کرد و برعکس.هر فایلی به صورت منطقی از سه بخش کلی تشکیل شده است.Header, Data, Footer ساختار ساده یک فایل هدر فایل مانند یک متادیتا عمل می کند و اطلاعاتی درباره ماهیت فایل در آنجا ذخیره می شود. اغلب اطلاعات ابتدای هدر شامل مجموعه ای از اعداد است که نوع فایل با آن مشخص می شود به این اعداد ، اعداد جادویی گفته می شود(Magic Number). به کمک اعداد جادویی می توانیم ماهیت فایل را بدست بیاریم و mimetype  آن را مشخص کنیم و هم چنین  از طریق mimetype ، صحت پسوند فایل را تشخیص دهیم.ساختار فایل (Bitmap) Bmp. توجه داشته باشید که Magic Number  قسمتی از هدر فایل است که به کمک آن  می توان Mimetype را بدست آورد. اغلب این اعداد جادویی در ابتدای هدر قرار دارند ولی برای برخی از فایل ها این گونه نیست و با یک فاصله از ابتدا قرار می گیرند که به آن HeaderOffset می گویند.کاربردیکی از کاربردهای تشخیص Mimetype  برای اعتبارسنجی فایل هایی  است که به سمت سرور فرستاده می شود برای مثال کاربر تصویری را به عنوان پروفایل خود انتخاب می کند و برنامه سمت سرور باید ماهیت فایل (mimetype)  را به عنوان یک تصویر تشخیص دهد و از ارسال فایل هایی که ممکن است به سرور آسیب برسانند جلوگیری کند. به عنوان مثالی دیگر در صورتی که ما برنامه ای نوشته ایم که مجموعه ای مشخصی از فایل ها ، برای نمونه فایل های تصویری را ویرایش می کند. وقتی که فایلی را کاربر برای ویرایش باز میکند؛ در اولین قدم برنامه باید تشخیص دهد که آیا این فایل محتوای تصویری دارد و توسط برنامه پشتیبانی می شود یا نه ؟!یک نمونه فایلی که با برنامه HexEditor باز شده استدر صورتی که یک فایل با ماهیت مشخص را در یکی از برنامه های به اصطلاح HexEditor با کنید می توانید محتوای آن را در قالب اعداد در مبنای شانزده مشاهده کنید و اعداد جادویی ابتدای فایل را مشاهده کنید. روشی که هکرها زیاد از آن استفاده می کنند.کتابخانه TwentyDevs.MimetypeDetectiveاین کتابخانه Mimetype   یک فایل را بر اساس محتوای هدر آن تشخیص می دهد. برخی از انواع فایل ها هدر یکسانی دارند و در نتیجه کار تشخیص بعد از اعداد جادویی موجود در هدر باید به کمک پسوند آنها صورت گیرد.نحوه نصب کتابخانهبرای نصب کتابخانه کافی است دستور زیر را در کنسول وارد کنید.Install package TwentyDevs.MimetypeDetectiveنحوه استفاده از کتابخانه بدین صورت است که می توانید به تابع زیر با روشهای مختلف فایل را ارسال کنید و به عنوان خروجی شیء از نوع MimetypeInfo را دریافت کنید. در این کلاس شما می توانید کلیه اطلاعات مربوط به آن Mimetype را دریافت کنید. این اطلاعات شامل موارد زیر است.پسوند  عنوانعدد جادویی و مقداری اطلاعات جزئی دیگر.برای تشخیص mimetype  یک فایل می توانید آدرس فایل ، یک استریم از فایل یا آرایه ای از بایت ها را به تابع  MimetypeDetective  ارسال کنید.MimeTypeDetection.GetMimeType(filePath);به غیر از آدرس فایل همانطور که اشاره شد ، می توانید به کمک یک استریم ، آرایه ای از بایت ها  و چند  override  دیگر  که این تابع دارد ، Mimetype  را بدست آورد.به کمک این کتابخانه حتی می توانید با استفاده از تابع زیر با ارسال پسوند فایل mimetype  متناظر با آن را پیدا کنیم.MimeTypeDetection.GetMimeTypeByExtension(&quot;rar&quot;);//application/x-rar-compressedهمچنین با ارسال رشته Mimetype  به تابع زیر می توانید سایر اطلاعات مربوط به آن را مثل پسوند متناظر و اعداد جادویی و دیگر اطلاعات را پیدا کرد.MimeTypeDetection.GetMimeTypeinfoByMimetypeString(&amp;quotimage/gif&amp;quot);در صورتی که یک نوع Mimetype  جدید بخواهیم به لیست اضافه کنیم می توانیم از متد زیر استفاده کنیم.MimeTypes.Add();این متد یک mimetype  جدید را به لیست mimetype  های موجود خود اضافه می کند و شما می توانید مواردی جدید را برای مقایسه به آن اضافه کنید.آدرس Githubبرای مشاهده آدرس گیت هاب کتابخانه اینجا را کلیک کنید.سخن آخرتشخیص ماهیت فایل به کمک محتوای هدر آن و به اصطلاح اعداد جادویی موجود در هدر یکی از اقدامات امنیتی برای نوشتن وب سرویس های امن می باشد. در ادامه مطالب من سعی می کنم که به کمک این کتابخانه روشی را برای اعتبارسنجی فایل ارائه دهم که برای بسیار از سنارویو ها می تواند کاربردی باشد. در زیرلیست مقالاتی که در آنها از این کتابخانه استفاده شده است مشاهده می کنید. اعتبارسنجی فایل بر اساس محتوا در Net. اعتبارسنجی فایل بر اساس محتوا در FluentValidationاین مقاله ممکن است به مرور تکمیل تر بشود یا اصطلاحاتی در آن صورت بگیرد.</description>
                <category>SoranRad.com</category>
                <author>SoranRad.com</author>
                <pubDate>Tue, 23 Aug 2022 10:06:57 +0430</pubDate>
            </item>
                    <item>
                <title>روش جدید ارسال ایمیل از طریق NET.</title>
                <link>https://virgool.io/20devs/%D8%B1%D9%88%D8%B4-%D8%AC%D8%AF%DB%8C%D8%AF-%D8%A7%D8%B1%D8%B3%D8%A7%D9%84-%D8%A7%DB%8C%D9%85%DB%8C%D9%84-%D8%A7%D8%B2-%D8%B7%D8%B1%DB%8C%D9%82-net-hnnydmjr5mz6</link>
                <description>در این مقاله میخواهیم به رفع مشکل ارسال ایمیل از طریق Gmail SMTP در دات نت بپردازیم.گوگل طی بیانیه ای که در تاریخ 30 می 2022 منتشر کرد، به دلیل افزایش امنیت حساب کاربران، امکان ارسال ایمیل از طریق برنامه یا دستگاه های شخص ثالث(third-party) را پشتیبانی نمیکند. ممکن است هنگام ارسال ایمیل در برنامه با چنین ارور یا خطایی مواجه شده باشید.برای رفع این مشکل باید از روش جایگزینی به نام App Password استفاده کرد که در ادامه مقاله، به نحوه استفاده از این قابلیت و ارسال ایمیل خواهیم پرداخت.1- ابتدا وارد صفحه مدیریت حساب شوید.2- در قسمت سرچ (بالای صفحه) App Password را جستجو و انتخاب کنید3-در این قسمت، گزینه Select app و سپس Other را انتخاب کنید و یک نام دلخواه به آن بدهید و سپس دکمه Generate را بزنید.در آخر،یک کلمه عبور 16 کاراکتری برای شما تولید میکند، تنها کافیست کلمه عبور تولید شده را کپی، و جایگزین کلمه عبور حساب جیمیل خود در برنامه کنید.یک نمونه کد ساده جهت تست کردن ارسال ایمیل با استفاده کتابخانه FluentEmail.کداتون بی باگ :)</description>
                <category>SoranRad.com</category>
                <author>نیما مام آقایی</author>
                <pubDate>Mon, 22 Aug 2022 10:28:33 +0430</pubDate>
            </item>
            </channel>
</rss>