مطالب دوره‌ها
به روز رسانی غیرهمزمان قسمتی از صفحه به کمک jQuery در ASP.NET MVC
یک صفحه شلوغ و سنگین را در نظر بگیرید. برای مثال قرار است ابتدا مطلب خاصی در سایت نمایش یابد و سپس ادامه صفحه که شامل انبوهی از لیست نظرات مرتبط با آن مطلب است به صورت غیرهمزمان و Ajax ایی بدون توقف پردازش صفحه، در فرصتی مناسب از سرور دریافت و به صفحه اضافه گردد (به روز رسانی قسمتی از صفحه در فرصت مناسب). در این حالت چون نمایش اولیه صفحه سریع صورت می‌گیرد، کاربر نهایی آنچنان احساس کند بودن بازکردن صفحات سایت را نخواهد داشت. در ادامه نحوه پیاده سازی این روش را به کمک jQuery Ajax بررسی خواهیم کرد.

مدل و کنترلر برنامه

namespace jQueryMvcSample07.Models
{
    public class BlogPost
    {
        public int Id { set; get; }
        public string Title { set; get; }
        public string Body { set; get; }
    }
}

using System.Web.Mvc;
using System.Web.UI;
using jQueryMvcSample07.Models;
using jQueryMvcSample07.Security;

namespace jQueryMvcSample07.Controllers
{
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            return View(); //نمایش یک منوی ساده در ابتدای کار
        }

        [HttpGet]
        public ActionResult ShowSynchronous()
        {
            var model = getModel();
            return View(model); //نمایش همزمان
        }

        private static BlogPost getModel()
        {
            //شبیه سازی یک عملیات طولانی
            System.Threading.Thread.Sleep(3000);
            var model = new BlogPost
            {
                Title = "عنوان ... ",
                Body = "مطلب... "
            };
            return model;
        }

        [HttpGet]
        public ActionResult ShowAsynchronous()
        {
            return View(); //نمایش ابتدایی صفحه
        }

        [HttpPost]
        [AjaxOnly]
        [OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
        public ActionResult RenderAsynchronous()
        {
            //دریافت اطلاعات به صورت غیرهمزمان
            var model = getModel();
            return PartialView(viewName: "_Post", model: model);
        }
    }
}
مدل برنامه، بیانگر ساختار اطلاعات مطلبی است که قرار است نمایش یابد.
در کنترلر Home، ابتدا اکشن متد Index آن فراخوانی شده و در این حالت دو لینک زیر نمایش داده می‌شوند:
@{
    ViewBag.Title = "Index";
}
<h2>
    نمایش اطلاعات به صورت همزمان و غیرهمزمان</h2>
<ul>
    <li>
        @Html.ActionLink(linkText: "نمایش همزمان", actionName: "ShowSynchronous", controllerName: "Home")
    </li>
    <li>
        @Html.ActionLink(linkText: "نمایش غیر همزمان", actionName: "ShowAsynchronous", controllerName: "Home")
    </li>
</ul>
لینک اول، به اکشن متد ShowSynchronous اشاره می‌کند و لینک دوم به اکشن متد ShowAsynchronous.
در هر دو صفحه نهایتا از یک Partial View به نام _Post.cshtml برای نمایش اطلاعات استفاده خواهد شد:
@model jQueryMvcSample07.Models.BlogPost
<fieldset>
    <legend>@Model.Title</legend>
    @Model.Body
</fieldset>
زمانیکه کاربر بر روی لینک نمایش همزمان کلیک می‌کند، به صفحه زیر هدایت می‌شود:
@model jQueryMvcSample07.Models.BlogPost
@{
    ViewBag.Title = "ShowSynchronous";
}

<h2>نمایش همزمان</h2>
@{ Html.RenderPartial("_Post", Model); }
این صفحه، یک صفحه متداول است و اطلاعات آن دقیقا در زمان نمایش صفحه اخذ شده و چون در اینجا از یک Sleep عمدی برای تولید اطلاعات استفاده گردیده است، نمایش آن حداقل سه ثانیه طول خواهد کشید.
در حالتیکه کاربر بر روی لینک نمایش غیرهمزمان کلیک می‌کند، صفحه زیر را مشاهده خواهد کرد:
@{
    ViewBag.Title = "ShowAsynchronous";
    var loadInfoUrl = Url.Action(actionName: "RenderAsynchronous", controllerName: "Home");
}
<h2>
    نمایش غیر همزمان</h2>
