نظرات نظرسنجی‌ها
برای توسعه پذیری سیستم خود از چه روشهایی استفاده کرده اید؟
فکر نمیکنم چندان انتخابی باشه این موضوع چراکه بسته به پروژه‌های متفاوت روش‌ها تغییر میکنند. 
به نظرم گزینه 3 درست تره یعنی بسته به پروژه ای که داریم روش کار میکنیم بین این دو باید انتخاب صورت بگیره.
مطالب
Defensive Programming - بازگشت نتایج قابل پیش بینی توسط متدها
در این مطلب یکی از اهداف Defensive Programming تحت عنوان Predictability مرتبط با متدها را بررسی کرده و تمرکز اصلی، بر روی مقدار بازگشتی متدها خواهد بود. 
پیش نیازها
به طور کلی، نتیجه حاصل از اجرای یک متد می‌تواند یکی از حالت‌های زیر باشد:

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


متد ValidateEmail با خروجی Boolean

        public bool ValidateEmail(string email)
        {
            var valid = true;
            if (string.IsNullOrWhiteSpace(email))
            {
                valid = false;
            }

            var isValidFormat = true;//todo: using RegularExpression
            if (!isValidFormat)
            {
                valid = false;
            }

            var isRealDoamin = true;//todo: Code here that confirms whether domain exists.
            if (!isRealDoamin)
            {
                valid = false;
            }

            return valid;
        }

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

var email = "email@example.com";
var isValid = ValidateEmail(email);
if(isValid)
{
    //do something
}


متد ValidateEmail با صدور استثناء

        public void ValidateEmail(string email)
        {
            if (string.IsNullOrWhiteSpace(email)) throw new ArgumentNullException(nameof(email));

            var isValidFormat = true;//todo: using RegularExpression
            if (!isValidFormat) throw new ArgumentException("email is not in a correct format");

            var isRealDoamin = true;//todo: Code here that confirms whether domain exists.
            if (!isRealDoamin) throw new ArgumentException("email does not include a valid domain.")
        }

روش بالا هم جواب می‌دهد ولی بهتر است کلاس Exception سفارشی به عنوان مثال ValidationException برای این قضیه در نظر گرفته شود تا بتوان وهله‌های صادر شده از این نوع را در لایه‌های بالاتر مدیریت کرد.


متد ValidateEmail با چندین خروجی


برای این منظور چندین راه حل پیش رو داریم.


با استفاده از پارامتر out:

        public bool ValidateEmail(string email, out string message)
        {
            var valid = true;
            message = string.Empty;

            if (string.IsNullOrWhiteSpace(email))
            {
                valid = false;
                message = "email is null.";
            }

            if (valid)
            {
                var isValidFormat = true;//todo: using RegularExpression
                if (!isValidFormat)
                {
                    valid = false;
                    message = "email is not in a correct format";
                }
            }

            if (valid)
            {
                var isRealDoamin = true;//todo: Code here that confirms whether domain exists.
                if (!isRealDoamin)
                {
                    valid = false;
                    message = "email does not include a valid domain.";
                }
            }

            return valid;
        }
و نحوه استفاده از آن:
var email = "email@example.com";
var isValid = ValidateEmail(email, out string message);
if (isValid)
{
    //do something
}
خب کمی بهتر شد؛ ولی امکان دریافت لیست خطاهای اعتبارسنجی را به صورت یکجا نداریم و یک تک پیغام را در اختیار ما قرار می‌دهد. برای بهبود آن می‌توان از یک Tuple به شکل زیر برای تولید خروجی متد بالا نیز استفاده کرد.
Tuple<bool, List<string>> result = Tuple.Create<bool, List<string>>(true, new List<string>());
یا بهتر است یک کلاس مشخصی برای این منظور در نظر گرفت؛ به عنوان مثال:
        public class OperationResult
        {
            public bool Success { get; set; }
            public IList<string> Messages { get; } = new List<string>();

            public void AddMessage(string message)
            {
                Messages.Add(message);
            }
        }
در این صورت بدنه متد ValidateEmail به شکل زیر تغییر خواهد کرد:
        public OperationResult ValidateEmail(string email)
        {
            var result = new OperationResult();

            if (string.IsNullOrWhiteSpace(email))
            {
                result.Success = false;
                result.AddMessage("email is null.");
            }

            if (result.Success)
            {
                var isValidFormat = true;//todo: using RegularExpression
                if (!isValidFormat)
                {
                    result.Success = false;
                    result.AddMessage("email is not in a correct format");
                }
            }

            if (result.Success)
            {
                var isRealDoamin = true;//todo: Code here that confirms whether domain exists.
                if (!isRealDoamin)
                {
                    result.Success = false;
                    result.AddMessage("email does not include a valid domain.");
                }
            }

            return result;
        }

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


استفاده از Exception برای نمایش پیغام برای کاربر نهایی

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

به عنوان مثال داشتن یک کلاس Exception سفارشی تحت عنوان UserFriendlyException در این راستا یک الزام می‌باشد.

   [Serializable]
   public class UserFriendlyException : Exception
   {
       public string Details { get; private set; }
       public int Code { get; set; }

       public UserFriendlyException()
       {
       }

       public UserFriendlyException(SerializationInfo serializationInfo, StreamingContext context)
           : base(serializationInfo, context)
       {

       }

       public UserFriendlyException(string message)
           : base(message)
       {
       }

       public UserFriendlyException(int code, string message)
           : this(message)
       {
           Code = code;
       }

       public UserFriendlyException(string message, string details)
           : this(message)
       {
           Details = details;
       }

       public UserFriendlyException(int code, string message, string details)
           : this(message, details)
       {
           Code = code;
       }

       public UserFriendlyException(string message, Exception innerException)
           : base(message, innerException)
       {
       }

       public UserFriendlyException(string message, string details, Exception innerException)
           : this(message, innerException)
       {
           Details = details;
       }
   }

