مطالب دوره‌ها
آشنایی با AOP IL Weaving
IL Weaving در AOP به معنای اتصال Aspects تعریف شده، پس از کامپایل برنامه به فایل‌های باینری نهایی است. اینکار با ویرایش اسمبلی‌ها در سطح IL یا کد میانی صورت می‌گیرد. بنابراین در این حالت دیگر یک محصور کننده و پروکسی، در این بین جهت مزین سازی اشیاء، در زمان اجرای برنامه تشکیل نمی‌شود. بلکه فراخوانی Aspects به معنای فراخوانی واقعی قطعه کدهایی است که به اسمبلی‌های برنامه پس از کامپایل آن‌ها تزریق شده‌اند.
در دنیای دات نت، ابزارهای چندی امکان انجام IL Weaving را فراهم ساخته‌اند که تعدادی از آن‌ها به قرار ذیل هستند:
- PostSharp
- LOOM.NET
- Wicca
و ...

در بین این‌ها، PostSharp معروفترین فریم ورک AOP بوده و در ادامه از آن استفاده خواهیم کرد.


پیشنیاز ادامه بحث

ابتدا یک پروژه کنسول جدید را آغاز کرده و سپس در خط فرمان پاور شل نوگت در VS.NET دستور ذیل را اجرا کنید:
 PM> Install-Package PostSharp
به این ترتیب ارجاعی به PostSharp به پروژه جاری اضافه خواهد شد. البته حجم آن نسبتا بالا است؛ نزدیک به 20 مگ به همراه ابزارهای تزریق کد همراه با آن. مجوز استفاده از آن نیز تجاری و مدت دار است.


مراحل ایجاد یک Aspect برای پروسه IL Code Weaving

ابتدا یک کلاس پایه مشتق شده از کلاسی ویژه موجود در یکی از فریم ورک‌های AOP باید تعریف شود. مرحله بعد، کار اتصال این Aspect می‌باشد که توسط پردازشگر ثانویه IL Code Weaving انجام می‌شود.
در ادامه قصد داریم همان مثال LoggingInterceptor قسمت دوم این سری را با استفاده از IL Code Weaving پیاده سازی کنیم.
using System;

namespace AOP03
{
    public class MyType
    {
        public void DoSomething(string data, int i)
        {
            Console.WriteLine("DoSomething({0}, {1});", data, i);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            new MyType().DoSomething("Test", 1);
        }
    }
}
کدهای برنامه همانند قبل است. اما اینبار بجای استفاده از Interceptors، با ارث بری از کلاس OnMethodBoundaryAspect کتابخانه PostSharp شروع خواهیم کرد:
using System;
using PostSharp.Aspects;

namespace AOP03
{
    [Serializable]
    public class LoggingAspect : OnMethodBoundaryAspect
    {
        public override void OnEntry(MethodExecutionArgs args)
        {
            Console.WriteLine("On Entry");
        }

        public override void OnExit(MethodExecutionArgs args)
        {
            Console.WriteLine("On Exit");
        }

        public override void OnSuccess(MethodExecutionArgs args)
        {
            Console.WriteLine("On Success");
        }

        public override void OnException(MethodExecutionArgs args)
        {
            Console.WriteLine("On Exception");
        }
    }
}
نیاز است این کلاس توسط ویژگی Serializable مزین شود تا توسط PostSharp قابل استفاده گردد. همانطور که ملاحظه می‌کنید، مراحل مختلف اجرای یک Aspcet در اینجا با override متدهای کلاس پایه OnMethodBoundaryAspect پیاده سازی شده‌اند. این مراحل را پیشتر در زمان استفاده از Interceptors توسط try/finally/catch بررسی کرده بودیم.
اکنون اگر برنامه را اجرا کنیم، اتفاق خاصی رخ نداده و همان خروجی معمول متد DoSomething در کنسول نمایش داده خواهد شد. بنابراین در مرحله بعد نیاز است تا این Aspect را به کدهای برنامه متصل کنیم.
کلاس OnMethodBoundaryAspect در کتابخانه PostSharp، از کلاس MulticastAttribute مشتق می‌شود. بنابراین LoggingAspect ایی را که ایجاد کرده‌ایم نیز می‌توان به صورت یک ویژگی به متد‌های مورد نظر خود افزود:
    public class MyType
    {
        [LoggingAspect]
        public void DoSomething(string data, int i)
        {
            Console.WriteLine("DoSomething({0}, {1});", data, i);
        }
    }
اکنون اگر برنامه را اجرا کنیم، با خروجی زیر مواجه خواهیم شد:
 On Entry
DoSomething(Test, 1);
On Success
On Exit
برای اینکه بتوان عملیات رخ داده را بهتر توضیح داد می‌تواند از یک دی‌کامپایلر مانند برنامه معروف Reflector استفاده کرد:
public void DoSomething(string data, int i)
{
    <>z__Aspects.a0.OnEntry(null);
    try
    {
        Console.WriteLine("DoSomething({0}, {1});", data, i);
        <>z__Aspects.a0.OnSuccess(null);
    }
    catch (Exception)
    {
        <>z__Aspects.a0.OnException(null);
        throw;
    }
    finally
    {
        <>z__Aspects.a0.OnExit(null);
    }
}
این کدی است که به صورت پویا توسط PostSharp به اسمبلی نهایی فایل اجرایی برنامه تزریق شده است.

خوب! این یک روش اتصال Aspects به برنامه است. اما اگر همانند Interceptors بخواهیم Aspect تعریف شده را سراسری اعمال کنیم چکار باید کرد (بدون نیاز به قرار دادن ویژگی بر روی تک تک متدها)؟
برای اینکار ابتدا نیاز است میدان عملکرد Aspect تعریف شده را توسط ویژگی MulticastAttributeUsage محدود کنیم تا برای مثال به خواص اعمال نشوند:
 [Serializable]
[MulticastAttributeUsage(MulticastTargets.Method, TargetMemberAttributes = MulticastAttributes.Instance)]
public class LoggingAspect : OnMethodBoundaryAspect
سپس فایل AssemblyInfo.cs استاندارد پروژه را گشوده و سطر زیر را به آن اضافه کنید:
 [assembly: LoggingAspect(AttributeTargetTypes = "AOP03.*")]
توسط AttributeTargetTypes می‌توان اعمال این Aspect را به یک فضای نام خاص نیز محدود کرد.

مزیت روش IL Code Weaving نسبت به Interceptors، کارآیی و سرعت بالاتر است. از این جهت که کدهایی که قرار است اجرا شوند، پیشتر در اسمبلی برنامه قرار گرفته‌اند و نیازی نیست تا در زمان اجرا، کدی به برنامه به صورت پویا تزریق گردد.
مطالب
پشتیبانی از Generic Attributes در C# 11
هر کلاسی در #C که از کلاس پایه‌ی System.Attribute مشتق شود، یک Attribute نامیده می‌شود و مهم‌ترین و هدف و کاربرد آن‌ها، مزین کردن و علامتگذاری سایر نوع‌ها و فیلدها هستند تا بر اساس آن‌ها بتوان کارکردهای بیشتری را در اختیار آن نوع‌ها قرار داد. برای مثال، استفاده از  ویژگی‌‌های JsonProperty و یا JsonPropertyName در حین اعمال serializations و یا در کاربردهای اعتبارسنجی مانند ویژگی‌های Required، Range و امثال آن‌ها:
public class Student
{
    [JsonPropertyName("id")]
    public int Id { get; set; }

    [JsonPropertyName("name")]
    public string Name { get; set; }
}

public class WeatherForecast
{
    [Required]
    public int TemperatureC { get; set; }

    [MinLength(50)]
    public string Summary { get; set; }
}

روش متداول ارسال نوع‌ها به attributes تا پیش از C# 11