<div id="info" align="center">
</div>
<div id="progress" align="center" style="display: none">
    <br />
    <img src="@Url.Content("~/Content/images/loadingAnimation.gif")" alt="loading..."  />
</div>
@section JavaScript
{
    <script type="text/javascript">
        $(function () {
            $("#progress").css("display", "block");
            $.ajax({
                type: "POST",
                url: '@loadInfoUrl',
                complete: function (xhr, status) {
                    var data = xhr.responseText;
                    if (xhr.status == 403) {
                        window.location = "/login";
                    }
                    else if (status === 'error' || !data || data == "nok") {
                        alert('خطایی رخ داده است');
                    }
                    else {
                        $("#progress").css("display", "none");
                        $("#info").html(data);
                    }
                }
            });
        });
    </script>
}
نمایش ابتدایی این صفحه بسیار سریع است. در ابتدای کار progress bar ایی فعال شده و سپس از طریق jQuery Ajax درخواست دریافت اطلاعات رندر شده اکشن متدی به نام RenderAsynchronous به سرور ارسال می‌شود. چون عملیات Ajax غیرهمزمان است، کاربر نیازی نیست تا رندر شدن کامل صفحه ابتدا صبر کند و سپس کل صفحه به او نمایش داده شود. در اینجا ابتدا صفحه به صورت کامل نمایان شده و سپس درخواستی Ajax ایی به سرور ارسال می‌گردد. در پایان عملیات، Partial View یاد شده رندر گردیده و در div ایی با id مساوی info نمایش داده می‌شود.
به این ترتیب می‌توان حس سریع بودن سایت را زمانیکه قسمتی از صفحه نیاز به زمان بیشتری برای نمایش اطلاعات دارد، به کاربر منتقل کرد.

دریافت پروژه کامل این قسمت
jQueryMvcSample07.zip 
نظرات مطالب
شروع به کار با بوت استرپ 4
یک نکته‌ی تکمیلی: چگونه کلاس‌های CSS استفاده نشده را تشخیص دهیم؟

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


در developer tools آن، برگه‌ی Sources و سپس در اینجا، قسمت Snippets را انتخاب کنید. در همین ناحیه کلیک راست کرده و گزینه‌ی new را انتخاب نمائید. سپس قطعه کد «List all undefined CSS classes» را در باکس خالی جلوی آن paste کنید و ctrl+s را بفشارید.

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


بلافاصله در برگه‌ی Console، چنین خروجی را مشاهده خواهید کرد:


این‌ها کلاس‌هایی هستند که در صفحه‌ی جاری استفاده شده‌اند، اما از بوت استرپ 4 (جمع تمام CSSهایی که به صفحه الحاق شده‌اند) حذف شده‌اند و دیگر حضور ندارند.
نظرات مطالب
طراحی گردش کاری با استفاده از State machines - قسمت دوم
- آیا چنین انتقال هایی به شکل زیر کار اصولی می‌باشد؟ 

به عنوان مثال اگر Trigger1 همان رویداد Save ما بوده که با توجه به Domain ای که در آن قرار داریم انجام آن در حالت‌های مشخص شده رویداد معتبری باشد، ماشین حالت بالا صحیح می‌باشد یا اینکه باید با یک رویداد دیگری به حالت State1 برگشته و دوباره انتقال به حالت State2 را انجام دهیم. البته متوجه هستم که به ازای تک تک رویدادها همیشه و نه لزوما باید دکمه متناظری را باتوجه به حالت فعلی شیء مورد، توسط کاربر قابل مشاهده باشد. با توجه به اینکه اگر کاربر نهایی به ازای تک تک رویدادهای موجود، لزوما دکمه‌های متناظری را در فرم مشاهده نکند و نیاز باشد یک دکمه Shortcut مانندی قرار بگیرد که احتمال دارد باعث چندین انتقال شود، Fire کردن چندین رویداد در پشت صحنه، کار اصولی می‌باشد؟