و همچنین لازم است در بالاترین لایه سیستم خود به عنوان مثال برای یک پروژه ASP.NET MVC یا ASP.NET Core MVC می‌توان یک ExceptionFilter سفارشی نیز تهیه کرد که هم به صورت سراسری استثنا‌ءهای سفارشی شما را مدیریت کند و همچنین خروجی مناسب Json برای استفاده در سمت کلاینت را نیز مهیا کند. به عنوان مثال برای درخواست‌های Ajax ای لازم است در سمت کلاینت نیز پاسخ‌های رسیده از سمت سرور به صورت سراسری مدیریت شوند و برای سایر درخواست‌ها همان نمایش صفحات خطای پیغام مرتبط با استثناء رخ داده شده کفایت می‌کند.


یک مدل پیشنهادی برای تهیه خروجی مناسب برای ارسال جزئیات استثنا رخ داده در درخواست‌های Ajax ای

    [Serializable]
    public class MvcAjaxResponse : MvcAjaxResponse<object>
    {
        public MvcAjaxResponse()
        {
        }

        public MvcAjaxResponse(bool success)
            : base(success)
        {
        }

        public MvcAjaxResponse(object result)
            : base(result)
        {
        }

        public MvcAjaxResponse(ErrorInfo error, bool unAuthorizedRequest = false)
            : base(error, unAuthorizedRequest)
        {
        }
    }
   

    [Serializable]
    public class MvcAjaxResponse<TResult> : MvcAjaxResponseBase
    {
        public MvcAjaxResponse(TResult result)
        {
            Result = result;
            Success = true;
        }

        public MvcAjaxResponse()
        {
            Success = true;
        }

        public MvcAjaxResponse(bool success)
        {
            Success = success;
        }

        public MvcAjaxResponse(ErrorInfo error, bool unAuthorizedRequest = false)
        {
            Error = error;
            UnAuthorizedRequest = unAuthorizedRequest;
            Success = false;
        }

        /// <summary>
        ///     The actual result object of AJAX request.
        ///     It is set if <see cref="MvcAjaxResponseBase.Success" /> is true.
        /// </summary>
        public TResult Result { get; set; }
    }

    public class MvcAjaxResponseBase
    {
        public string TargetUrl { get; set; }

        public bool Success { get; set; }

        public ErrorInfo Error { get; set; }

        public bool UnAuthorizedRequest { get; set; }

        public bool __mvc { get; } = true;
    }

و کلاس  ErrorInfo:
    [Serializable]
    public class ErrorInfo
    {
        public int Code { get; set; }
        public string Message { get; set; }
        public string Detail { get; set; }
        public Dictionary<string, string> ValidationErrors { get; set; }

        public ErrorInfo()
        {
        }
        public ErrorInfo(string message)
        {
            Message = message;
        }
        public ErrorInfo(int code)
        {
            Code = code;
        }

        public ErrorInfo(int code, string message)
            : this(message)
        {
            Code = code;
        }

        public ErrorInfo(string message, string details)
            : this(message)
        {
            Detail = details;
        }

        public ErrorInfo(int code, string message, string details)
            : this(message, details)
        {
            Code = code;
        }
    }

یک مثال واقعی
        public async Task CheckIsDeactiveAsync(long id)
        {
            if (await _organizationalUnits.AnyAsync(a => a.Id == id && !a.IsActive).ConfigureAwait(false))
                throw new UserFriendlyException("واحد سازمانی جاری غیرفعال می‌باشد.");
        }

روش نام گذاری متدهایی که امکان بازگشت خروجی Null را دارند

متد زیر را در نظر بگیرید:
public User GetById(long id);
وظیفه این متد یافت و بازگشت یک وهله از کلاس User می‌باشد و نباید خروجی Null تولید کند. در صورتیکه در پیاده سازی آن امکان یافت چنین کاربری نبود، بهتر است یک استثنای سفارشی دیگر شبیه به EntityNotFoundException زیر را صادر کنید:
    [Serializable]
    public class EntityNotFoundException : Exception
    {
        public Type EntityType { get; set; }
        public object Id { get; set; }
        public EntityNotFoundException()
        {
        }

        public EntityNotFoundException(string message)
            : base(message)
        {

        }

        public EntityNotFoundException(string message, Exception innerException)
            : base(message, innerException)
        {
        }

        public EntityNotFoundException(SerializationInfo serializationInfo, StreamingContext context)
            : base(serializationInfo, context)
        {

        }
        public EntityNotFoundException(Type entityType, object id)
            : this(entityType, id, null)
        {

        }
        public EntityNotFoundException(Type entityType, object id, Exception innerException)
            : base($"There is no such an entity. Entity type: {entityType.FullName}, id: {id}", innerException)
        {
            EntityType = entityType;
            Id = id;
        }

    }
یا اگر امکان بازگشت مقدار Null را داشته باشد، بهتر است نام آن به GetByIdOrNull تغییر یابد. در این صورت تکلیف استفاده کننده از این متد مشخص می‌باشد.

یک مثال واقعی 

        public async Task<UserOrganizationalUnitInfo> GetCurrentOrganizationalUnitInfoOrNullAsync(long userId)
        {
            return (await _setting.GetSettingValueForUserAsync(
                    UserSettingNames.CurrentOrganizationalUnitInfo, userId).ConfigureAwait(false))
                .FromJsonString<UserOrganizationalUnitInfo>();
        }