تا پیش از C# 11، روش پیاده سازی یک attribute جنریک که بتواند با انواع و اقسام نوع‌ها کار کند، به صورت زیر است:
- ارسال یک پارامتر از نوع System.Type به سازنده‌ی attribute
- تعریف خاصیتی مانند ParamType در صورت نیاز؛ تا مشخص کند که چه نوعی به سازنده‌ی attribute ارسال شده‌است. مانند مثال فرضی زیر:
[AttributeUsage(AttributeTargets.Class)]
public class CustomDoNothingAttribute: Attribute
{
    // Note the type parameter in the constructor
    public CustomDoNothingAttribute(Type t)
    {
        ParamType = t;
    }

    public Type ParamType { get; }
}
و سپس با استفاده از عملگر typeof، نوع مدنظر را به سازنده‌ی ویژگی تعریف شده، ارسال می‌کنیم:
[CustomDoNothing(typeof(string))]
public class Student
{
    public int Id { get; set; }

    public string Name { get; set; }
}
یک نمونه مثال دنیای واقعی آن، [ServiceFilter(typeof(ResponseLoggerFilter))] در ASP.NET Core است که دیگر با وجود جنریک‌ها، آنچنان هماهنگ و یک‌دست با سایر اجزای زبان به نظر نمی‌رسد. نمونه‌ی هماهنگ با پیشرفت‌های زبان، باید چنین شکلی را داشته باشد: [<ServiceFilter<ResponseLoggerFilter]


امکان تعریف ویژگی‌های جنریک در C# 11

‍C# 11 به همراه پیشتیبانی از generic attributes ارائه شده‌است. بنابراین اینبار بجای ارسال پارمتری از نوع Type به سازنده‌ی ویژگی‌، می‌توان کلاس آن attribute را به صورت جنریک تعریف کنیم که می‌تواند یک یا چندین نوع را به عنوان پارامتر بپذیرد. بنابراین مثال قبل در C# 11 به صورت زیر بازنویسی می‌شود:
[AttributeUsage(AttributeTargets.Class)]
public class CustomDoNothingAttribute<T> : Attribute
    where T : class
{
    public T ParamType { get; }
}

[CustomDoNothing<string>]
public class Student
{
    public int Id { get; set; }

    public string Name { get; set; }
}
یک مزیت مهم این روش نسبت به قبل، امکان تعریف قیود و type safety است. برای نمونه در مثال فوق، نوع T به کلاس‌ها محدود شده‌است و نوع‌های دیگر را نمی‌پذیرد. به این ترتیب می‌توان این نوع بررسی‌ها را بجای زمان اجرا و صدور استثناءها، دقیقا در زمان کامپایل انجام داد.
و اگر نیاز به تعیین چند نوع بود، باید خاصیت AllowMultiple نحوه‌ی استفاده از ویژگی را به true تنظیم کرد:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class DecorateAttribute<T> : Attribute where T : class
{
    // ....
}
تا بتوان به تعریف زیر رسید:
[Decorate<LoggerDecorator>]
[Decorate<TimerDecorator>]
public class SimpleWorker
{
    // ....
}


محدودیت‌های انتخاب نوع‌ها در ویژگی‌های جنریک C# 11

در ویژگی‌های جنریک نمی‌توان از نوع‌های زیر استفاده کرد (همان محدودیت‌های typeof، در اینجا هم برقرار هستند):
- نوع‌های dynamic
- nullable reference types مانند ?string
- نوع‌های tuple تعریف شده‌ی به کمک C# tuple syntax مانند (int x, int y)

چون این نوع‌ها به همراه یکسری metadata annotations هستند که صرفا بیانگر توضیحی اضافی در مورد نوع بکارگرفته شده هستند و در صورت نیاز، بجای آن‌ها می‌توانید از نوع‌های زیر استفاده کنید:
- از object بجای dynamic
- از string بجای ?string
- از <ValueTuple<int, int بجای (int X, int Y)

همچنین در زمان استفاده‌ی از یک ویژگی جنریک، باید نوع مورد استفاده، کاملا مشخص و در اصطلاح fully constructed باشد:
public class GenericAttribute<T> : Attribute { }

public class GenericType<T>
{
    [GenericAttribute<T>] // Not allowed! generic attributes must be fully constructed types.
    public string Method1() => default;

    [GenericAttribute<string>]
    public string Method2() => default;
}
مطالب
تفاوت سیستم‌های یکپارچه با میکروسرویس‌ها (Monolithic vs Microservices architecture)

 معماری میکرو سرویس یا یکپارچه؟

برای درک میکروسرویس‌ها، باید بدانیم کاربرد سیستم‌های یکپارچه چیست و چه چیزی باعث شد در زمان‌های اخیر از برنامه‌های یکپارچه به میکروسرویس‌ها حرکت کنیم.


 سیستم‌های یکپارچه ( Monolithic applications  )

اگر تمام عملکردهای یک پروژه در یک بخش واحد وجود داشته باشند، آن برنامه به عنوان یک برنامه‌ی یکپارچه شناخته می‌شود. ما برنامه‌ی خود را در لایه‌های مختلفی مانند Presentation ، Service ، UI  طراحی می‌کنیم و سپس آن بخش از کدهای نوشته شده را به عنوان یک فایل خروجی به کار می‌گیریم. این چیزی نیست جز یک برنامه‌ی یکپارچه، که در آن " mono " یک پایگاه کد منفرد حاوی تمام عملکردهای مورد نیاز را نشان می‌دهد.


چرا اصلا به سمت میکروسرویس‌ها برویم؟

خب برای جواب به این سوال بهتر است معایب سیستم‌های یکپارچه را مرور کنیم:

  1. مدیریت دشوار بخاطر گسترش برنامه در گذشت زمان
  2. برای تغییری کوچک، کل برنامه را دوباره باید منتشر ( publish ) کنیم
  3. با تغییر و آپدیت برنامه، زمان انتشار افزایش می‌یابد.
  4. درک دشوار برای توسعه دهنده‌های جدید هر پروژه
  5. برای تقسیم ترافیک روی قسمت‌های مختلف برنامه، باید نمونه‌های کل برنامه را در چندین سرور منتشر کنیم که بسیار ناکارآمد و باعث استفاده‌ی بیهوده از منابع میشود
  6. اگر از فناوری یا تکنولوژی‌های جدید استفاده کنیم، برای عملکردی خاص، چه از نظر هزینه و چه از نظر زمان، بر کل برنامه تاثیر گذار است
  7. و در نهایت وجود یک باگ در هر ماژول میتواند کل برنامه را مختل کند.

و اما مزایای سیستم‌های یکپارچه:

  1. توسعه‌ی آن نسبت به میکروسرویس‌ها ساده است.
  2. انتشار آن آسان‌تر است؛ زیرا فقط یک خروجی، مستقر شده‌است.
  3. در مقایسه با معماری میکروسرویس‌ها، توسعه‌ی آن نسبتا آسان‌تر و ساده‌تر است.
  4. مشکلات تأخیر و امنیت شبکه در مقایسه با معماری میکروسرویس‌ها نسبتاً کمتر است.
  5. توسعه دهندگان نیازی به یادگیری برنامه‌های مختلف ندارند؛ آنها می‌توانند تمرکز خود را بر روی یک برنامه حفظ کنند.


میکروسرویس ها

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

معماری میکروسرویس تأثیر بسزایی در رابطه‌ی بین برنامه و پایگاه داده دارد. بجای اشتراک گذاری یک پایگاه داده با سایر میکروسرویس‌ها، هر میکروسرویس، پایگاه داده خاص خود را دارد که اغلب منجر به تکثیر برخی از داده‌ها می‌شود، اما اگر می‌خواهید از این معماری بهره مند شوید، داشتن یک پایگاه داده در هر میکروسرویس، ضروری است؛ زیرا اتصال ضعیف را تضمین می‌کند. مزیت دیگر داشتن یک پایگاه داده‌ی مجزا برای هر میکروسرویس این است که هر میکروسرویس می‌تواند از نوع پایگاه داده‌ای که برای نیازهای خود مناسب‌تر است، استفاده کند. هر سرویس یک ماژول را ارائه می‌دهد، به طوری که خدمات مختلف را می‌توان به زبان‌های برنامه نویسی مختلف نوشت. الگوهای زیادی در معماری میکروسرویس دخیل هستند مانند discovery و registry service ، Caching ، ارتباط API ، امنیت و غیره.