- نحوه استفاده از ماشین حالت ایجاد شده در لایه Application/ServiceLayer به چه شکل خواهد بود؟ حدس و سعی بنده به شکل زیر می‌باشد:
 public InvoiceStateMachine(InvoiceModel model)
        {
            _stateMachine =
                new StateMachine<InvoiceStatus, Trigger>(() => model.Status, state => model.Status = state);
سپس تعریف فیلدی به شکل زیر به منظور داشتن یک رویداد Parameterised، در دل ماشین حالت ایجاد شده:
public Func<InvoiceModel, Task> OnSaveAsync = null;
private readonly StateMachine<InvoiceStatus, Trigger>.TriggerWithParameters<InvoiceModel> _saveTrigger;

کانفیگ آن به شکل زیر:
 _stateMachine.Configure(InvoiceStatus.Pending)
                .OnEntryFromAsync(_saveTrigger, async invoice => await OnSaveAsync(invoice))
سپس در InvoiceService به شکل زیر عمل کرده:
 private void InitializeStateMachine(InvoiceModel model)
        {
            _stateMachine = new InvoiceStateMachine(model)
            {
                OnSaveAsync = async invoiceModel =>
                {
                    var result = model.IsNew() ? await CreateAsync(model) : await EditAsync(model);
                    if (!result.Succeeded)
                        throw new BusinessRuleValidationException(result.Message);
                }
            };
        }

و در نهایت متدی که از بیرون فراخوانی خواهد شد:
[Transactional]
public async Task<Result> SaveAsync(InvoiceModel model)
{
    try
    {
        InitializeStateMachine(model);
        await _stateMachine.TryFireSaveAsync(model);
        return Success();
    }
    catch (BusinessRuleValidationException e)
    {
        return Failed(e.Message);
    }
}

برای انتقال خطاهای ایجاد شده در زمان انتقال، راه حلی به غیر از صدور یک استثناء مشخص و گرفتن آن و ساخت خروجی مورد نظر متد سرویس به ذهنم نمی‌رسد.
مطالب
الگوی PRG در ASP.NET MVC
تا حالا با این پنجره حتما مواجه شدین:


دارید اطلاعات یک فرم داخل صفحه رو به سمت سرور میفرستید و پس از اتمام عملیات، صفحه دوباره نمایش داده میشه. در این حالت اگه دکمه F5 یا دکمه Refresh مرور گر رو بزنید، با این پنجره مواجه میشید که میگه دارید اطلاعات قبلی رو دوباره به سمت سرور میفرستید. بعضی وقت‌ها کاربران به هر دلیل دوباره صفحه رو Refresh میکنند و با این پنجره روبرو میشن بدون اینکه بدونن جریان از چه قراره، دوباره اطلاعات رو به سمت سرور میفرستن و این کار باعث ثبت اطلاعات تکراری میشه. برای جلوگیری از این کار الگویی به نام Post/Redirect/Get هست که راه حلی رو برای اینکار پیشنهاد میده. 

راه حل به این صورت هست که پس از پست شدن فرم به سمت سرور و انجام عملیات، بجای اینکه صفحه، دوباره با استفاده از متد GET به کاربر نشون داده بشه، کاربر Redirect بشه به صفحه. برای توضیح این مسئله به سراغ AccountController که بصورت پش فرض وقتی یک پروژه ASP.NET MVC رو از نوع Internet ایجاد میکنید، وجود داره. 

 اکشن Register از نوع GET صفحه ثبت نام کاربر رو نمایش میده. 
[HttpGet]
[AllowAnonymous]
public ActionResult Register()
{
      return View();
}
پس از اینکه کاربر اطلاعات داخل فرم رو پر کرد و به سمت سرور فرستاد و صحت اطلاعات فرستاده معتبر و عمل ثبت موفقیت آمیز بود برای ادامه کار به دو روش میتوان عمل کرد:
 ١- کاربر به صفحه دیگری منتقل بشه و در اون صفحه پیام موفقیت آمیز بودن عملیات نشون داده بشه. مثلا معمولا پس از انجام عمل ثبت نام، کاربر به صفحه شخصی یا صفحه اصلی سایت منتقل میشه  و یا در موقع ویرایش اطلاعات پش از انجام عمل ویرایش کاربر به صفحه دیگری که لیستی از آیتمها که کاربر یکی از آنها را ویرایش کرده باز گردانده میشه.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
    if (ModelState.IsValid)
    {
        // Attempt to register the user
        try
        {
            WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
            WebSecurity.Login(model.UserName, model.Password);
            ViewBag.Message = "Successfully Registered!";

            // PRG has been maintained
            return RedirectToAction("Index", "Home");
        }
        catch (MembershipCreateUserException e)
        {
            ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
        }
    }
    // If we got this far, something failed, redisplay form
    return View(model);
}
٢- نمایش دوباره صفحه ولی با تغییر هدر صفحه به کد 303 . کد 303 به مروگر اعلام میکنه صفحه ریدایرکت شده است
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
    if (ModelState.IsValid)
    {
        // Attempt to register the user
        try
        {
            WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
            WebSecurity.Login(model.UserName, model.Password);
            ViewBag.Message = "Successfully Registered!";

            // PRG has been maintained
            return RedirectToAction("Register");
        }
        catch (MembershipCreateUserException e)
        {
            ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
        }
    }
    // If we got this far, something failed, redisplay form
    return View(model);
}
در این حالت دوباره صفحه ثبت نام نمایش داده میشه ولی با زدن دکمه رفرش، اطلاعات دوباره به سمت سرور فرستاده نمیشه
مطالب
پیاده سازی پروژه‌های 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 یاد شده نیست و وجود .? به معنای ادامه‌ی این زنجیره، در صورت نال نبودن قطعه‌ی قبلی است.
نظرات مطالب
راه اندازی وب سایت سریع و سبک با Nancy
همانطور که جناب نصیری گفتند، به این روش که آدرس‌ها را کاربر پسند می‌کنیم اصطلاحا Url rewriting می‌گویند. این نوع آدرس دهی باعت خوانا شدن، و همچنین بهتر ایندکس شدن در موتورهای جستجو می‌شود.
در دات نت پروژه MVC آدرس‌ها را طبق این اصول پیاده می‌کند و کاربر می‌تواند قاعده آدرس دهی خود را به صورت یک Route تعریف کند.
در Asp .net سنتی هم کتابخانه‌های Url Rewriting وجود دارند که برای شما این کار را انجام می‌دهند. ضمن اینکه خود شما هم با نوشتن HttpModule می‌توانید این کار را انجام دهید. 
نظرات مطالب
EF Code First #1
تمام ORMهای دات نتی در سطح پایین خودشون از ADO.NET استفاده می‌کنند. بنابراین پشت صحنه این‌ها، استفاده از Data Reader و امثال آن است، به همراه یک سری مزیت جانبی دیگر مانند: «5 دلیل برای استفاده از یک ابزار ORM» و جلوگیری از اختراع چرخ‌هایی به شدت ناقص و معیوب مانند: «مروری بر کدهای کلاس SqlHelper» و همچنین امنیت توکار و پیش فرض لحاظ شده در آن‌ها مانند: «امنیت در LINQ to SQL»
مطالب
اجرای وظایف زمان بندی شده با Quartz.NET - قسمت دوم
در این قسمت، نحوه‌ی استفاده از قابلیت‌های کتابخانه‌ی Quartz.NET را در قالب پرسش و پاسخ ادامه می‌دهیم.