نظرات مطالب
Blazor 5x - قسمت 34 - توزیع برنامه‌های Blazor بر روی IIS
روش درست کردن دمو برای پروژه‌های blazor در Github (یا روش توزیع پروژه‌های Blazor WASM در Github-Pages)

ابتدا فایل yml زیر را در پوشه‌ی github\workflows\deploy.yml. قرار دهید (پوشه‌ای را به این نام، در ریشه‌ی پروژه‌ی خود ایجاد کنید):
name: Deploy to GitHub Pages

# Run workflow on every push to the main branch
on:
  push:
    branches: [ main ]

jobs:
  deploy-to-github-pages:
    # use ubuntu-latest image to run steps on
    runs-on: ubuntu-latest
    steps:
    # uses GitHub's checkout action to checkout code form the main branch
    - uses: actions/checkout@v2
    
    # sets up .NET Core SDK
    - name: Setup .NET Core SDK
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 5.0.302

    # publishes Blazor project to the release-folder
    - name: Publish .NET Core Project
      run: dotnet publish ./src/DNTPersianComponents.Blazor.WasmSample/Server/DNTPersianComponents.Blazor.WasmSample.Server.csproj -c Release -o release --nologo
    
    # changes the base-tag in index.html from '/' to 'DNTPersianComponents.Blazor' to match GitHub Pages repository subdirectory
    - name: Change base-tag in index.html from / to DNTPersianComponents.Blazor
      run: sed -i 's/<base href="\/" \/>/<base href="\/DNTPersianComponents.Blazor\/" \/>/g' release/wwwroot/index.html
    
    # copy index.html to 404.html to serve the same file when a file is not found
    - name: copy index.html to 404.html
      run: cp release/wwwroot/index.html release/wwwroot/404.html

    # add .nojekyll file to tell GitHub pages to not treat this as a Jekyll project. (Allow files and folders starting with an underscore)
    - name: Add .nojekyll file
      run: touch release/wwwroot/.nojekyll
      
    - name: Commit wwwroot to GitHub Pages
      uses: JamesIves/github-pages-deploy-action@3.7.1
      with:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        BRANCH: github-pages
        FOLDER: release/wwwroot
در این قالب، چهار مورد را باید ویرایش کنید:
- نام شاخه‌ی اصلی پروژه؛ که یا main است و یا master.
- شماره نگارش دات نت مورد استفاده.
- مسیر فایل csproj پروژه‌ی wasm.
- نام اصلی مخزن کد.


سپس آن‌را به مخزن کد خود commit کنید. بعد به قسمت settings->pages در github مراجعه کرده و source را بر روی نام شاخه‌ی جدید github-pages (فوق در قسمت آخر کار) قرار داده و آن‌را ذخیره کنید. الان سایت دموی شما در مسیری که در همین قسمت pages پس از ذخیره سازی، نمایش می‌دهد، آماده‌است.


یک نکته‌ی مهم

چون base href، توسط action فوق اصلاح می‌شود تا به پوشه‌ی نسبی محل قرارگیری برنامه اشاره کند، نیاز است navlink‌ها با href شروع شده‌ی با / نباشند؛ چون به ریشه‌ی سایت اشاره می‌کنند و نه مسیر نسبی محل قرارگیری برنامه. کلا در هر قسمتی از برنامه، این نکته باید رعایت شود. مثلا اگر فونت وبی را در فایل app.css تعریف کرده‌اید، مسیر آن نباید با / شروع شود؛ وگرنه یافت نخواهد شد. یک مثال:
فایل app.css برنامه در مسیر wwwroot\css\app.css قرار دارد و داخل آن فایل، فونت‌های پوشه‌ی دیگر wwwroot\lib\samim-font را به صورت زیر تعریف کرده‌ایم؛ که یعنی مسیر فونت را از ریشه‌ی سایت پیدا کن:
src: url('/lib/samim-font/Samim-Bold.eot?v=4.0.5');
این مسیر، باید به مسیر نسبی زیر که به یک پوشه‌ی بالاتر (از محل قرار گیری app.css) اشاره می‌کند، اصلاح شود:
src: url('../lib/samim-font/Samim-Bold.eot?v=4.0.5');
نظرات مطالب
Blazor 5x - قسمت 27 - برنامه‌ی Blazor WASM - کار با سرویس‌های Web API
یک نکته: تاثیر hot reload دات نت 6 بر روی درخواست‌های HTTP سمت کلاینت

ممکن است پس از ارتقاء به دات نت 6، شاهد خطاهای BadHttpRequestException زیادی در سمت Web API شوید (البته فقط در حالت توسعه):
Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Unexpected end of request content.
این خطا عنوان می‌کند که درخواست HTTP رسیده ناقص است و در اینجا بیشتر به معنای abort شدن آن در سمت کلاینت است. عکس العمل برنامه‌ی Blazor به یک چنین خطایی که در سمت سرور رخ داده، ریفرش کردن کل آدرس جاری و راه اندازی مجدد برنامه‌است. اما ... چرا این اتفاق رخ داده بود؟