اصول میکروسرویس‌ها:

تک مسئولیتی: یکی از اصولی است که به عنوان بخشی از الگوی طراحی SOLID تعریف شده است. بیان می‌کند که یک Unit ، یا یک کلاس، یک متد یا یک میکروسرویس باید تنها یک مسئولیت را داشته باشد. هر میکروسرویس باید یک مسئولیت داشته باشد و یک عملکرد واحد را ارائه دهد. شما همچنین می‌توانید بگویید تعداد میکروسرویس‌هایی که باید توسعه دهید، برابر با تعداد عملکردهای مورد نیاز شما است. پایگاه داده نیز غیرمتمرکز است و به طور کلی، هر میکروسرویس، پایگاه داده خاص خود را دارد.

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

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

مزایای میکروسرویس ها:

  1. مدیریت آن آسان است زیرا نسبتا کوچکتر است.
  2. اگر در یکی از میکروسرویس‌ها، بروزرسانی وجود داشته باشد، باید فقط آن میکروسرویس را مجدداً منتشر کنیم.
  3. میکروسرویس‌ها مستقل هستند و از این رو به طور مستقل منتشر می‌شوند. زمان راه اندازی و انتشار آنها نسبتاً کمتر است.
  4. برای یک توسعه‌دهنده جدید بسیار آسان است که وارد پروژه شود، زیرا او باید فقط یک میکروسرویس خاص را که عملکردی را که روی آن کار می‌کند، درک کند و نه کل سیستم را.
  5. اگر یک میکروسرویس خاص به دلیل استفاده بیش از حد کاربران از آن عملکرد، با بار زیادی مواجه است، ما باید فقط آن میکروسرویس را تنظیم کنیم. از این رو، معماری میکروسرویس از مقیاس بندی افقی پشتیبانی می‌کند.
  6. هر میکروسرویس بر اساس نیازهای تجاری می‌تواند از فناوری‌های مختلفی استفاده کند.
  7. اگر یک میکروسرویس خاص به دلیل برخی باگ‌ها از کار بیفتد، بر روی سایر میکروسرویس‌ها تأثیر نمی‌گذارد و کل سیستم دست نخورده باقی می‌ماند و به ارائه سایر عملکردها به کاربران ادامه می‌دهد.

معایب میکروسرویس ها:

  1. پیچیده است و پیچیدگی آن با افزایش تعداد ریز سرویس‌ها افزایش می‌یابد.
  2. نیاز به نیروهای متخصص
  3. استقرار مستقل میکروسرویس‌ها پیچیده‌است.
  4. میکروسرویس‌ها از نظر استفاده از شبکه پرهزینه هستند؛ زیرا نیاز به تعامل با یکدیگر دارند و همه این تماس‌های راه دور منجر به تأخیر شبکه می‌شود.
  5. امنیت کمتر به دلیل ارتباط بین سرویس‌ها
  6. اشکال زدایی دشوار است

مطالب
Functional Programming یا برنامه نویسی تابعی - قسمت سوم – Immutability

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

  • Immutability: عدم توانایی تغییر داده
  • State: داده‌هایی که در طول زمان تغییر می‌کنند
  • Side Effect: تغییری که روی داده‌ها اتفاق می‌افتد

در قطعه کد زیر سعی شده‌است تفاوت یک کلاس Stateless و stateful را به سادگی نشان دهیم:

    //Stateful
    public class UserProfile
    {
        private User _user;
        private string _address;

        public void UpdateUser(int userId, string name)
        {
            _user = new User(userId, name);
        }
    }

    //Stateless
    public class User
    {
        public User(int id, string name)
        {
            Id = id;
            Name = name;
        }

        public int Id { get; }
        public string Name { get; }
    }


چرا Immutable بودن مهم است؟ 

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

در واقع انتظار داریم که به ازای یک ورودی بر اساس بدنه‌ی متد، یک خروجی داشته باشیم؛ ولی در واقعیت تاثیری که اجرای متد بر روی state کل کلاس خواهد گذاشت، از دید ما پنهان است و باعث به وجود آمدن مشکلات بعدی خواهد شد. برای مثال قطعه کد بالا را به صورت Honest بازنویسی میکنیم: 

    public class UserProfile
    {
        private readonly User _user;
        private readonly string _address;

        public UserProfile(User user,string address)
        {
            _user = user;
            _address = address;
        }
        public UserProfile UpdateUser(int userId, string name)
        {
            var newUser = new User(userId, name);
            return  new UserProfile(newUser,_address);
        }
    }

    public class User
    {
        public User(int id, string name)
        {
            Id = id;
            Name = name;
        }

        public int Id { get; }
        public string Name { get; }
    }

در این مثال متد UpdateUser به جای  void، یک شی از جنس کلاس UserProfile را بر می‌گرداند. کلاس UserProfile هم برای وهله سازی نیاز به یک شیء از جنس User و Address را دارد. بنابراین مطمئن هستیم که مقدار دهی شده‌اند. نکته دیگر در قطعه کد بالا این است که به ازای هر بار فراخوانی متد، یک شیء جدید بدون وابستگی به وهله سازی اشیاء دیگر، برگردانده میشود.


Immutable بودن باعث می‌شود: 

  • خوانایی کد افزایش پیدا کند
  • جای واحدی برای Validate کردن داشته باشیم
  • به صورت ذاتی Thread Safe باشیم


در مورد محدودیت‌هایی که در کار با اشیاء Immutable باید در نظر داشته باشیم، می‌توان به مصرف بالای رم و سی پی یو، اشاره کرد. در واقع به نسبت حالت mutate، تعداد اشیاء بیشتری ساخته خواهند شد. در فریمورک دات نت برای کار با اشیا immutable امکاناتی در نظر گرفته شده که این هزینه را کاهش می‌دهند. به طور مثال می‌توانیم از کلاس ImmutableList استفاده کنیم و از ایجاد اشیاء اضافه‌تر و تحمیل بار اضافی به GC جلوگیری کنیم. یک مثال: 

//Create Immutable List
ImmutableList<string> list = ImmutableList.Create<string>();
ImmutableList<string> list2 = list.Add("Salam");

//Builder
ImmutableList<string>.Builder builder = ImmutableList.CreateBuilder<string>();
builder.Add("avali");
builder.Add("dovomi");
builder.Add("sevomi");

ImmutableList<string> immutableList = builder.ToImmutable();


چطور با side effect کنار بیایم؟ 

یکی از الگوهای رایج برای این کار، مفهوم جدا سازی Command/Query است. به طور ساده تمامی عملیاتی را که تاثیر گذار هستند، به صورت Command در نظر میگیریم. Command ‌ها معمولا هیچ نوعی را بازگشت نمیدهند و همینطور بر عکس این قضیه برای Query ‌ها صادق است. اشتباه رایج درباره این الگو، محدود کردن این الگو به معماری‌های خاصی مانند Domain Driven می‌باشد؛ در صورتیکه الزامی برای رعایت این الگو در سایر معماری‌ها وجود ندارد. 

به مثال زیر دقت کنید. سعی کردم قسمت‌های Command و Query را از هم جدا کنم: 

در واقع هر برنامه می‌تواند شامل دو قسمت باشد:

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

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