ابتدا یک توضیح کلی:
برای مدیریت وظیفه‌ها در Quartz.NET، در هر جای پروژه می‌توانید به صورت ذیل به مدیر وظیفه‌ها دسترسی داشته باشید.
var scheduler = new StdSchedulerFactory().GetScheduler();
از حالا به بعد، هر جا که در کدها کلمه‌ی scheduler را دیدید، ایجاد آن از طریق خط قبل بوده است.
سعی کنید همیشه هنگام ایجاد اشیا از نوع IJobDetail و ITrigger، از متد WithIdentity (همان طور که در قسمت قبل مشاهده کردید) برای نامگذاری وظایف و triggerها استفاده کنید تا بتوانید بعداً با استفاده از نامشان به آنها ارجاع پیدا کرده و مدیریتشان کنید

1) سوال: چگونه می‌توان در یک زمان دلخواه (مثلاً در زمان کلیک بر روی یک دکمه)، اجرای یک وظیفه را متوقف کرد؟
جواب: برای توقف تمامی وظایف می‌توان از متد ()Shutdown شی scheduler استفاده کرد:
scheduler.Shutdown(true);
در صورتی که مقدار true را به متد Shutdown پاس دهید، تا زمانی که وظایفی که در وسط اجرای خود هستند کارشان به پایان نرسد، کنترل اجرای برنامه به خط بعد نمی‌رود، اما در صورتی که این متد را بدون پارامتر فراخوانی کنید یا مقدار false را به آن پاس دهید، کنترل اجرای برنامه به دستور بعد می‌رود و وظایف در پشت صحنه به کار خود ادامه می‌دهند. دقت داشته باشید که منظور از ادامه‌ی کار یک وظیفه، ادامه‌ی کار آن در وضعیت جاری خود است. به بیان واضح تر، اگر مرتبه‌ی اجرای یک وظیفه 20 مرتبه بود و در مرتبه‌ی دوم اجرای آن، متد ()Shutdown به هر صورتی فراخوانی شد، مرتبه‌های دیگر به هیچ وجه اجرا نمی‌شوند.
اگر چند وظیفه به طور همزمان در حال اجرا باشند و قصد داشته باشید تا یکی از آنها را متوقف کنید، یکی از دو حالت زیر وجود دارد:
1)  یک وظیفه به چند trigger نسبت داده شده است.
2)  هر وظیفه فقط یک trigger دارد.
در صورتی که قصد دارید وظیفه از تمامی triggerها گرفته شود (معمولاً هم همین رفتار مد نظر است)، از متد DeleteJob استفاده کنید؛ و اگر قصد دارید تا اجرای وظیفه توسط یک trigger مشخص لغو شود و triggerهای دیگر مختص آن وظیفه به کار خود ادامه دهند، از متد UnscheduleJob استفاده کنید. اگر از متد DeleteJob استفاده می‌کنید، نام وظیفه را با ایجاد نمونه ای از کلاس JobKey برای آن مشخص کنید و در صورتی که از متد UnscheduleJob استفاده می‌کنید، نام trigger را با ایجاد نمونه ای از کلاس TriggerKey تعیین کنید.
scheduler.DeleteJob(new JobKey("job1"));
// or
scheduler.UnscheduleJob(new TriggerKey("trigger1"));