اگر برنامه‌ی شما اطلاعاتی را در پوشه‌ی wwwroot ثبت کند، برای مثال یک بانک اطلاعاتی SQLite را در آن قرار داده‌اید، hot reload حتی این پوشه را هم تحت کنترل خود دارد و با هر تغییری در آن، حتی نوشته شدن یک رکورد در فایل بانک اطلاعاتی، مجددا برنامه را کامپایل می‌کند. همین کامپایل پشت صحنه، سبب abort شدن درخواست‌های HTTP سمت کلاینت آن و مشاهده‌ی خطاهای BadHttpRequestException سمت سرور می‌شود که عدم اطلاع از دلیل آن می‌تواند این تصور را پیش بیاورد که کدهای برنامه مشکلی دارند، اما خیر.
اگر با CLI کار می‌کنید، دستور dotnet watch run --no-hot-reload کمی وضع را بهتر می‌کند و معادل افزودن تنظیم زیر به فایل Properties\launchSettings.json است:
"profiles": {
  "Name.Server": {
      "hotReloadEnabled": false,
راه حل دیگر آن، خارج کردن پوشه‌ی wwwroot از حالت watch است که به صورت زیر در فایل csproj قابل انجام است (در سمت برنامه‌ی web api):
<ItemGroup>
  <Watch Remove="wwwroot\**\*" />
</ItemGroup>
مطالب
استفاده از date picker شمسی جاوا اسکریپتی در Blazor با قابلیت ورود تاریخ به صورت دستی
دیت پیکرهای گوناگونی توسط افراد مختلف نوشته شده‌اند که هر یک مشکلات خاص خود را دارند. در این مطلب به چگونگی استفاده از یکی از سازگارترین  دیت پیکرهای جاوا اسکریپتی که توسط آقای امیرمسعود ایرانی نوشته شده است در Blazor خواهیم پرداخت.
مهم‌ترین ویژگی این دیت پیکر امکان ورود تاریخ به صورت دستی توسط کاربر است.

فرمت‌های قابل قبول برای ورود تاریخ عبارتند از:
۹۰۰۸۱۴ ۱۴۰۸۹۰ ۱۳۹۰۰۸۱۴ ۱۴/۸/۹۰ ۹۰/۸/۱۴ ۱۴/۸/۱۳۹۰ ۱۳۹۰/۸/۱۴ ۱۴-۸-۹۰ ۹۰-۸-۱۴ ۱۴-۸-۱۳۹۰ ۱۳۹۰-۸-۱۴ 
و فرمت‌های ویژه:
۰۸۱۴ ۱۴۰۸ ۱۴-۸ ۸-۱۴ ۱۴/۸ ۸/۱۴ ۱۴
در فرمت‌های ویژه که سال و ماه وارد نشده‌اند، سال و ماه فعلی به حساب خواهد آمد.
در فرمت‌هایی که سال مشخص نشده باشد، دو رقم ابتدایی در صورت امکان روز محاسبه خواهند شد.
بنابراین قادر خواهیم بود که در خروجی یک فرمت استاندارد داشته باشیم حتی با فرمت‌های مختلفی که کاربر وارد خواهد کرد.

روش به کارگیری تقویم در Blazor

در ابتدا فایل‌های مورد نیاز را دانلود کرده (AMIB_jsPersianCal_0.2.1.rar) و به پروژه اضافه می‌کنیم.
سپس به _layout رفته و ارجاعات زیر را برای افزودن فایل‌های css و js به پروژه اضافه می‌کنیم:
<link href="css/js-persian-cal.css" rel="stylesheet"/>
<script src="js/js-persian-cal.min.js"></script>
حال برای استفاده از دیت پیکر در کامپوننت‌ها از تگ input به شکل زیر استفاده می‌کنیم:
<input type="text" id="pcal1" />
Id آن مهم است زیرا توسط آن به تابع جاوااسکریپتی معرفی می‌شود. می‌توان هر اسمی را اختیار کرد فقط بهتر است تمامی دیت پیکرهای موجود در صفحه یک اسم داشته باشند اما با ایندکس‌های مختلف مانند pcal1، pcal2 و ... . دلیل آن این است که می‌توان تمامی دیت پیکرهای را توسط یک حلقه به تابع مربوطه معرفی کرد.
همانطور که می‌دانید برای استفاده از توابع جاوا اسکریپتی در Blazor از JSRuntime استفاده می‌شود. بنابراین به شکل زیر عمل خواهیم کرد.
protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        int dateFieldCount = 1;
        if (firstRender)
        {
            for (int i = 1; i <= dateFieldCount; i++)
            {
                await JsRuntime.InvokeVoidAsync("CallAmib", "pcal" + i.ToString());
            }
        }
    }
توسط حلقه for تمامی تگ‌های input موجود در کامپوننت را که Id آنها با pcal شروع می‌شود به دیت پیکر تبدیل خواهیم نمود. فقط مقدار متغیر dateFieldCount را باید به تعداد تگ‌های دیت پیکر موجود در کامپوننت تنظیم نمود.
لازم به ذکر است که باید در ابتدای کامپوننت، JSRuntime را به شکل زیر تزریق نمود.
@inject IJSRuntime  JsRuntime
حال فقط کافیست اسکریپت CallAmib را ایجاد کرده و به _layout اضافه نمود.
window.CallAmib = (objCal1) => {
    new AMIB.persianCalendar(objCal1);
}
  بنابراین فایل _layout برنامه الان چیزی شبیه به زیر خواهد بود:
@using Microsoft.AspNetCore.Components.Web
@namespace ShamsiDatePickerBlazor.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="~/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />
    <link href="css/js-persian-cal.css" rel="stylesheet" />
    <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
</head>
<body>
    @RenderBody()

    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="">Reload</a>
        <a>🗙</a>
    </div>
    <script src="js/js-persian-cal.min.js"></script>
    <script src="js/CallAmib.js"></script>
    <script src="_framework/blazor.server.js"></script>
</body>
</html>
تا اینجای کار اگر پروژه را اجرا کنیم، دیت پیکری مانند زیر را خواهیم داشت:

مشکل!!

برای بایند کردن مقدار تاریخ انتخاب شده نمی‌توان از bind-value به طور معمول استفاده کرد؛ زیرا در حقیقت تغییرات input با جاوا اسکریپت انجام می‌گیرد و حالت صفحه تغییری نمی‌کند. برای مرتفع کردن این مشکل نیاز است که در اسکریپت CallAmib متد onchange به شکل زیر صدا زده شده و مقدار تاریخ انتخابی به یک متد داخل کامپوننت ارسال گردیده و در آنجا به یک فیلد منتسب شود.
window.CallCall = (objCal1) => {
    new AMIB.persianCalendar(objCal1,{
        onchange: function(pdate) {
            DotNet.invokeMethodAsync('ShamsiDatePickerBlazor', 'DateChanged', pdate.toString()).then(
                (date) => {
                    console.log(data);
                }
            );
        }
    });
}
توضیحات اسکریپت بالا:
متغیر pdate به صورت توکار مربوط به AMIB.persianCalendar می باشد و مقدار تاریخ انتخابی را در بر دارد.
متد DotNet.invokeMethodAsync یک متد توکار دات نت می‌باشد و برای فراخوانی متدهای سی شارپی از داخل توابع جاوا اسکریپتی به کار می‌رود. آرگومان اول آن در حقیقت نام اسمبلی پروژه می‌باشد. آرگومان دوم آن نام تابع سی شارپی‌است که باید فراخوانی شود و در نهایت آرگومان سوم آن تاریخ انتخاب شده می‌باشد.
در پایان باید متد DateChanged،  به شکل زیر در کامپوننت index نوشته شود:
static string selectedDate;
[JSInvokable]
public static void DateChanged(string pdate)
{
    selectedDate = pdate;
}
این تابع بایستی با صفت [JSInvokable] مزین شود و حتما هم استاتیک باشد.
برای دیدن مقدار جدید selectedDate کافی است روی دکمه ShowNewValue یکبار کلیک نمایید.
نکته: می‌توان به جای input، از InputText مربوط به EditForm هم استفاده نمود. فقط باید یک Id هم به آن انتساب داد. همچنین برای انتساب مقدار دیت پیکر به مدل، باید در متد DateChanged، فیلد مورد نظر از مدل را بجای متغیر selectedDate گذاشت.
شما می‌توانید در اینجا کدهای کامل این مطلب را ملاحظه نمایید.
نظرات اشتراک‌ها
بررسی وضعیت فعلی پروژه Roslyn
کامپایلر فعلی سی‌شارپ یا csc.exe یک exe بومی (native) ویندوز است. آنچنان هم متدهای قابل توجهی را در اختیار برنامه نویس قرار نمی‌دهد. فقط با خط فرمان می‌شود با آن کار کرد. ضمن اینکه نوشتن محصور کننده هم برای آن نهایتا به کار تمیزی ختم نخواهد شد. نمونه‌اش API ویندوز است. به همین جهت بازنویسی آن با سی‌شارپ به کدی با کیفیت بالاتر و قابلیت نگهداری بهتر ختم شده‌است. همین مساله در آینده کمک خواهد کرد تا قابلیت‌های بیشتری را سریعتر بتوانند به زبان‌های دات نتی اضافه کنند.
مطالب
تولید فایل Word بدون نصب MS Word بر روی سرور

یکی از مواردی که ممکن است در محیط کاری با آن برخورد داشت، تقاضای تولید فایل word یک گزارش با فرمتی مشخص از یک برنامه ASP.Net است. برای مثال یک قالب درست کرده‌اند که header‌ و footer و کلا یک فرمت رسمی دارد. الان برنامه شما باید این فایل word رسمی را با گزارشی که تولید می‌کند پر کند. حالا اینجاست که گرفتاری برنامه نویس شروع می‌شود! روی سرور باید word نصب باشد تا توسط اشیاء COM آن بتوان یک چنین کارهایی را آن‌هم با ASP.Net که به صورت پیش فرض کمترین سطح دسترسی را روی سیستم دارد انجام داد. یا اینکه باید به سراغ کامپوننت‌های تجاری رفت و حالا اینجا با این وضع تحریم و غیره چگونه بتوان آنها را خریداری کرد یا شاید احتمالا در سایت‌های وارز بتوان نسخه تکه پاره شده آنها را یافت. مشکلی هم که این نوع کامپوننت‌ها دارند این است که ممکن است سال دیگر اصلا ساپورت نشوند. محصولات مایکروسافت هم که مرتبا در حال به روز رسانی هستند. در این حالت برنامه متکی به این نوع کامپوننت‌های تجاری سورس بسته در همان نگارش قبلی خود مجبور است باقی بماند.
خوشبختانه با ارائه آفیس 2007 و فرمت OpenXML فایلهای آن، این مشکل تقریبا مرتفع شده است. مایکروسافت نیز برای سهولت تولید این نوع اسناد، OpenXML SDK را ارائه داده است که از آدرس زیر قابل دریافت است:
Open XML Format SDK 1.0

البته پیش نمایش نگارش دو SDK آن نیز موجود است که در مطلب جاری به آن پرداخته نخواهد شد.

فایل‌های office 2007 از یک فایلzip تشکیل شده از چند فایل xml داخل آن، ایجاد شده‌اند. برای مثال یک فایل docx را با winrar یا امثال آن باز کنید (تصویر زیر):



برای کار با اینگونه اسناد باید با اصطلاحات زیر آشنا شد:
Package : فایل zip شما (همان فایل برای مثال docx) اینجا یک بسته نام دارد.
Parts : اجزای این بسته که همان فایل‌های آن هستند، parts نامیده شده اند.
Relations : اگر به فایل‌های موجود در یک بسته دقت کنید، فایلهایی با پسوند rels را خواهید دید که بیانگر نحوه ارتباط Parts با یکدیگر هستند.
Relations Ids: هر ارتباط با یک ID منحصربفرد تعریف می‌گردد.

اگر علاقمند باشید که پوستری را در این رابطه مشاهده نمائید می‌توان به آدرس زیر مراجعه نمود.
Open XML Developer Map

نحوه استفاده از OpenXML SDK در دات نت:
ابتدا باید ارجاعی را به فایل DocumentFormat.OpenXml.dll که پس از نصب در مسیر OpenXMLSDK\1.0.1825\lib قرار گرفته است به پروژه افزود. سپس نیاز است تا ارجاعی به کتابخانه WindowsBase نیز به برنامه افزوده شود (تصویر زیر). افزودن ارجاعی به این کتابخانه جهت کامپایل برنامه ضروری است (شکل زیر).


تا اینجا ارجاعات برنامه به صورت زیر خواهند بود:



یک مثال ساده:
قصد داریم یک فایل docx ساده را با استفاده از OpenXML SDK ایجاد کنیم. در مثال زیر فرمت متغیر docXml را می‌توان با ایجاد یک فایل docx ساده در word و سپس باز کردن بسته فشرده شده آن و مشاهده محتوای فایل word\document.xml بدست آورد.
using System.IO;
using System.Text;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;

namespace OpenXMLTestApp
{
class CWord
{

public static void CreateDocument(string documentFileName, string text)
{
using (WordprocessingDocument wordDoc =
WordprocessingDocument.Create(documentFileName, WordprocessingDocumentType.Document))
{
MainDocumentPart mainPart = wordDoc.AddMainDocumentPart();

string docXml =
@"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>
<w:document xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
<w:body><w:p><w:r><w:t>#REPLACE#</w:t></w:r></w:p></w:body>
</w:document>";

docXml = docXml.Replace("#REPLACE#", text);

using (Stream stream = mainPart.GetStream())
{
byte[] buf = (new UTF8Encoding()).GetBytes(docXml);
stream.Write(buf, 0, buf.Length);
}
}
}
}
}

و نحوه استفاده از آن می‌تواند به صورت زیر باشد:

CWord.CreateDocument("test.docx", "سلام دنیا");

این کتابخانه کار ایجاد فایل‌های xml ، تولید روابط بین‌ آنها و همچنین بسته بندی و zip کردن نهایی را به صورت خودکار انجام می‌دهد.

برای مطالعه بیشتر می‌توان به منابع زیر مراجعه نمود:

یک ویدیوی آموزشی رایگان از مایکروسافت
دریافت

سؤالات متداول در MSDN
http://msdn.microsoft.com/en-us/library/bb491088.aspx
البته اگر پس از نصب SDK به پوشه doc آن مراجعه نمائید، این سؤال و جواب‌ها را در فایل راهنمای chm آن نیز می‌توان پیدا کرد.

مثال دیگری در مورد ایجاد یک گزارش از بانک اطلاعاتی و گرفتن خروجی docx از آن
http://openxmldeveloper.org/articles/GenerateWordTable.aspx
البته این مثال خیلی قدیمی است و قسمت‌های کار با پکیج را با SDK‌ ارائه شده می‌توان به صورت خودکار انجام داد. اما حداقل نحوه تولید جداول استاندارد OpenXML را می‌توان از آن ایده گرفت.

مثالی در مورد نحوه قرار دادن عکس در فایل docx تولیدی

همچنین مثال‌های بیشتری را در وبلاگ‌های مربوطه می‌توان یافت:
http://blogs.msdn.com/brian_jones/
http://blogs.msdn.com/ericwhite/default.aspx


مطالب
پیاده سازی پروژه‌های React با TypeScript - قسمت چهارم - تعیین نوع هوک‌های useState و useRef
پس از بررسی روش تعیین نوع‌های خواص props در قسمت‌های قبل، اکنون نوبت به بررسی روش تعیین نوع‌های انواع React Hooks است. در این قسمت دو هوک پرکاربرد useState و useRef را بررسی می‌کنیم.


روش تعیین نوع useState Hook

برای این منظور در ابتدا فایل جدید src\components\Input.tsx را ایجاد کرده و به صورت زیر تکمیل می‌کنیم:
import React, { useState } from "react";

export const Input = () => {
  const [name, setName] = useState("");
  return <input value={name} onChange={(e) => setName(e.target.value)} />;
};
همچنین تعریف المان آن‌را نیز به فایل src\App.tsx جهت نمایش </ Input> با ذکر "import { Input } from "./components/Input، انجام می‌دهیم.

پس از این تعاریف ... برنامه بدون مشکل کار می‌کند و کامپایل می‌شود. اکنون سؤال اینجا است که آیا تایپ‌اسکریپت در ایجا اصلا کاری را هم انجام می‌دهد؟ برای درک این موضوع، سطر useState را به صورت زیر تغییر می‌دهیم:
const [name, setName] = useState(0);
بلافاصله خطای زیر ظاهر می‌شود:

عنوان می‌کند که مقدار رشته‌ای e.target.value، به مقدار عددی name قابل انتساب نیست. به عبارتی TypeScript از قابلیت Type Inference خود در اینجا استفاده می‌کند. درست است که به ظاهر نوعی را برای useState و خروجی منتسب به آن تعیین نکرده‌ایم، اما بر اساس نوع مقدار پیش‌فرض آن، نوع name و setName به صورت خودکار مشخص می‌شوند و نیازی به ذکر صریح این نوع، نیست. برای مثال در حالت اول چون مقدار پیش‌فرض useState را یک رشته‌ی خالی معرفی کرده بودیم، نوع name نیز string درنظر گرفته شده بود. در حالت دوم بر اساس مقدار پیش‌فرض عددی useState، اینبار نوع name نیز یک number خواهد بود و دیگر نمی‌توان یک مقدار رشته‌ای را مانند e.target.value به آن انتساب داد. مزیت کار کردن با TypeScript در اینجا، مشاهده‌ی آنی خطای یک چنین استفاده‌ها و انتساب‌های نادرستی است.

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



و نکته‌ی مهم اینجا است که نیازی به ذکر صریح این نوع جنریک، مانند مثال زیر نیست:
const [name, setName] = useState<string>("");
و سطر فوق با سطر زیر که بیانگر Type Inference است، دقیقا یکی است:
const [name, setName] = useState("");

سؤال: اگر مقدار اولیه‌ی useState را null تعیین کردیم و یا اصلا تعریف نکردیم و undefined بود، چطور؟
در یک چنین حالتی که نوع دقیق state، از طریق مقدار اولیه‌ی آن قابل استنتاج نیست، نیاز هست همانند تصاویر فوق، تعریف جنریک useState را به نحو صریحی ذکر کرده و آن‌را با union types تکمیل کنیم:
const [name, setName] = useState<string | null>(null);
به این ترتیب عنوان کرده‌ایم که نوع name در اینجا می‌تواند رشته‌ای و یا نال باشد.


روش تعیین نوع useRef Hook

در ادامه می‌خواهیم نحوه‌ی تعیین نوع DOM Elements را در React بررسی کنیم. با استفاده از useRef می‌توان به ارجاعی از یک DOM Element دسترسی یافت.
import React, { useRef, useState } from "react";

export const Input = () => {
  const [name, setName] = useState("");
  const inputRef = useRef(null);

  if (inputRef && inputRef.current) {
    console.log("ref", inputRef.current.value);
  }
  return (
    <input
      ref={inputRef}
      value={name}
      onChange={(e) => setName(e.target.value)}
    />
  );
};
برای اینکار ابتدا useRef را با یک مقدار اولیه‌ی null، توسط ویژگی ref، به یک DOM Element خاص متصل می‌کنیم. تا اینجا برنامه بدون مشکل کار می‌کند؛ اما زمانیکه خواستیم برای مثال به inputRef.current.value دسترسی پیدا کنیم، دیگر تعریف ساده‌ی useRef(null) پاسخگو نبوده و خطای زیر گزارش می‌شود:


عنوان می‌کند نوعی که inputRef.current دارد، نال است و نال به همراه خاصیت value نیست. برای اینکه نوع inputRef را بهتر بتوانیم بررسی کنیم، دقیقا بر روی آن کلیک راست کرده و گزینه‌ی Go to Type Definition را انتخاب کنید. بلافاصله به تعریف زیر هدایت خواهیم شد:
interface MutableRefObject<T> {
   current: T;
}
inputRef، از نوع MutableRefObject جنریک است که تنها دارای یک خاصیت current است. نوع T آن هم در اینجا با توجه به مقدار اولیه‌ی آن، null درنظر گرفته شده‌است. به همین جهت هرچند می‌دانیم inputRef.current به المان input صفحه اشاره می‌کند، اما نمی‌توانیم به خواص و متدهای آن دسترسی پیدا کنیم.
برای رفع این مشکل فقط کافی است نوع المان مدنظر را صریحا به عنوان آرگومان جنریک useRef معرفی کنیم:
const inputRef = useRef<HTMLInputElement>(null);
نحوه‌ی تشخیص این نوع هم ساده‌است. فقط کافی است اشاره‌گر ماوس را بر روی المانی خاص قرار دهید. بلافاصله نوع آن مشخص می‌شود:



فعال بودن Strict Null Checking در پروژه‌های TypeScript ای React

نکات مطلب «نوع‌های نال نپذیر در TypeScript» به صورت پیش‌فرض در پروژه‌های تایپ اسکریپتی React هم فعال هستند؛ چون پرچم strict واقع در فایل tsconfig.json پروژه، به صورت پیش‌فرض به true تنظیم شده‌است و این پرچم، Strict Null Checking را نیز شامل می‌شود. برای آزمایش فعال بودن آن، کدهای فوق را به صورت زیر تغییر دهید تا صرفا if آن حذف شود:
//if (inputRef && inputRef.current) {
  console.log("ref", inputRef.current.value);
//}
بلافاصله خطای امکان نال بودن inputRef.current در اولین بار رندر کامپوننت جاری را دریافت خواهید کرد:

که برای رفع آن، همانند قبل باید ذکر if بررسی نال بودن inputRef و خاصیت current آن‌را اضافه کرد؛ تا دیگر در زمان اجرای برنامه، با شانس نال بودن یکی از این‌دو مواجه نشویم و به کیفیت بالاتری در برنامه‌ی خود برسیم.
روش بررسی if (inputRef && inputRef.current) معادل ساده‌تری را نیز در TypeScript 3.7 به بعد دارد که به Optional Chaining معروف است؛ به صورت زیر:
console.log("ref", inputRef?.current?.value);
در این حالت دیگر نیازی به ذکر if یاد شده نیست و وجود .? به معنای ادامه‌ی این زنجیره، در صورت نال نبودن قطعه‌ی قبلی است.
مطالب
استفاده از #F در پروژه های MVC4
در این پست با روش پیاده سازی پروژه‌های WPF با استفاده از #F آشنا شدید. قصد دارم در طی چند پست روش پیاده سازی پروژه‌های Asp.Net MVC 4  با استفاده از #F را شرح دهم.
»اگر با #F آشنایی ندارید می‌توانید از اینجا شروع کنید.
به صورت کلی برای استفاده گسترده از #F در پروژه‌های وب نیاز به یک سری template‌های آماده داریم در غیر این صورت کار کمی سخت خواهد شد. به تصویر زیر دقت نمایید:

واضح است که با توجه به تصویر بالا کنترلر‌ها و البته مدل‌های app  و هر آنچه که سمت سرور به آن نیاز است باید با استفاده از #F پیاده سازی شوند. اما هنگامی که کنترلر‌ها با استفاده از #F نوشته شوند سیستم مسیر یابی نیز تحت تاثیر قرار خواهد گرفت. علاوه بر آن باید فکری برای بخش Bundling و همچنین فیلتر‌هاو... نمود. در نتیجه با توجه به template پروژه مورد نظر بر خلاف حالت پیش فرض #C آن که در قالب یک پروژه ارائه می‌شود در این جا حداقل به دو پروژه نیاز داریم. خوشبختانه همانند پروژه FSharpX که برای WPF مناسب است برای MVC نیز template آماده موجود است که در ادامه با آن آشنا خواهیم شد.

شروع به کار

ابتدا در VS.Net یک پروژه جدید ایجاد نمایید. از بخش Online Template گزینه FSharp MVC 4 را جستجو کنید. 

بعد از انتخاب نام پروژه و  کلیک بر روی Ok ( و البته دانلود حدود ده MB اطلاعات) صفحه زیر نمایان می‌شود. در این قسمت تنظیمات مربوط به انتخاب View Engine و نوع قالب پروژه را وجود دارد. در صورتی که قصد استفاده از Web Api را دارید گزینه  Web Api Project را انتخاب کنید در غیر این صورت گزینه Empty Project.

البته از Visual Studio 2012 به بعد این بخش به صورت زیر خواهد بود که قسمت Single Page App به آن اضافه شده است:

بعد از کلیک بر روی Ok یک پروژه بر اساس Template مورد نظر ساخته می‌شود. همانند تصویر زیر:

بررسی تغییرات

در یک نگاه به راحتی می‌توان تغییرات زیر را در پروژه Web تشخیص داد:

»پوشه Controller وجود ندارد؛

»پوشه مدل وجود ندارد؛

»فایل Global.asax دیگر فایلی به نام Global.asax.cs را همراه با خود ندارد.

دلیل اصلی عدم وجود موارد بالا این است که تمام این موارد باید به صورت #F پیاده سازی شوند در نتیجه به پروژه #F ساخته شده منتقل شده اند. فایل Global.asax را باز نمایید. سورس زیر قابل مشاهده است:

<%@ Application Inherits="FsWeb.Global" Language="C#" %> <script Language="C#" RunAt="server">
 // Defines the Application_Start method and calls Start in // System.Web.HttpApplication from which Global inherits. protected void Application_Start(Object sender, EventArgs e) { base.Start(); } </script>
تمامی کاری که تکه کد بالا انجام می‌دهد فراخواهی متد Start در فایل Global متناظر در پروژه #F است. اگر به قسمت reference‌های پروژه Web دقت کنید خواهید که به پروژه #F متناظر رفرنس دارد.
حال به بررسی پروژه #F  ساخته شده خواهیم پرداخت. در این پروژه یک فایل  Global.fs وجود دارد که سورس آن به صورت زیر است:

namespace FsWeb

open System
open System.Web
open System.Web.Mvc
open System.Web.Routing

type Route = { controller : string
               action : string
               id : UrlParameter }

type Global() =
    inherit System.Web.HttpApplication() 

    static member RegisterRoutes(routes:RouteCollection) =
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
        routes.MapRoute("Default", 
                        "{controller}/{action}/{id}", 
                        { controller = "Home"; action = "Index"
                          id = UrlParameter.Optional } )

    member this.Start() =
        AreaRegistration.RegisterAllAreas()
        Global.RegisterRoutes(RouteTable.Routes)
  در پروژه‌های MVC بر اساس زبان #C،  کلاسی به نام RouteConfig وجود دارد که در فایل Global.asax.cs فراخوانی می‌شود:
RouteConfig.RegisterRoutes(RouteTable.Routes);
و کد‌های مورد نظر جهت تعیین الگوی مسیر یابی کنترلر‌ها و درخواست‌ها در کلاس RouteConfig نیز به صورت زیر می‌باشد:
public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
اما در اینجا بر خلاف #C، تعریف و اجرای سیستم مسیر یابی در یک فایل انجام می‌شود. در بخش اول ابتدا یک type تعریف می‌شود که معادل با تعیین Routing در زبان #C است. علاوه بر آن متد RegisterRoutes جهت تعیین و انتساب Route Type تعریف شده به Route Collection مورد نظر نیز در این فایل می‌باشد. 

//تعریف الکوی مسیر یابی
type Route = { controller : string
               action : string
               id : UrlParameter }

type Global() =
    inherit System.Web.HttpApplication() 

    static member RegisterRoutes(routes:RouteCollection) =
       //فراخوانی  و انتساب الگوی مسیر یابی به  مسیر‌های تعریف شده
       routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
        routes.MapRoute("Default", 
                        "{controller}/{action}/{id}", 
                        { controller = "Home"; action = "Index"
                          id = UrlParameter.Optional } )
در نهایت نیز تابع RegisterRoute در تابع Start فراخوانی خواهد شد.
Global.RegisterRoutes(RouteTable.Routes)
نتیجه کلی تا اینجا:
در این پست با Template پروژه‌های F# MVC 4 اشنا شدیم و از طرفی مشخص شد که برای پیاده سازی این گونه پروژه‌ها حداقل نیاز به دو پروژه داریم. یک پروژه که از نوع #C است ولی در آن فقط View ‌ها و فایل جاوااسکریپتی و البته Css وجود دارد. از طرف دیگر کنترلر‌ها و مدل‌ها و هر چیز دیگر که مربوط به سمت سرور است در قالب یک پروژه #F پیاده سازی می‌شود.
در پست بعدی با روش تعریف و توسعه کنترلر‌ها و مدل‌ها آشنا خواهیم شد.