برای مسائلی که در بالا صحبت شد، نمونه‌‌ای را آماده کرده‌ام. این نمونه به طور ساده یک سیستم مدیریت نوبت است که نوبت‌ها را در فایلی ذخیره و بازیابی میکند ( mutate ) و منطق مربوط به نوبت‌ها و زمان ویزیت آن میتواند به صورت immutable پیاده سازی شود. این کد در دو حالت functional و غیر functional پیاده سازی شده تا به خوبی تفاوت آن را در حالت قبل و بعد از برنامه نویسی تابعی بتوانیم درک کنیم. به جهت خوانایی بیشتر و دسترسی به کد‌ها، آن‌ها را روی گیت‌هاب قرار داده و شما میتوانید از اینجا سورس کد مورد نظر را بررسی کنید. سعی شده در این مثال تمامی مواردی که در این قسمت ذکر شد را پیاده سازی کنیم. امیدوارم که مطالب مربوط به برنامه نویسی تابعی یا functional programming توانسته باشد دیدگاه جدیدی را به کدهایی که مینویسیم بدهد. در  قسمت‌های بعدی به مواردی مانند مدیریت exception ‌ها و کار با null ‌ها و ... خواهیم پرداخت.

مطالب
#Defensive Code in C - قسمت دوم

تعریف متد‌ها در برنامه نویسی:

متدها جزء اولین چیزهایی هستند که در هنگام شروع برنامه نویسی در هر یک از زبان‌های برنامه نویسی، برنامه نویس با آنها آشنا می‌شود. بنابراین متد‌ها به عنوان اصلی‌ترین Building Block  ها در زبان‌های برنامه نویسی دارای اهمیت بسیار زیادی می‌باشند. متد‌ها اولین جاهایی هستند که ما می‌توانیم کار خودمان را از آنها شروع کنیم و به سوی هدف خود حرکت کنیم.

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

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

متد‌ها علاوه بر ویژگی‌های ذکر شده‌ی در بالا که بیشتر بر ویژگی‌های ذاتی و عملکردی آن تمرکز داشت، باید داری یکسری ویژگی‌های دیگر نیز باشند، متد‌ها باید Clean ، Testable و Predictable باشند. هر کدام از ویژگی‌های مذکور توسط این پارامتر‌ها تشریح می‌شوند. 

در ذیل ویژگی‌های مذکور در شکل بالا را تشریح خواهیم کرد.

Clear Purpose :

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

· متد به راحتی قابل درک می‌باشد.

· میزان خطا‌ها را به شدت کاهش می‌دهد.

· دیباگ کردن را در صورت وجود هر خطایی ساده‌تر می‌کند.

· قابلیت توسعه را افزایش می‌دهد.

· نوشتن تست برای این نوع متد‌ها به دلیل اینکه فقط بر روی یک هدف خاص تمرکز دارند ساده است.

Good Name :

· نام متد عمکرد آن را به روشنی بیان می‌کند

Focused Code :

· تمام کد نوشته شده‌ی در متد فقط بر روی یک هدف تمرکز دارند.

· خوانایی کد بالا است و میزان توضیحاتی که برای کد نوشته می‌شود در کمترین حد ممکن است.

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

Short Length :

· تعداد خطوط کد مربوط به متد کم می‌باشد. این مسئله خود باعث کاهش باگ‌های احتمالی در یک متد می‌شود.

Automated Code Test :

· متد این قابلیت را دارد که توسط زیر ساخت‌های تست، تست شود که این مسئله خود باعث افزایش کیفیت کد می‌شود.

Predictable Result :

· متد دارای یک نتیجه‌ی قابل پیش بینی می‌باشد.


در ادامه سعی می‌کنیم با ذکر یک مثال، مواردی را که ذکر شد بیشتر توضیح دهیم و دیدگاه کاربردی آن را بررسی کنیم.

مثالی از دنیای واقعی:

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

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

Clear Purpose :

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

Clear Name :

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

Focused Code :

 متد باید کاری را انجام دهد که نام آن بیان می‌کند و تمام کد‌های متد باید حتما بر روی آن هدف تمرکز کنند.

Short Length :

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

Automated Code Test :

 آیا این متد به وسیله‌ی  Automated Code Test  می تواند تست شود؟ چند نکته در مورد این کد وجود دارد که توانایی Automated Code Test را از این کد می‌گیرد. اولین مسئله این است که در این مثال منطق یا همان Business برنامه با UI تلفیق شده‌است. برای رفع این مشکل باید منطق برنامه را در یک پروژه‌ی مجزا از نوع Class Library قرار داد. مسئله‌ی دیگر این است که این متد برای تست شدن بسیار طولانی می‌باشد و باید به یکسری اجزای کوچکتر و منطقی‌تر شکسته شود و هر متد باید یک هدف و عملکرد روشن را داشته باشد. 

در قسمت بعدی راهکارهایی برای Refactor کردن کد بر اساس اصول ذکر شده ارائه خواهد شد.

مطالب
ساخت ربات تلگرامی با #C
 با رشد دنیای تکنولوژی، وسائل هوشمند همراه نیز به سرعت پیشرفته‌تر شدند. در این میان با گسترش زیرساخت اینترنت، رشد شبکه‌های اجتماعی نیز چشمگیر بوده است. یکی از بهترین این‌ها، شبکه‌های تلگرام می‌باشد که با بهره گیری از سرورهای ابری، امنیت و سرعت را برای کاربران به ارمغان آورده است.
چندی پیش موسسان تلگرام با معرفی API‌های کاربردی، به توسعه کنندگان اجازه دادند که با بهره گیری از بستر این شبکه، اقدام به تولید اینترفیسی به اسم بات کنند که با دریافت دستورات سفارشی، عملیات خاصی را انجام دهد.
در واقع تلگرام و متدهای ارائه شده، یک راه ارتباطی بین کاربران و برنامه‌های تولید شده را ایجاد کردند که با قدری ذوق و سلیقه، شاهد بات‌های جالب و کاربردی هستیم.
در این مقاله سعی شده طرز تهیه یک بات با زبان #C توضیح داده شود.
در ابتدا شما باید توسط یکی از بات‌های اصلی تلگرام اقدام به ثبت نام کاربری و تنظیمات بات مورد نظر خودتان نمایید. بات مورد نظر @BotFather می‌باشد که با شروع مکالمه می‌توان با فرستادن دستورات مختلف تنظیمات مختلفی را انجام داد. با شروع مکالمه با بات مورد نظر با دستور /start دستورات زیر قابل انجام می‌باشد:

 You can control me by sending these commands :

/ newbot - create a new bot

/ token - generate authorization token

/ revoke - revoke bot access token

/ setname - change a bot's name

/ setdescription - change bot description

/ setabouttext - change bot about info

/ setuserpic - change bot profile photo

/ setcommands - change bot commands list

/ setjoingroups - can your bot be added to groups ?

/ setprivacy - what messages does your bot see in groups ?

/ deletebot - delete a bot

/ cancel - cancel the current operation
با انجام دستور /newbot در ابتدا نام بات و یوزنیم (دقت کنید یوزرنیم می‌بایست حتما به کلمه‌ی bot ختم شود) را تنظیم کنید.
بعد از تایید نام و یوزر نیم، به شما یک توکن اختصاص داده می‌شود که توسط آن شما شناسایی می‌شوید.
در اینجا شما می‌توانید تنظیمات اضافه‌تری مانند عکس برای پروفایل و غیره را نیز تنظیم کنید.
در مرحله‌ی بعد می‌توانید در همین قسمت دستورات مورد نظر را جهت بات خود تنظیم کنید. برای این کار باید دستور /setcommands را وارد کنید و دستور مورد نظر خود را به فرمت command1 – Description وارد کنید.
مرحله‌ی بعد، تنظیمات برنامه‌ی شما جهت دریافت دستورات وارد شده و انجام عملیات مورد نظر و تولید و ارسال خروجی مورد نظر است.