2) سوال: چگونه می‌توان اجرای وظیفه‌ها را به حالت تعلیق در آورد؟
جواب: برای به تعلیق در آوردن اجرای تمامی وظایف، از متد ()StandBy استفاده کنید:
scheduler.Standby();
برای ادامه‌ی کار، متد ()Start را مجدداً فراخوانی کنید.
در صورتی که قصد دارید اجرای وظیفه ای خاص را به حالت تعلیق در آورید، از متد ()PauseJob استفاده کنید. نام وظیفه را با ایجاد نمونه ای از کلاس JobKey برای آن مشخص کنید:
scheduler.PauseJob(new JobKey("job1"));
برای ادامه‌ی وظیفه، از متد ()ResumeJob استفاده کنید. نام وظیفه را با ایجاد نمونه ای از کلاس JobKey برای آن مشخص کنید:
scheduler.ResumeJob(new JobKey("job1"));
برای تعلیق اجرای تمامی وظایف، متد ()PauseAll، و برای ادامه‌ی کار تمامی وظایف، متد ()ResumeAll را فراخوانی کنید.

3) سوال: چگونه می‌توان یک وظیفه‌ی در حال اجرا را آپدیت کرد و تغییر مشخصات داد؟
جواب: با استفاده از متد AddJob و تنظیم پارامتر دوم آن به مقدار true:
IJobDetail job = JobBuilder.Create<NewJob>()
                             .WithIdentity("job1")
                             .Build();

scheduler.AddJob(job, true);

اگر قبلاً کلاسی با عنوان OldJob برای وظیفه ای با نام job1 تعریف شده است، با استفاده از قطعه کد بالا می‌توان کلاس NewJob را به جای آن معرفی کرد. البته به شرطی که نام وظیفه‌ی جدید با نام وظیفه‌ی قدیم، یکسان باشد. پارامتر دوم متد AddJob مشخص می‌کند که آیا در صورتی که نام وظیفه ای که قرار است در فرایند زمانبندی قرار بگیرد با نام یکی از وظایف موجود یکسان باشد، وظیفه‌ی جدید، جایگزین وظیفه‌ی قدیم شود یا خیر.