دریافت دستورات به دو طریق انجام می‌شود:
1. توسط دستور getUpdates می‌توان تمامی کامندهای دریافتی را از سرور تلگرام دریافت کرد و با انجام پروسس‌های لازم، خروجی را به کاربر مورد نظر ارسال کرد.
2. توسط تابع webhook از تلگرام درخواست کرد در صورت دریافت دستور جدید به بات، این دستور را به یک آدرس خاص ارسال کرد.

قابل توجه است که می‌توان فقط از یکی از دو روش فوق استفاده کرد. همچنین در روش دوم حتما سرور مورد نظر باید گواهی ssl تایید شده داشته باشد.
کد زیر دریافت کامندهای یک بات به روش اول می‌باشد :
public class mydata
    {
        public result[] result;
    }
    public class result
    {
        public int update_id { get; set; }
        public message message { get; set; }
    }
    public class message
    {
        public int message_id { get; set; }
        public message_from from { get; set; }
        public message_chat chat { get; set; }
        public int date { get; set; }
        public string text { get; set; }
    }
    public class message_from
    {
        public int ind { get; set; }
        public string first_name { get; set; }
        public string username { get; set; }
    }
    public class message_chat
    {
        public int id { get; set; }
        public string first_name { get; set; }
        public string username { get; set; }
    }
 
public  Void GetUpdates()
        {
 
            WebRequest req = WebRequest.Create("https://api.telegram.org/bot" + yourToken + "/getUpdates");
            req.UseDefaultCredentials = true;
            WebResponse resp = req.GetResponse();
            Stream stream = resp.GetResponseStream();
            StreamReader sr = new StreamReader(stream);
            string s = sr.ReadToEnd();
            sr.Close();
            var jobject = Newtonsoft.Json.Linq.JObject.Parse(s);
            mydata gg = JsonConvert.DeserializeObject<mydata>(jobject.ToString());
            List<result> results = new List<result>();
            foreach (result rs in gg.result)
            {
                results.Add(rs); 
                SendMessage(rs.message.chat.id.ToString(), "hello"+" "+"Dear"+rs.message.chat.first_name); 
            }             
        }
و توسط تابع زیر می‌توان به کاربری که به بات کامند ارسال کرد، پاسخ داد:
public static void SendMessage(string chat_id, string message)
        {
            WebRequest req = WebRequest.Create("https://api.telegram.org/bot" + youToken + "/sendMessage?chat_id=" + chat_id + "&text=" + message);
            req.UseDefaultCredentials = true;
 
            var result = req.GetResponse();
            req.Abort();
        }

لازم به ذکر است خروجی توابع بات‌های تلگرام با فرمت JSON می‌باشد که با نصب پکیج NewTonsoft می‌توان آن را به لیست تبدیل کرد.
rs.message.chat.id، آی دی فردی است که به بات تلگرامی ما مسیج ارسال کرده است.
rs.message.chat.first_name نام فردی است که به بات تلگرام مسیج ارسال کرده است.
همچنین می‌توان در جواب کامند بات، علاوه بر متن، صدا و تصویر را نیز ارسال نمود .

در این لینک و این لینک می‌توان توضیحات بیشتری را در این زمینه مطالعه کرد.
در انتها خوشحال می‌شوم ذوق‌ها و ایده‌های شما را در ساخت بات‌ها با آیدی @iekhtiari مشاهده کنم.
مطالب
اصول طراحی شی گرا SOLID - #بخش چهارم اصل ISP

اصل 4) I – ISP– Interface Segregation principle
مقایسه با دنیای واقعی:

بیایید فکر کنیم شما یک کامپیوتر دسکتاپ جدید خریداری کرده اید.شما یک زوج پورت USP، چند پورت سریال، یک پورت VGA و ...  را پیدا میکنید. اگر شما بدنه‌ی کیس خود را باز کنید، تعدادی اسلات بر روی مادربرد خود که برای اتصال قطعات مختلف با یکدیگر هستند را مشاهده خواهید کرد که عمدتا مهندسان سخت افزار در زمان منتاژ از آنها استفاده میکنند.
این اسلات‌ها تا زمانی که بدنه کیس را باز نکنید قابل مشاهده نخواهند بود. به طور خلاصه تنها رابطه‌های مورد نیازی که برای شما ساخته شده اند، قابل مشاهده خواهند بود.
و فرض کنید شما به یک توپ فوتبال نیاز دارید. به یک فروشگاه وسایل ورزشی میروید و فروشنده شروع به نشان دادن انواع توپ ها، کفش ها،لباس  و گرم کن‌های ورزشی،لباس شنا و کاراته ،زانوبند و ... کرده است. در نهایت ممکن است شما چیزی را خریداری کنید که اصلا مورد نیازتان نبوده است یا حتی ممکن است فراموش کنیم که ما چرا اینجا هستیم.!
ارائه مشکل:
ما میخواهیم یک سیستم مدیریت گزارشات را توسعه بدهیم. اولین کاری که انجام می‌دهیم ایجاد یک لایه منطقی که توسط سه UI مختلف دیگر مورد استفاده قرار میگیرد.
1- EmployeeUI : نمایش گزارش‌های مربوط با کارمند وارد شده‌ی جاری در سایت.
2- ManagerUI  : نمایش گزارش‌های مربوط به خود و تیمی که به او تعلق دارد.
3- AdminUI : نمایش گزارش‌های مربوط به کارمندان، مربوط به تیم و مربوط به شرکت مانند گزارش سود و زیان و ...
public interface IReportBAL
{
    void GeneratePFReport();
    void GenerateESICReport();

    void GenerateResourcePerformanceReport();
    void GenerateProjectSchedule();

    void GenerateProfitReport();
}
public class ReportBAL : IReportBAL
{    
    public void GeneratePFReport()
    {/*...............*/}

    public void GenerateESICReport()
    {/*...............*/}

    public void GenerateResourcePerformanceReport()
    {/*...............*/}

    public void GenerateProjectSchedule()
    {/*...............*/}

    public void GenerateProfitReport()
    {/*...............*/}
}
public class EmployeeUI
{
    public void DisplayUI()
    {
        IReportBAL objBal = new ReportBAL();
        objBal.GenerateESICReport();
        objBal.GeneratePFReport();
    }
}
public class ManagerUI
{
    public void DisplayUI()
    {
        IReportBAL objBal = new ReportBAL();
        objBal.GenerateESICReport();
        objBal.GeneratePFReport();
        objBal.GenerateResourcePerformanceReport ();
        objBal.GenerateProjectSchedule ();
    }
}
public class AdminUI
{
    public void DisplayUI()
    {
        IReportBAL objBal = new ReportBAL();
        objBal.GenerateESICReport();
        objBal.GeneratePFReport();
        objBal.GenerateResourcePerformanceReport();
        objBal.GenerateProjectSchedule();
        objBal.GenerateProfitReport();
    }
}
حال زمانی که توسعه دهنده در هر UI عبارت objBal را تایپ کند، در لیست بازشوی آن (intellisense )  به صورت زیر نمایش داده خواهد شد : 

اما مشکل چیست؟ مشکل این است شخصی که روی لایه‌ی EmployeeUI کار میکند به تمام متدها‌ها به خوبی دسترسی دارد و این متدهای غیرضروری ممکن است باعث سردرگمی او شود.
این مشکل به این دلیل اتفاق می‌افتد که وقتی کلاسی یک واسط را پیاده سازی می‌کند، باید همه متدهای آن را نیز پیاده سازی کند. حالا اگر خیلی از این متدها توسط این کلاس استفاده نشود، می‌گوییم که این کلاس از مشکل واسط چاق رنج می‌برد.
اصل ISP  میگوید : " کلاینت‌ها نباید وابسته به متدهایی باشند که آنها را پیاده سازی نمی‌کنند. "
برای رسیدن به این امر در مثال بالا باید آن واسط را به واسط‌های کوچکتر تقسیم کرد. این تقسیم بندی باید بر اساس استفاده کنندگان از واسط‌ها صورت گیرد.
ویرایش مثال بالا با در نظر گرفتن اصل ISP  :
public interface IEmployeeReportBAL
{
    void GeneratePFReport();
    void GenerateESICReport();
}
public interface IManagerReportBAL : IEmployeeReportBAL
{
    void GenerateResourcePerformanceReport();
    void GenerateProjectSchedule();
}
public interface IAdminReportBAL : IManagerReportBAL
{
    void GenerateProfitReport();
}
public class ReportBAL : IAdminReportBAL 
{    
    public void GeneratePFReport()
    {/*...............*/}

    public void GenerateESICReport()
    {/*...............*/}

    public void GenerateResourcePerformanceReport()
    {/*...............*/}

    public void GenerateProjectSchedule()
    {/*...............*/}

    public void GenerateProfitReport()
    {/*...............*/}
}
وضعیت نمایش :
public class EmployeeUI
{
    public void DisplayUI()
    {
        IEmployeeReportBAL objBal = new ReportBAL();
        objBal.GenerateESICReport();
        objBal.GeneratePFReport();
    }
}
  



public class ManagerUI
{
    public void DisplayUI()
    {
        IManagerReportBAL  objBal = new ReportBAL();
        objBal.GenerateESICReport();
        objBal.GeneratePFReport();
        objBal.GenerateResourcePerformanceReport ();
        objBal.GenerateProjectSchedule ();
    }
}
 




public class AdminUI
{
    public void DisplayUI()
    {
        IAdminReportBAL  objBal = new ReportBAL();
        objBal.GenerateESICReport();
        objBal.GeneratePFReport();
        objBal.GenerateResourcePerformanceReport();
        objBal.GenerateProjectSchedule();
        objBal.GenerateProfitReport();
    }
}





  DIP   : در قسمت بعدی آخرین اصل را بررسی خواهیم کرد  
مطالب
ASP.NET MVC #2

MVC‌ چیست و اساس کار آن چگونه است؟

الگوی MVC در سال‌های اول دهه 70 میلادی در شرکت زیراکس توسط خالقین زبان اسمال‌تاک که جزو اولین زبان‌های شیءگرا محسوب می‌شود، ارائه گردید. نام MVC از الگوی Model-View-Controller گرفته شده و چندین دهه است که در صنعت تولید نرم افزار مورد استفاده می‌باشد. هدف اصلی آن جدا سازی مسئولیت‌های اجزای تشکیل دهنده «لایه نمایشی» برنامه است.
این الگو در سال 2004 برای اولین بار در سکویی به نام Rails به کمک زبان روبی جهت ساخت یک فریم ورک وب MVC مورد استفاده قرار گرفت و پس از آن به سایر سکوها مانند جاوا، دات نت (در سال 2007)، PHP و غیره راه یافت.
تصاویری را از این تاریخچه در ادامه ملاحظه می‌کنید؛ از دکتر Trygve Reenskaug تا شرکت زیراکس و معرفی آن در Rails به عنوان اولین فریم ورک وب MVC.


C در MVC معادل Controller است. کنترلر قسمتی است که کار دریافت ورودی‌های دنیای خارج را به عهده دارد؛ مانند پردازش یک درخواست HTTP ورودی.


زمانیکه کنترلر این درخواست را دریافت می‌کند، کار وهله سازی Model را عهده دار خواهد شد و حاوی اطلاعاتی است که نهایتا در اختیار کاربر قرار خواهد گرفت تا فرآیند پردازش درخواست رسیده را تکمیل نماید. برای مثال اگر کاربری جهت دریافت آخرین اخبار به سایت شما مراجعه کرده است،‌ در اینجا کار تهیه لیست اخبار بر اساس مدل مرتبط به آن صورت خواهد گرفت. بنابراین کنترلرها، پایه اصلی و مدیر ارکستر الگوی MVC محسوب می‌شوند.
در ادامه، کنترلر یک View را جهت نمایش Model انتخاب خواهد کرد. View در الگوی MVC یک شیء ساده است. به آن می‌توان به شکل یک قالب که اطلاعاتی را از Model دریافت نموده و سپس آن‌ها را در مکان‌های مناسبی در صفحه قرار می‌دهد، نگاه کرد.
نتیجه استفاده از این الگو، ایزوله سازی سه جزء یاد شده از یکدیگر است. برای مثال View نمی‌داند و نیازی ندارد که بداند چگونه باید از لایه دسترسی به اطلاعات کوئری بگیرد. یا برای مثال کنترلر نیازی ندارد بداند که چگونه و در کجا باید خطایی را با رنگی مشخص نمایش دهد. به این ترتیب انجام تغییرات در لایه رابط کاربری برنامه در طول توسعه کلی سیستم، ساده‌تر خواهد شد.
همچنین در اینجا باید اشاره کرد که این الگو مشخص نمی‌کند که از چه نوع فناوری دسترسی به اطلاعاتی باید استفاده شود. می‌توان از بانک‌های اطلاعاتی، وب سرویس‌ها، صف‌ها و یا هر نوع دیگری از اطلاعات استفاده کرد. به علاوه در اینجا در مورد نحوه طراحی Model نیز قیدی قرار داده نشده است. این الگو تنها جهت ساخت بهتر و اصولی «رابط کاربری» طراحی شده است و بس.



تفاوت مهم پردازشی ASP.NET MVC با ASP.NET Web forms

اگر پیشتر با ASP.NET Web forms کار کرده باشید اکنون شاید این سؤال برایتان وجود داشته باشد که این سیستم جدید در مقایسه با نمونه قبلی، چگونه درخواست‌ها را پردازش می‌کند.


همانطور که مشاهده می‌کنید، در وب فرم‌ها زمانیکه درخواستی دریافت می‌شود، این درخواست به یک فایل موجود در سیستم مثلا default.aspx ارسال می‌گردد. سپس ASP.NET یک کلاس وهله سازی شده معرف آن صفحه را ایجاد کرده و آن‌را اجرا می‌کند. در اینجا چرخه طول عمر صفحه مانند page_load و غیره رخ خواهد داد. جهت انجام این وهله سازی، View به فایل code behind خود گره خورده است و جدا سازی خاصی بین این دو وجود ندارد. منطق صفحه به markup آن که معادل است با یک فایل فیزیکی بر روی سیستم، کاملا مقید است. در ادامه، این پردازش صورت گرفته و HTML نهایی تولیدی به مرورگر کاربر ارسال خواهد شد.
در ASP.NET MVC این نحوه پردازش تغییر کرده است. در اینجا ابتدا درخواست رسیده به یک کنترلر هدایت می‌شود و این کنترلر چیزی نیست جز یک کلاس مجزا و مستقل از هر نوع فایل ASPX ایی در سیستم. سپس این کنترلر کار پردازش درخواست رسیده را شروع کرده، اطلاعات مورد نیاز را جمع آوری و سپس به View ایی که انتخاب می‌کند، جهت نمایش نهایی ارسال خواهد کرد. در اینجا View این اطلاعات را دریافت کرده و نهایتا در اختیار کاربر قرار خواهد داد.



آشنایی با قرارداد یافتن کنترلرهای مرتبط

تا اینجا دریافتیم که نحوه پردازش درخواست‌ها در ASP.NET MVC بر مبنای کلاس‌ها و متدها است و نه بر مبنای فایل‌های فیزیکی موجود در سیستم. اگر درخواستی به سیستم ارسال می‌شود، در ابتدا، این درخواست جهت پردازش، به یک متد عمومی موجود در یک کلاس کنترلر هدایت خواهد شد و نه به یک فایل فیزیکی ASPX (برخلاف وب فرم‌ها).