4) سوال: چگونه می‌توان یک trigger در حال اجرا را آپدیت کرد و تغییر مشخصات داد؟
جواب: یک trigger جدید ایجاد و با استفاده از متد ()RescheduleJob، جایگزین trigger قدیمی کنید:
ITrigger trigger = TriggerBuilder.Create()
                                 .WithIdentity("newTrigger")
                                 .StartNow()
                                 .ForJob("job1")
                                 .WithSimpleSchedule(x => x.WithIntervalInSeconds(5).WithRepeatCount(20))
                                 .Build();

scheduler.RescheduleJob(new TriggerKey("oldTrigger"), trigger);

نام trigger جدید می‌تواند با نام trigger قدیم یکسان باشد. در تکه کد قبل، triggerیی با نام newTrigger ایجاد و زمان اجرای آن به حال تنظیم شده است. با استفاده از متد ()ForJob و تعیین نام وظیفه، trigger جدید را به وظیفه ای با نام job1 نسبت داده ایم. بازه‌ی زمانی اجرا، هر 5 ثانیه و 21 مرتبه خواهد بود. در متد ()RescheduleJob و در پارامتر اول آن، نام trigger قدیمی را با ایجاد شی ای از کلاس TriggerKey مشخص کرده ایم و به پارامتر دوم، شی ایجاد شده برای trigger جدید را پاس داده ایم.

5) سوال: چگونه می‌توان تعداد دفعات اجرای یک وظیفه را بی نهایت تعیین کرد؟
پاسخ: با استفاده از متد ()RepeatForever در هنگام ایجاد trigger:
ITrigger trigger = TriggerBuilder.Create()
                                 .WithIdentity("trigger1")
                                 .StartAt(startTime)
                                 .ForJob("job1")
                                 .WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever())
                                 .Build();

6) سوال: چگونه می‌توان تعداد دفعات اجرای تمامی وظایف را به دست آورد؟
پاسخ: با استفاده از متد ()GetMetaData:
SchedulerMetaData metaData = scheduler.GetMetaData();

int numberOfJobsExecuted = metaData.NumberOfJobsExecuted;
متد ()GetMetaData، اطلاعاتی در مورد مدیر وظایف می‌دهد. نوع برگشتی این متد، SchedulerMetaData است. یکی از خصیصه‌های این نوع، NumberOfJobsExecuted نام دارد که تعداد دفعات اجرای تمامی وظایف تا زمان حال را برگشت می‌دهد.

7) سوال: چگونه می‌توان زمان آغاز به کار مدیر زمانبندی را متوجه شد؟
پاسخ: یکی دیگر از خصیصه‌های نوع RunningSince ،SchedulerMetaData نام دارد که بدین منظور استفاده می‌شود.
SchedulerMetaData metaData = scheduler.GetMetaData();

DateTimeOffset? runningSince = metaData.RunningSince;

ادامه دارد...
مطالب
ویرایش قالب پیش فرض Add View در ASP.NET MVC برای سازگار سازی آن با Twitter bootstrap
همانطور که در مطلب «اعمال کلاس‌های ویژه اعتبارسنجی Twitter bootstrap به فرم‌های ASP.NET MVC» ملاحظه کردید، برای سازگار سازی یک فرم جدید ایجاد شده ASP.NET MVC با پیش فرض‌های Twitter bootstrap، حداقل 8 مرحله باید طی شود و ... چقدر خوب می‌شد اگر این‌کارها به صورت خودکار توسط VS.NET بجای قالب پیش فرض ایجاد فرم آن، تولید می‌شد. در ادامه قصد داریم این سفارشی سازی را انجام دهیم.


مراحل کلی سفارشی سازی قالب‌های Scaffolding پیش فرض ASP.NET MVC

قالب‌های Scaffolding پیش فرض ASP.NET در مسیر Microsoft Visual Studio X\Common7\IDE\ItemTemplates\CSharp\Web\MVC X\CodeTemplates قرار دارند. برای نمونه اگر بخواهیم پیش فرض‌های تولید فرم‌های MVC4 را تغییر دهیم، باید به پوشه MVC 4\CodeTemplates\AddView\CSHTML مراجعه و فایل Create.tt را ویرایش کنیم.
اینکار هرچند عملی است اما آنچنان جالب نیست؛ از این جهت که تاثیری کلی و سراسری خواهد داشت.
برای اعمال محلی این تغییرات فقط به یک پروژه خاص، تنها کافی است همین مسیر CodeTemplates\AddView\CSHTML به همراه تمام فایل‌های tt آن، در پوشه جاری پروژه مدنظر ما کپی شود. به این ترتیب ابتدا به این پوشه محلی مراجعه خواهد شد.
روش دوم کپی کردن این فایل‌ها، استفاده از بسته نیوگت ذیل است:
 PM> Install-Package Mvc4CodeTemplatesCSharp