همانطور که در تصویر مشاهده می‌کنید، در ابتدای پردازش یک درخواست، آدرسی به سیستم ارسال خواهد شد. بر مبنای این آدرس، نام کنترلر که در اینجا زیر آن خط قرمز کشیده شده است، استخراج می‌گردد (برای مثال در اینجا نام این کنترلرProducts است). سپس فریم ورک به دنبال کلاس این کنترلر خواهد گشت. اگر آن‌را در اسمبلی پروژه بیابد، از آن خواهد خواست تا درخواست رسیده را پردازش کند؛ در غیراینصورت پیغام 404 یا یافت نشد، به کاربر نمایش داده می‌شود.
اما فریم ورک چگونه این کلاس کنترلر درخواستی را پیدا می‌کند؟
در زمان اجرا، اسمبلی اصلی پروژه به همراه تمام اسمبلی‌هایی که به آن ارجاعی دارند جهت یافتن کلاسی با این مشخصات اسکن خواهند شد:
1- این کلاس باید عمومی باشد.
2- این کلاس نباید abstract باشد (تا بتوان آن‌را به صورت خودکار وهله سازی کرد).
3- این کلاس باید اینترفیس استاندارد IController را پیاده سازی کرده باشد.
4- و نام آن باید مختوم به کلمه Controller باشد (همان مبحث Convention over configuration یا کار کردن با یک سری قرار داد از پیش تعیین شده).

برای مثال در اینجا فریم ورک به دنبال کلاسی به نام ProductsController خواهد گشت.
شاید تعدادی از برنامه نویس‌های ASP.NET MVC تصور ‌کنند که فریم ورک در پوشه‌ی استانداردی به نام Controllers به دنبال این کلاس خواهد گشت؛ اما در عمل زمانیکه برنامه کامپایل می‌شود، پوشه‌ای در این اسمبلی وجود نخواهد داشت و همه چیز از طریق Reflection مدیریت خواهد شد.

مطالب
تفاوت‌های پروژه‌های ما و پروژه‌های اونا!

چندی قبل پروژه‌ای دولتی در زمینه‌ی غلط یابی متون فارسی منتشر شد؛ اما ... آنچنان بازتابی در بین سایت‌های ایرانی پیدا نکرد. حداکثر بازتاب آن مقاله‌ی روزنامه همشهری در این حد بود که "مایکروسافت که یکی برامون درست کرده بود! تو چرا بی خود زحمت کشیدی!". البته روزنامه‌ی همشهری هم مقصر نیست؛ چون به طور قطع نویسنده‌ی آن با توانایی‌های این برنامه در مقایسه با غلط یاب ساده مایکروسافت اطلاعات آنچنانی ندارد.
از سایت‌های ایرانی هم نباید انتظار داشت که برای محصولات ایرانی تبلیغ کنند! اگر به سایت‌های ایرانی فعال در زمینه‌ی IT دقت کنید بیشتر ذوق کردن‌های آن‌ها ناشی از کارهای شرکت خارجی است؛ مثلا:
- گوگل امروز در صفحه‌ی اولش یک عکس جدید گذاشته! (2000 تا سایت این رو پوشش می‌دن!)
- رئیس جدید مایکروسافت دیروز که می‌خواست بره سخنرانی دوبار جیغ کشید، سه بار دور سالن دوید گفت اینجا عجب شرکت باحالیه!
- استیوجابز خیلی مرد نازنینی است چون فقط یک دست شلوار لی و پولیور مشکی دارد که 10 سالش است همین فقط تنشه (هم... از شما چه پنهون وضع خود من هم بهتر از این نیست:) )
- فیس بوک امروز عکس‌های شما رو در پروفایلتون هم نمایش می‌ده!
وب سایت‌های لینوکسی ایرانی هم که یک چیزی توی این مایه‌ها هستند:
این مایکروسافت سه تا نقطه (!) باز یک سیستم عامل جدید بیرون داد، ولی اوبونتوی ما 5 ثانیه زودتر میاد بالا؛ به همین جهت هفته بعد قراره پس از طی مراحل نصب سیستم عامل که از امروز شروع میشه به همراه کلیه دوستانی که در این امر مهم دخیل هستند جشن بگیریم! زنده باد آزادی!

و ... سؤال اصلی اینجا است: چرا مدل توسعه‌‌ای که از آن صحبت شد نمی‌فروشه؟! چرا کسی برای آن تبلیغ نکرد؟ چرا آنچنان کسی ذوق زده نشد؟! چرا زود فراموش شد؟

خوب؛ حالا بد نیست نگاهی هم داشته باشیم به مدل توسعه‌ی "اونا"

یکی از این "اونا" مثلا می‌تونه تیم سیلورلایت مایکروسافت باشه. بیائیم ببینیم "اونا" چکار می‌کنند تا محصولاتشون بفروشه؛ ملت از شنیدن اخبار اون‌ها ذوق زده بشن؛ هزار‌تا سایت کارشون رو به رایگان تبلیغ کنند و ...
- "اونا" یا کلا هر تیم توسعه‌ای تشکیل شده از یک سری آدم! هر کدام از این‌ها یا در سایت MSDN یا به طور جداگانه وبلاگ دارند و کاری رو که به طور منظم انجام می‌دن، ارسال جزئیاتی اندک از پیشرفت‌های حاصل شده است. مثلا آقای TimHeuer هر از چندگاهی این کار رو می‌کنه. به این صورت دیگه روزنامه همشهری چاپ نیویورک نمیاد بنویسه، Adobe که یکی برامون درست کرده بود! تو چرا بی‌خود زحمت کشیدی! چون الان روزنامه‌ی نیویورک تایمز می‌دونه جزئیات کاری که انجام شده به مرور زمان چی بوده.
- "اونا" قسمتی رو در سایت MSDN دارند به نام Silverlight TV . یک نفر رو هم استخدام کردن به نام آقای جان پاپا! تا بیاد براشون با اعضای مختلف تیم مصاحبه کنه و یک سری از جزئیات رو بیشتر برای عموم مردم توضیح بده.
- "اونا" هر از چندگاهی سمینار برگزار می‌کنند: (+). میان پز می‌دن ما اینکار رو کردیم اونکار رو کردیم؛ دنیا، بدونید ما چقدر عالی هستیم!
- "اونا" یک bug tracking system دارند. یک features suggestion system دارند: (+) . مردم الان می‌دونند اگر باگی رو در سیستم پیدا کردند، کجا باید گزارش بدن. اگر نیاز به ویژگی جدیدی داشتند باید چکار کنند. صرفا با یک صفحه‌ی ثابت که لینک دریافت دو تا فایل رو گذاشته مواجه نیستند.
- "اونا" یک فوروم مخصوص هم برای Silverlight درست کردند تا استفاده کننده‌ها بیان ابتدایی‌ترین تا پیشرفته‌ترین سوالات خودشون رو مطرح کنند. نرفتند اون پشت قایم بشن! یا بگن این ایمیل ما است؛ دوست داشتید ایمیل بزنید ما هم وقت کردیم جواب می‌دیم!
- "اونا" مستندات و راهنمای بسیار کامل، قوی و قابل مرور تحت وب دارند: (+).
- "اونا" اگر کارشون را رایگان و سورس باز می‌خواهند ارائه دهند از یک سورس کنترل استفاده می‌کنند: (+).به رها کردن یک تکه سورس کد در ملاء عام کار سورس باز نمی‌گن! هر کاری آداب و اصول خودش رو داره। شما که نمیای بچه‌ت رو بذاری سر راه و بری؟! به این امید که خودش patch میشه، خودش به روز میشه، یکی پیدا میشه تا دستی به سرش بکشه!
البته این مورد جدیدا جهت پروژه غلط یاب یاد شده راه اندازی شده: (+) ولی فکر نمی‌کنم کسی متوجه شده باشه، چرا؟! چون اخبار رو این روزها علاقمندان از طریق فیدهای RSS دنبال می‌کنند؛ نه با مراجعه‌ی هر روزه به یک سایت. انتظار بی‌موردی است که استفاده‌ کنندگان هر روز به سایت ما سر بزنند تا ببینند چه خبره! به همین جهت RSS اختراع شده. همچنین پروژه اختصاصی فارسی و سورس کنترل انگلیسی هم همخوانی ندارند.
- "اونا" اکانت توئیتر دارند: (+). "اونا" اکانت فیس بوک دارند: (+). "اونا" از این امکانات برای گزارش دادن رخدادهای داخلی خودشون استفاده می‌کنند. یکی از ابزارهای مهم تبلیغاتی اون‌ها است. کلا رسانه‌ها رو در دنیای غرب به عصر قبل و بعد از توئیتر تقسیم بندی می‌کنند. پیش از توئیتر اخبار تهیه می‌شد و تبدیل به خوراک اطلاعاتی عموم می‌شد؛ الان اطلاعات موجود در توئیتر، جمع آوری، آنالیز و تحلیل می‌شود و سپس تبدیل به خوراک خبرگزاری‌ها می‌گردد.
- "اونا" برای ارائه دانلود‌هاشون دیتاسنتر اختصاصی دارند. جایی خوندم مساحت دیتاسنتر‌های فعلی گوگل در حد یکی از ایالت‌های آمریکا شده ...؛ تصورش رو بکنید که با یک وب سایت هاست شده در یک کشور ثالث، بخواهید یک نرم افزار 200 مگی را به هزاران نفر عرضه کنید. یا مشکل پنهای باند پیدا می‌کنید یا کند شدن سرور یا ...
- "اونا" اگر وقت کنند هر از چندگاهی برای تبلیغ و توسعه کارشون کتاب هم منتشر می‌کنند: (+)، تعدادی از این‌ها هم رایگان است.