سفارشی سازی فایل Create.tt پیش فرض ASP.NET MVC جهت سازگار سازی آن با Twitter bootstrap

در اینجا قصد داریم همان 8 مرحله مطلب «اعمال کلاس‌های ویژه اعتبارسنجی Twitter bootstrap به فرم‌های ASP.NET MVC» را به فایل Create.tt که اکنون در پوشه CodeTemplates\AddView\CSHTML\Create.tt ریشه پروژه جاری قرار دارد، اعمال کنیم.
الف) ابتدا نام این فایل را به CreateBootstrapForm.tt تغییر می‌دهیم. از این لحاظ که این نام جدید در drop down مرتبط با scaffold template صفحه Add view ظاهر خواهد شد. به علاوه نیازی نیست تا این فایل tt در همان لحظه اجرا شود، بنابراین به خواص آن در VS.NET مراجعه کرده و مقدار گزینه custom tool آن‌را خالی می‌کنیم (مانند سایر فایل‌های tt اضافه شده).
ب) قسمت ابتدایی فایل CreateBootstrapForm.tt را که همان کپی مطابق اصل فایل Create.tt است، به نحو ذیل تغییر می‌دهیم:
<#
    if (!mvcHost.IsContentPage) {
#>
<script src="~/Scripts/jquery-1.9.1.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>

<#
    }
}
#>
@using (Html.BeginForm()) {
    @Html.ValidationSummary(true, null, new { @class = "alert alert-error alert-block" })

    <fieldset class="form-horizontal">
        <legend><#= mvcHost.ViewDataType.Name #></legend>

<#
foreach (ModelProperty property in GetModelProperties(mvcHost.ViewDataType)) {
    if (!property.IsPrimaryKey && !property.IsReadOnly && property.Scaffold) {
#>
        <div class="control-group">
<#
        if (property.IsForeignKey) {
#>
            @Html.LabelFor(model => model.<#= property.Name #>, "<#= property.AssociationName #>",new {@class="control-label"})
<#
        } else {
#>
            @Html.LabelFor(model => model.<#= property.Name #>,new {@class="control-label"})
<#
        }
#>
        
           <div class="controls">
<#
        if (property.IsForeignKey) {
#>
            @Html.DropDownList("<#= property.Name #>", String.Empty)
<#
        } else {
#>
            @Html.EditorFor(model => model.<#= property.Name #>)
<#
        }
#>
            @Html.ValidationMessageFor(model => model.<#= property.Name #>,null,new{@class="help-inline"})
</div>
        </div>

<#
    }
}
#>
<div class="form-actions">
            <button type="submit" class="btn btn-primary">ارسال</button>
            <button class="btn">لغو</button>
          </div>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>
<#
if(mvcHost.IsContentPage && mvcHost.ReferenceScriptLibraries) {
#>

@section JavaScript {    

}
که حاصل آن به صورت ذیل قابل استفاده و دسترسی خواهد بود:


دریافت فایل CreateBootstrapForm.tt اصلاح شده:
همانطور که عنوان شد، برای استفاده از آن فقط کافی است آن‌را در مسیر CodeTemplates\AddView\CSHTML\CreateBootstrapForm.tt ریشه پروژه جاری خود کپی کنید.
مطالب
MVVM و فراخوانی متدهای اشیاء View از طریق ViewModel

ما در ViewModel دسترسی مستقیمی به هیچ یک از اشیاء موجود در View نداریم (و درستش هم همین است). الان فرض کنید که می‌خواهیم از طریق ViewModel یک View را ببندیم؛ مثلا متد Close آن پنجره را فراخوانی کنیم. به عبارتی در حالت کلی می‌خواهیم یکی از متدهای تعریف شده یکی از عناصر بصری موجود در View را از طریق ViewModel فراخوانی نمائیم.
برای حل این مساله از فایل‌های همان SDK‌ مرتبط با Expression blend استفاده خواهیم کرد.

ابتدا ارجاعاتی را به اسمبلی‌های System.Windows.Interactivity.dll و Microsoft.Expression.Interactions.dll اضافه می‌کنیم.
سپس دو فضای نام مرتبط هم باید اضافه شوند:

  xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"  
  xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"

یک مثال عملی:
قصد داریم از طریق ViewModel ، پنجره‌ای را ببندیم. کدهای XAML این مثال را در ادامه مشاهده خواهید کرد:

<Window x:Class="WpfCallMethodActionSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:vm="clr-namespace:WpfCallMethodActionSample.ViewModels"
Name="ThisWindow"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<vm:MainWindowViewModel x:Key="vmMainWindowViewModel" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource vmMainWindowViewModel}}">

<Button Content="Save &amp; Close" VerticalAlignment="Top" Margin="5">
<i:Interaction.Triggers>
<!--فراخوانی متدی در ویوو مدل-->
<i:EventTrigger EventName="Click">
<ei:CallMethodAction
TargetObject="{Binding}"
MethodName="SaveButtonClicked" />
</i:EventTrigger>

<!--فراخوانی متدی در شیء جاری از طریق ویوو مدل-->
<i:EventTrigger SourceObject="{Binding}" EventName="CloseMainWindow">
<ei:CallMethodAction
TargetObject="{Binding ElementName=ThisWindow}"
MethodName="Close"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
</Window>

همچنین ViewModel تعریف شده نیز همین چند سطر زیر است:

using System;

namespace WpfCallMethodActionSample.ViewModels
{
public class MainWindowViewModel
{
public void SaveButtonClicked()
{
close();
}

public event EventHandler CloseMainWindow;
private void close()
{
if (CloseMainWindow != null) CloseMainWindow(this, EventArgs.Empty);
}
}
}

توضیحات:
اگر به ViewModel دقت کنید خبری از DelegateCommand در آن نیست. بله، به کمک ترکیبی از EventTrigger و CallMethodAction می‌توان جایگزینی را جهت DelegateCommand معرفی شده در قسمت‌های قبل این سری مباحث MVVM ارائه داد.
EventTrigger در اینجا به این معنا است که اگر EventName ذکر شده رخ داد، آنگاه این اعمال را انجام بده. مثلا در اینجا CallMethodAction را فراخوانی کن.
CallMethodAction در اسمبلی Microsoft.Expression.Interactions.dll تعریف شده است و تنها متدی از نوع void و بدون پارامتر را می‌تواند به صورت خودکار فراخوانی کند (محدودیت مهم آن است).
اینکه این متد کجا قرار دارد، توسط TargetObject آن مشخص می‌شود. اگر TargetObject را مساوی Binding قرار دادیم، یعنی به دنبال متدی که در DataContext گرید وجود دارد بگرد. به عبارتی به صورت خودکار به SaveButtonClicked تعریف شده در ViewModel ما متصل خواهد شد و آن‌را فراخوانی می‌کند.

تا اینجا رخداد Click دکمه تعریف شده را به متد SaveButtonClicked موجود در ViewModel سیم کشی کردیم.

در مرحله بعد می‌خواهیم از طریق ViewModel ، متدی را در View فراخوانی کنیم. نکته آن هم پیشتر ذکر شد؛ TargetObject صحیحی را باید انتخاب کرد. در اینجا برای پنجره جاری نام ThisWindow تعریف شده است و از طریق تعریف:

TargetObject="{Binding ElementName=ThisWindow}"

به CallMethodAction خواهیم گفت که قرار است متد Close را در شیء ThisWindow فراخوانی کنی.
همچنین نحوه تعریف EventTrigger ما هم در اینجا برعکس شده است:

<i:EventTrigger SourceObject="{Binding}"  EventName="CloseMainWindow">

قبلا به دنبال مثلا رخداد Click یک دکمه بودیم، اکنون با توجه به SourceObject تعریف شده، در ViewModel به دنبال این رخداد که برای نمونه در اینجا CloseMainWindow نام گرفته خواهیم گشت.

بنابراین View اینبار به رخداد CloseMainWindow تعریف شده در ViewModel سیم کشی خواهد شد. اکنون اگر این رخداد در ViewModel فراخوانی شود، CallMethodAction متناظر فعال شده و متد Close پنجره را فراخوانی می‌کند.