و ... و ... بدون رعایت این موارد پروژه‌های خوب ارائه شده در اینترنت نمی‌فروشند! حتی شما دوست عزیز!

مطالب
آیا برنامه نویس‌های دات نت باید نگران دنیای 64 بیتی باشند؟

جواب ساده و کوتاه: خیر!
کدمدیریت شده‌ی شما در هر دو پلتفرم 32 بیتی - x86 و x64 بدون نیاز به هیچگونه تغییری و بدون نگرانی اجرا خواهد شد.
گزیده‌ای از MSDN :
اگر کد شما 100 درصد مدیریت شده است (managed code ایی که به صورت خالص از دات نت فریم ورک استفاده می‌کند و هیچگونه وابستگی خارجی دیگری به کتابخانه‌های دیگر ندارد)، تنها با کپی شدن در یک محیط x64 دارای CLR ایی 64 بیتی (دات نت فریم ورک 64 بیتی)، بدون هیچگونه مشکلی اجرا خواهد شد.

سؤال: چرا و چگونه؟!
کامپایلرهای دات نتی (تفاوتی نمی‌کند که چه زبانی مورد استفاده باشد)، کد شما را به IL‌ ترجمه می‌کنند و IL اساسا درکی از پروسسور ندارد. JIT است که در آخرین لحظه در این مورد تصمیم گیری می‌کند.

این نگرانی از کجا حاصل شده است؟
نگارش R2 ویندوز 2008 سرور، فقط 64 بیتی خواهد بود و ویندوز سرور 2008 فعلی، آخرین سروری از مایکروسافت است که هر دو نسخه‌ی 32 بیتی و 64 بیتی را دارد. بنابراین دیر یا زود تمام برنامه نویس‌های ویندوزی "مجبور" خواهند شد دنیای 64 بیتی را تجربه کنند. (البته اگر تاکنون آن‌را تجربه نکرده‌اند)
و البته هنوز یک سری از محیط‌های توسعه، کامپایلر مخصوص 64 بیتی ندارند (مانند دلفی که قرار است در طول سال جاری اولین تجربه‌ی 64 بیتی خود را ارائه دهد)

نکته:
در صفحه‌ی build ویژوال استودیو، شما می‌توانید نوع پلتفرم مورد نظر را نیز تعیین کنید:



پیش فرض آن بر روی Any CPU‌ است و در این حالت کد کامپایل شده‌ی شما بدون مشکل بر روی پلتفرم‌هایی که مشاهده می‌کنید اجرا خواهد شد و تنها پیشنیاز اجرای آن، نصب نسخه‌ی دات نت فریم ورک مخصوص آن پلتفرم است، بدون اینکه نیاز باشد برنامه نویس نگران جزئیات خاصی در مورد خصوصیات ویژه‌ی آن پلتفرم‌ ویژه باشد.


سؤال: اگر کد ما خالص نبود چطور؟ (منظور اینکه 100 درصد دات نتی نبود)

حالت الف) اگر از کامپوننت‌های خارجی استفاده می‌کنید (حتی اگر 100 درصد دات نتی هم باشند) حتما اطمینان حاصل کنید که برای پلتفرم خاصی کامپایل نشده‌اند (همان Any CPU‌‌ مورد استفاده بوده)، زیرا کد شما که برای تمام CPU ها کامپایل شده، در محیط 64 بیتی، تنها توانایی بارگذاری اسمبلی‌های 64 بیتی را خواهد داشت (64 بیتی رفتار می‌کند) و با مواجه شدن با اسمبلی‌هایی که برای یک پروسسور خاص دیگر کامپایل شده‌اند، با خطای BadImageFormatException خاتمه می‌یابد.

حالت ب) استفاده از API ویندوز یا DLL های غیر دات نتی
باید با هماهنگی با تولید کننده‌ی مربوطه حتما از نگارش 64 بیتی استفاده شود و همچنین برنامه‌ی شما باید توانایی استفاده از اشاره‌گرهای 64 بیتی را داشته باشد. اندازه‌ی نوع داده‌ای IntPtr در یک محیط 32 بیتی 4 است و در یک محیط 64 بیتی 8 خواهد بود (IntPtr.Size). اگر در حین اجرای ترجمه‌ی API یک کتابخانه به اشتباه بجای استفاده از IntPtr از int‌ استفاده شده باشد، ممکن است کد شما در یک محیط 32 بیتی سال‌ها بدون مشکل اجرا شود، اما در اولین اجرای خود در یک محیط 64 بیتی، کرش خواهد کرد. (بدلیل overflow حاصل)
IntPtr به اندازه‌ی کافی هوشمند است تا سایز خودش را مطابق پلتفرم تنظیم کند و مشکل ساز نشود.

مثال:

[DllImport("kernel32.dll")]
public static extern void GetSystemInfo([MarshalAs(UnmanagedType.Struct)] ref SYSTEM_INFO lpSystemInfo);

[StructLayout(LayoutKind.Sequential)]
public struct SYSTEM_INFO
{
internal _PROCESSOR_INFO_UNION uProcessorInfo;
public uint dwPageSize;
public IntPtr lpMinimumApplicationAddress;
public int lpMaximumApplicationAddress;
public IntPtr dwActiveProcessorMask;
public uint dwNumberOfProcessors;
public uint dwProcessorType;
public uint dwAllocationGranularity;
public ushort dwProcessorLevel;
public ushort dwProcessorRevision;
}

[StructLayout(LayoutKind.Explicit)]
public struct _PROCESSOR_INFO_UNION
{
[FieldOffset(0)]
internal uint dwOemId;
[FieldOffset(0)]
internal ushort wProcessorArchitecture;
[FieldOffset(2)]
internal ushort wReserved;
}

در این مثال که از API‌ ویندوز استفاده می‌شود، به اشتباه نوع lpMaximumApplicationAddress‌ به صورت int تعریف شده است (بجای IntPtr). این کد بدون مشکل در یک برنامه‌ی 32 بیتی کار می‌کند اما همین برنامه در یک محیط 64 بیتی یا کرش خواهد کرد یا مقدار lpMaximumApplicationAddress منفی گزارش می‌شود.