مطالب
آشنایی با فریمورک الکترون Electron
فریمورک الکترون، ساخته شده توسط Github، مدتی است سر و صدای زیادی به پا کرده است و شرکت‌های بزرگی در حال استفاده‌ی از این فریمورک در برنامه‌های دسکتاپ خود هستند که Microsoft Visual Studio Code یکی از آنهاست. الکترون از چند لحاظ مورد لطف جامعه‌ی برنامه نویسان قرار گرفته است که تعدادی از علل آن را بررسی می‌کنیم:

  1. ساخت برنامه‌های دسکتاپ به صورت چندسکویی (ویندوز، لینوکس، مک)
  2. استفاده از HTML,CSS,JavaScript که طراحان وب در این زمینه با آن به آسانی ارتباط برقرار می‌کنند.
  3. قابلیت استفاده از کتابخانه‌های قدرتمند تحت وب چون Bootstrap,Jquery,Angular Js و ...
  4. متن باز و رایگان است.
برای راه اندازی الکترون نیاز است که یکی از بسته‌های Node.js یا IO.js را نصب نمایید تا از طریق مخزن npm نسبت به نصب آن اقدام کنیم. محیط کنسول آن را باز میکنیم و مشغول نوشتن می‌شویم. ابتدا از طریق npm در دایرکتوری پروژه، فایل package.json را ایجاد می‌کنیم. بدین منظور دستور زیر را در کنسول وارد می‌کنیم:
D:\electron\test1>npm init
تعدادی سوال از شما میکند و بر اساس پاسخ‌هایتان فایل package.json را می‌سازد که فعلا می‌توانید وارد نکنید و بعدا طبق میلتان آن را ویرایش کنید. بعد از آن نیاز است الکترون را داخل این دایرکتوری نصب کنیم تا در لیست وابستگی‌های (Dependencies) فایل Package.json قرار بگیرد. برای نصب آن لازم است دستور زیر وارد کنید:
npm i electron-prebuilt --save-dev
دستور بالا با فلگ save  یا s، باعث می‌شود نسخه‌ی prebuilt الکترون به عنوان یکی از وابستگی‌ها، به سیستم اضافه شود و فلگ dev اعلام می‌دارد که بسته‌ی الکترون را در وابستگی‌های توسعه و دیباگینگ قرار بده.

 لازم است در اینجا توضیحی کوتاه در مورد انواع وابستگی‌ها داشته باشیم:

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

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

PeerDependencies: این نوع وابستگی‌ها برای معرفی بسته‌هایی استفاده می‌شوند که در پلاگین‌هایی که استفاده می‌کنید تاثیر دارند. ممکن است پلاگینی نیاز به استفاده‌ی از یک بسته را دارد، ولی آن را در کد، Require نکرده باشد (در مورد Require بعدا صحبت می‌کنیم). ولی برای اجرا نیاز به این بسته دارد. به همین دلیل از نسخه‌ی 3 به بعد، به شما هشدار میدهد که این بسته‌ها را نیز لحاظ کنید (تا نسخه‌ی npm2 به طور خودکار نصب می‌شد). همچنین نسخه بندی این وابستگی‌ها را نیز در نظر می‌گیرد. این حالت را می‌توانید مانند پلاگین‌های جی‌کوئری تصور کنید که نیاز است قبل از آن‌ها، کتابخانه‌ی جی‌کوئری صدا زده شود؛ در صورتی که در خود پلاگین، جی کوئری صدا زده نشده است.

ویرایشگر اتم

قبل از اینکه بخواهیم کدنویسی با هر زبانی را آغاز کنیم، عموما یک ادیتور مناسب را برای کارمان بر می‌گزینیم. الکترون نیازی به ادیتور خاصی ندارد و از Notepad گرفته تا هر ادیتور قدرتمند دیگری را می‌توانید استفاده کنید. ولی ادیتور اتم Atom که توسط خود الکترون هم تولید شده است، برای استفاده رایج است. ویژوال استودیو هم در این زمینه بسیار خوب و قدرتمند ظاهر شده است و حاوی Intellisense هوشمندی است.
 
این ادیتور که با ظاهری جذاب، توسط تیم گیت هاب تولید شده است، یک ویرایشگر متن باز با قابلیت توسعه و تغییر پذیری بالاست و از بسته‌های Node.js پشتیانی میکند و به صورت داخلی مجهز به سیستم گیت می‌باشد. بیشتر فناوری‌های استفاده شده در این ویرایشگر، رایگان بوده و دارای جامعه‌ی بزرگ متن باز می‌باشند. از فناوری‌های مورد استفاده‌ی آن می‌توان به الکترون، CoffeScript ، Node.js ,LESS و ... اشاره کرد. شعار سازندگان این ادیتور «یک ویرایشگر قابل هک برای قرن 21» می‌باشد.
برای پشتیبانی از زبان‌های مختلف، حاوی تعدادی زیادی پلاگین پیش فرض است مانند روبی ، سی شارپ، PHP ,Git,Perl,C/C++, Go,Objective-C,YAML و ...

آغاز کدنویسی
بگذارید کدنویسی را شروع کنیم. اگر اتم را نصب کرده باشید، می‌توانید با وارد کردن عبارت زیر، پروژه خود را در ادیتور باز کنید:
atom .
نماد "." به معنی دایرکتوری جاری است و به ادیتور اتم می‌گوید که این دایرکتوری را به عنوان یک پروژه، باز کن. بعد از باز شدن می‌توانید ساختار دایرکتوری و فایل‌ها را در سمت چپ ببینید. فایل package.json را باز کنید و به شکل زیر، آن را تغییر دهید:
{
  "name": "electron",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "electron ."
  },
  "author": "",
  "license": "ISC"
}
این تغییر، شامل حذف خصوصیت test و افزودن خصوصیت start به بخش scripts است. مقدار خصوصیت start را برابر . electron بگذارید تا موقع اجرا و تست پروژه، الکترون، پروژه‌ی موجود را در دایرکتوری جاری که فایل package.json در آن قرار دارد، اجرا کند. در بخش Main، نام فایل آغازین را نوشته است که باید آن را بسازید ( اگر این خصوصیت وجود هم نداشته باشد به طور پیش به این مقدار تنظیم شده است). به همین علت New File را اجرا کنید تا فایل Index.js را بسازید:
const electron = require('electron');
const {app} = electron;
const {BrowserWindow} = electron;
سه خط بالا را می‌نویسیم ، اولین خط نیاز ما را به کتابخانه و شیء الکترون فراهم می‌کند و بعد از آن، از الکترون درخواست دو شیء را به نام‌های app و BrowserWindow، می‌کنیم. شیء app مسئول چرخه‌ی زندگی اپلیکیشن است و موارد مربوط به آن را کنترل می‌کند؛ مثل رویدادهایی که ممکن است در برنامه رخ بدهند که در جلوتر با یکی از این رویدادهای آشنا می‌شویم. شیء BrowserWindow می‌تواند برای شما یک نمونه پنجره‌ی جدید را ایجاد کند و بتوانید آن پنجره را از این طریق مدیریت کنید.
let win;
app.on('ready', function() {
  // Create the browser window.
  win = new BrowserWindow({
           width: 800,
           height: 600
     });
});
در قسمت بعدی برای رویداد ready، یعنی زمانیکه الکترون آماده سازی‌ها را انجام داده است و برنامه آماده بارگذاری است، متدی را تعریف می‌کنیم که در آن یک پنجره‌ی با پهنای 800 پیکسل در 600 پیکسل، می‌سازد.این پنجره، پنجره‌ی اصلی شماست. کلمه‌ی کلید let و const را که می‌بینید، جز قوانین جدید Ecma Script هستند که در سایت جاری قبلا به آن‌ها پرداخته شده است و از تکرار آن خودداری می‌کنیم. دلیل اینکه متغیر win را به صورت عمومی تعریف کردیم این است که این احتمال زیاد می‌رود بعدا با اجرای سیستم Garbage در جاوااسکریپت، پنجره به طور خودکار بسته شود.
اکنون در کنسول می‌نویسیم:
npm start
حال npm با بررسی خصوصیت start در فایل package.json دستور . electron را اجرا خواهد کرد و برنامه‌ی ما، یک پنجره‌ی خالی را نمایش خواهد داد.
برای اینکه اولین برنامه واقعا خالی نباشد و ظاهری به آن بدهیم، یک فایل html می‌سازیم و در callback رویداد ready، بعد از ساخت پنجره آن را صدا می‌زنیم:
win.loadURL(`file://${__dirname}/index.html`);
با متد loadURL به راحتی می‌توانید یک صفحه‌ی وب را از شبکه و یا از روی سیستم، بخوانید. در بخش آرگومان، از پروتکل فایل استفاده شده است تا به آن بگوییم فایل مورد نظر روی سیستم جاری است. عبارت بعدی که به صورت template string تعریف شده‌است، حاوی مسیر index.js یا همان startup path است و سپس فایل index.html معرفی شده‌است. مجددا برنامه را اجرا کنید تا فایل index.html خود را داخل آن ببینید.
نظرات مطالب
سفارشی سازی ASP.NET Core Identity - قسمت اول - موجودیت‌های پایه و DbContext برنامه
- نکات مطلب «اعتبارسنجی مبتنی بر JWT در ASP.NET Core 2.0 بدون استفاده از سیستم Identity» را با این پروژه یکی کنید (قسمت تولید توکن و قسمت تولید Claims آن).
ضمنا در ASP.NET Core، بخش MVC و Web API یکی هستند. یعنی زمانیکه کاربر لاگین کرد، در یک کنترلر هم می‌توان متدهای Web API را در اختیار او قرار داد و هم MVC را. بنابراین الزامی به استفاده‌ی از JWT برای کار با Web API آن وجود ندارد. JWT فقط یک روش اعتبارسنجی دیگر است. کلاینت‌ها در حین کار با JWT باید اکسس‌توکن را به سمت سرور ارسال کنند؛ در حین کار با کوکی‌ها، کوکی تولید شده‌ی پس از لاگین را. مابقی آن یکی است. یعنی این پروژه الزاما نیازی به تلفیق با JWT ندارد. کوکی‌ها، هم در برنامه‌های MVC و هم در کلاینت‌های غیر MVC قابل استفاده هستند.
- مربوط است به استفاده از تامین کننده‌های لاگین خارجی مانند گوگل، توئیتر و غیره.
مطالب دوره‌ها
تغییر ترتیب آیتم‌های یک لیست به کمک افزونه jquery.sortable در ASP.NET MVC
در این مطلب قصد داریم ترتیب عناصر نمایش داده شده توسط یک لیست را به کمک افزونه بسیار سبک وزن jquery.sortable تغییر داده و نتایج را در سمت سرور مدیریت کنیم. این افزونه بر اساس امکانات کشیدن و رها ساختن HTML5 کار می‌کند و با مرورگرهای IE8 به بعد سازگار است.

مدل‌های برنامه

using System.Collections.Generic;

namespace jQueryMvcSample05.Models
{
    public class Survey
    {
        public int Id { set; get; }
        public string Title { set; get; }

        public virtual ICollection<SurveyItem> SurveyItems { set; get; }
    }
}

namespace jQueryMvcSample05.Models
{
    public class SurveyItem
    {
        public int Id { set; get; }
        public string Title { set; get; }
        public int Order { set; get; }

        //[ForeignKey("SurveyId")]
        public virtual Survey Survey { set; get; }
        public int SurveyId { set; get; }
    }
}
به کمک این ساختار قصد داریم اطلاعات یک سیستم نظر سنجی را نمایش دهیم.
تعدادی نظر سنجی به همراه گزینه‌های آن‌ها تعریف خواهند شد (یک رابطه one-to-many است). سپس توسط افزونه sortable می‌خواهیم ترتیب قرارگیری گزینه‌های آن‌را مشخص کنیم یا تغییر دهیم.


منبع داده فرضی برنامه


using System.Collections.Generic;
using jQueryMvcSample05.Models;

namespace jQueryMvcSample05.DataSource
{
    /// <summary>
    /// یک منبع داده فرضی جهت دموی ساده‌تر برنامه
    /// </summary>
    public static class SurveysDataSource
    {
        private static IList<Survey> _surveysCache;
        static SurveysDataSource()
        {
            _surveysCache = createSurveys();
        }

        public static IList<Survey> SystemSurveys
        {
            get { return _surveysCache; }
        }

        private static IList<Survey> createSurveys()
        {
            var results = new List<Survey>();
            for (int i = 1; i < 6; i++)
            {
                results.Add(new Survey
                {
                    Id = i,
                    Title = "نظر سنجی " + i,
                    SurveyItems = new List<SurveyItem>
                    {
                       new SurveyItem{ Id = 1, SurveyId = i, Title = "گزینه 1", Order = 1 },
                       new SurveyItem{ Id = 2, SurveyId = i, Title = "گزینه 2", Order = 2 },
                       new SurveyItem{ Id = 3, SurveyId = i, Title = "گزینه 3", Order = 3 },
                       new SurveyItem{ Id = 4, SurveyId = i, Title = "گزینه 4", Order = 4 }
                    }
                });
            }
            return results;
        }
    }
}
در اینجا نیز از یک منبع داده فرضی تشکیل شده در حافظه جهت سهولت دموی برنامه استفاده خواهد شد.


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

using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Web.UI;
using jQueryMvcSample05.DataSource;
using jQueryMvcSample05.Security;

namespace jQueryMvcSample05.Controllers
{
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            var surveysList = SurveysDataSource.SystemSurveys;
            return View(surveysList);
        }

        [HttpPost]
        [AjaxOnly]
        [OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
        public ActionResult SortItems(int? surveyId, string[] items)
        {
            if (items == null || items.Length == 0 || surveyId == null)
                return Content("nok");

            updateSurvey(surveyId, items);

            return Content("ok");
        }

        /// <summary>
        /// این متد جهت آشنایی با پروسه به روز رسانی ترتیب گزینه‌ها در اینجا قرار گرفته است
        /// بدیهی است محل قرارگیری آن باید در لایه سرویس برنامه اصلی باشد
        /// </summary>
        private static void updateSurvey(int? surveyId, string[] items)
        {
            var itemIds = new List<int>();
            foreach (var item in items)
            {
                itemIds.Add(int.Parse(item.Replace("item-row-", string.Empty)));
            }

            var survey = SurveysDataSource.SystemSurveys.FirstOrDefault(x => x.Id == surveyId.Value);
            if (survey == null)
                return;

            int order = 0;
            foreach (var itemId in itemIds)
            {
                order++;
                var surveyItem = survey.SurveyItems.FirstOrDefault(x => x.Id == itemId);
                if (surveyItem == null) continue;
                surveyItem.Order = order;
            }

            //todo: call save changes ....
        }
    }
}

و کدهای View متناظر

@model IList<jQueryMvcSample05.Models.Survey>
@{
    ViewBag.Title = "Index";
    var sortUrl = Url.Action(actionName: "SortItems", controllerName: "Home");
}
<h2>
    نظر سنجی‌ها</h2>
@foreach (var survey in Model)
{
    <fieldset>
        <legend>@survey.Title</legend>
        <div id="sortable-@survey.Id">
            @foreach (var surveyItem in survey.SurveyItems.OrderBy(x => x.Order))
            {            
                <div id="item-row-@surveyItem.Id">
                    <span class="handles">::</span>
                    @surveyItem.Title
                </div>
            }
        </div>
    </fieldset>
}
<div>
    لطفا برای تغییر ترتیب آیتم‌های تعریف شده، از امکان کشیدن و رها کردن تعریف شده بر
    روی آیکون‌های :: در کنار هر آیتم استفاده نمائید.
</div>
@section JavaScript
{
    <script type="text/javascript">
        $(document).ready(function () {
            $('div[id^="sortable"]').sortable({ handle: 'span' }).bind('sortupdate', function (e, ui) {
                var sortableItemId = $(ui.item).parent().attr('id');
                var surveyId = sortableItemId.replace('sortable-', '');
                var items = [];
                $('#' + sortableItemId + ' div').each(function () {
                    items.push($(this).attr('id'));
                });
                //alert(items.join('&'));                
                $.ajax({
                    type: "POST",
                    url: "@sortUrl",
                    data: JSON.stringify({ items: items, surveyId: surveyId }),
                    contentType: "application/json; charset=utf-8",
                    dataType: "json",
                    complete: function (xhr, status) {
                        var data = xhr.responseText;
                        if (xhr.status == 403) {
                            window.location = "/login";
                        } else if (status === 'error' || !data || data == "nok") {
                            alert('خطایی رخ داده است');
                        }
                        else {
                            alert('انجام شد');
                        }
                    }
                });
            });
        });
    </script>
}
توضیحات

در اینجا نیاز بود تا ابتدا کدهای کنترلر و View ارائه شوند، تا بتوان در مورد ارتباطات بین آن‌ها بهتر بحث کرد.
در ابتدای نمایش صفحه Home، رکوردهای نظرسنجی‌ها از منبع داده دریافت شده و به View ارسال می‌شوند. در View برنامه یک حلقه تشکیل گردیده و این موارد رندر خواهند شد.
هر نظر سنجی با یک div بیرونی که با id مساوی sortable شروع می‌شود، آغاز گردیده و گزینه‌های آن نظر سنجی نیز توسط divهایی با id مساوی item-row شروع خواهند گردید. هر کدام از این idها حاوی id رکوردهای متناظر هستند. از این id‌ها در کدهای برنامه جهت یافتن یک نظر سنجی یا یک ردیف مشخص برای به روز رسانی ترتیب آن‌ها استفاده خواهیم کرد.
ادامه کار، به تنظیمات و اعمال افزونه sortable مرتبط می‌شود. توسط تنظیم ذیل به jQuery اعلام خواهیم کرد، هرجایی یک div با id شروع شده با sortable یافتی، افزونه sortable را به آن متصل کن:
 $('div[id^="sortable"]').sortable
در ادامه در ناحیه و  div ایی که عمل کشیدن و رها شدن رخ داده، id این div را بدست آورده و سپس کلیه row-itemهای آن را در آرایه‌ای به نام items قرار می‌دهیم:
 var sortableItemId = $(ui.item).parent().attr('id');
var surveyId = sortableItemId.replace('sortable-', '');
var items = [];
$('#' + sortableItemId + ' div').each(function () {
  items.push($(this).attr('id'));
});
اکنون که به id یک نظر سنجی و همچنین idهای ردیف‌های مرتب شده حاصل دسترسی داریم، آن‌ها را توسط jQuery Ajax به کنترلر برنامه ارسال می‌کنیم:
 data: JSON.stringify({ items: items, surveyId: surveyId })
امضای اکشن متد SortItems نیز دقیقا بر همین مبنا تنظیم شده است:
 public ActionResult SortItems(int? surveyId, string[] items)

اطلاعاتی که در اینجا دریافت می‌شوند در متد updateSurvey مورد استفاده قرار خواهند گرفت. بر اساس surveyId دریافتی، نظرسنجی مرتبط را یافته و سپس به گزینه‌های آن دست خواهیم یافت. اکنون نوبت به پردازش آرایه items دریافت شده است. این آرایه بر اساس انتخاب کاربر مرتب شده است.


دریافت کدها و پروژه کامل این قسمت
jQueryMvcSample05.zip
 
مطالب
کار با SignalR Core از طریق یک کلاینت Angular
نگارش AspNetCore.SignalR 1.0.0-alpha1-final چند روزی هست که منتشر شده‌است. در این مطلب قصد داریم یک برنامه‌ی وب ASP.NET Core 2.0 را به همراه یک Hub ایجاد کرده و سپس این Hub را در یک کلاینت Angular (2+) مورد استفاده قرار دهیم.


پیشنیازها

برای دنبال کردن این مثال فرض بر این است که NET Core 2.0 SDK. و همچنین Angular CLI را نیز پیشتر نصب کرده‌اید. مابقی بحث توسط خط فرمان و ابزارهای dotnet cli و angular cli ادامه داده خواهند شد و الزامی به نصب هیچگونه IDE نیست و این مثال تنها توسط VSCode پیگیری شده‌است.


تدارک ساختار ابتدایی مثال جاری


ساخت برنامه‌ی وب، توسط dotnet cli
ابتدا یک پوشه‌ی جدید را به نام SignalRCore2Sample ایجاد می‌کنیم. سپس داخل این پوشه، پوشه‌ی دیگری را به نام SignalRCore2WebApp ایجاد خواهیم کرد (تصویر فوق). از طریق خط فرمان به این پوشه وارد شده (در ویندوز، در نوار آدرس، دستور cmd.exe را تایپ و enter کنید) و سپس فرمان ذیل را صادر می‌کنیم:
 dotnet new mvc
این دستور، یک برنامه‌ی جدید ASP.NET Core 2.0 را تولید خواهد کرد.

ساخت برنامه‌ی کلاینت، توسط angular cli
سپس از طریق خط فرمان به پوشه‌ی SignalRCore2Sample بازگشته و دستور ذیل را صادر می‌کنیم:
 ng new SignalRCore2Client
این دستور، یک برنامه‌ی Angular را در پوشه‌ی SignalRCore2Client تولید می‌کند (تصویر فوق).

اکنون که در پوشه‌ی ریشه‌ی SignalRCore2Sample قرار داریم، اگر در خط فرمان، دستور . code را صادر کنیم، VSCode هر دو پوشه‌ی وب و client را با هم در اختیار ما قرار می‌دهد:


تکمیل پیشنیازهای برنامه‌ی وب

پس از ایجاد ساختار اولیه‌ی برنامه‌های وب ASP.NET Core و کلاینت Angular، اکنون نیاز است وابستگی جدید AspNetCore.SignalR را به آن معرفی کنیم. به همین جهت به فایل SignalRCore2WebApp.csproj مراجعه کرده و تغییرات ذیل را به آن اعمال می‌کنیم:
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.0.0-alpha1-final" />
  </ItemGroup>
  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
    <DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="2.0.0" />
  </ItemGroup>
</Project>
در اینجا ابتدا بسته‌ی Microsoft.AspNetCore.SignalR اضافه شده‌است. همچنین Microsoft.DotNet.Watcher.Tools را نیز اضافه کرده‌ایم تا بتوان از مزیت build تدریجی پروژه، به ازای هر تغییر صورت گرفته، استفاده کنیم.
پس از این تغییرات، دستور ذیل را در خط فرمان صادر می‌کنیم تا وابستگی‌های پروژه نصب شوند:
 dotnet restore
البته اگر افزونه‌ی #C مخصوص VSCode را نصب کرده باشید، تغییرات فایل csproj را دنبال کرده و پیام restore را نیز ظاهر می‌کند؛ تا همین دستور فوق را به صورت خودکار اجرا کند.
یک نکته: نگارش فعلی افزونه‌ی #C مخصوص VSCode، با تغییر فایل csproj و restore وابستگی‌های آن نیاز دارد یکبار آن‌را بسته و سپس مجددا اجرا کنید، تا اطلاعات intellisense خود را به روز رسانی کند. بنابراین اگر VSCode بلافاصله کلاس‌های مرتبط با بسته‌های جدید را تشخیص نمی‌دهد، علت صرفا این موضوع است.

پس از بازیابی وابستگی‌ها، به ریشه‌ی پروژه‌ی برنامه‌ی وب وارد شده و دستور ذیل را صادر کنید:
 dotnet watch run
این دستور، پروژه را build کرده و سپس بر روی پورت 5000 ارائه می‌دهد. همچنین به ازای هر تغییری در فایل‌های کدهای برنامه، به صورت خودکار برنامه را build کرده و مجددا ارائه می‌دهد.


تکمیل برنامه‌ی وب جهت ارسال پیام‌هایی به کلاینت‌های متصل به آن

پس از افزودن وابستگی‌های مورد نیاز، بازیابی و build برنامه، اکنون نوبت به تعریف یک Hub است، تا از طریق آن بتوان پیام‌هایی را به کلاینت‌های متصل ارسال کرد. به همین جهت یک پوشه‌ی جدید را به نام Hubs به پروژه‌ی وب افزوده و سپس کلاس جدید MessageHub را به صورت ذیل به آن اضافه می‌کنیم:
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;

namespace SignalRCore2WebApp.Hubs
{
    public class MessageHub : Hub
    {
        public Task Send(string message)
        {
            return Clients.All.InvokeAsync("Send", message);
        }
    }
}
این کلاس از کلاس پایه Hub مشتق می‌شود. سپس در متد Send آن می‌توان پیام‌هایی را به کلاینت‌های متصل به برنامه ارسال کرد.

پس از تعریف این Hub، نیاز است به کلاس Startup مراجعه کرده و دو تغییر ذیل را اعمال کنیم:
الف) ثبت و معرفی سرویس SignalR
ابتدا باید SignalR را فعالسازی کرد. به همین جهت نیاز است سرویس‌های آن‌را به صورت یکجا توسط متد الحاقی AddSignalR در متد ConfigureServices به نحو ذیل معرفی کرد:
public void ConfigureServices(IServiceCollection services)
{
   services.AddSignalR();
   services.AddMvc();
}

ب) ثبت مسیریابی دسترسی به Hub
پس از تعریف Hub، مرحله‌ی بعدی، مشخص سازی نحوه‌ی دسترسی به آن است. به همین جهت در متد Configure، به نحو ذیل Hub را معرفی کرده و سپس یک path را برای آن مشخص می‌کنیم:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   app.UseSignalR(routes =>
   {
      routes.MapHub<MessageHub>(path: "message");
    });
یعنی اکنون این Hub در آدرس ذیل قابل دسترسی است:
  http://localhost:5000/message
این آدرسی است که در کلاینت Angular، از آن برای اتصال به هاب، استفاده خواهیم کرد.


انتشار پیام‌هایی به تمام کاربران متصل به برنامه

آدرس فوق به تنهایی کار خاصی را انجام نمی‌دهد. از آن جهت اتصال کلاینت‌های برنامه استفاده می‌شود و این کلاینت‌ها پیام‌های رسیده‌ی از طرف برنامه را از این آدرس دریافت خواهند کرد. بنابراین مرحله‌ی بعد، ارسال تعدادی پیام به سمت کلاینت‌ها است. برای این منظور به HomeController برنامه‌ی وب مراجعه کرده و آن‌را به نحو ذیل تغییر می‌دهیم:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using SignalRCore2WebApp.Hubs;

namespace SignalRCore2WebApp.Controllers
{
    public class HomeController : Controller
    {
        private readonly IHubContext<MessageHub> _messageHubContext;
        public HomeController(IHubContext<MessageHub> messageHubContext)
        {
            _messageHubContext = messageHubContext;
        }

        public IActionResult Index()
        {
            return View(); // show the view
        }

        [HttpPost]
        public async Task<IActionResult> Index(string message)
        {
            await _messageHubContext.Clients.All.InvokeAsync("Send", message);
            return View();
        }
    }
}
برای دسترسی به Hubهای تعریف شده می‌توان از سیستم تزریق وابستگی‌ها استفاده کرد. برای این منظور تنها کافی است Hub مدنظر را به عنوان آرگومان جنریک IHubContext تعریف کرد. سپس از طریق آن می‌توان به این context‌، در قسمت‌های مختلف برنامه دسترسی یافت و برای مثال پیام‌هایی را به کاربران ارائه داد.
در این مثال ابتدا View ذیل نمایش داده می‌شود:
@{
    ViewData["Title"] = "Home Page";
}

<form method="post"
      asp-action="Index"
      asp-controller="Home"
      role="form">
  <div class="form-group">
     <label label-for="message">Message: </label>
     <input id="message" name="message" class="form-control"/>
  </div>
  <button class="btn btn-primary" type="submit">Send</button>
</form>
کار آن فرستادن یک پیام به متد Index است. سپس این متد، به کمک context تزریق شده‌ی Hub پیام‌ها، این پیام را به تمام کلاینت‌های متصل ارسال می‌کند.


تکمیل برنامه‌ی کلاینت Angular جهت نمایش پیام‌های رسیده‌ی از طرف سرور

تا اینجا ساختار ابتدایی برنامه‌ی Angular را توسط Angular CLI ایجاد کردیم. اکنون نیاز است وابستگی سمت کلاینت SignalR Core را نصب کنیم. به همین جهت از طریق خط فرمان به پوشه‌ی SignalRCore2Client وارد شده و دستور ذیل را صادر کنید:
 npm install @aspnet/signalr-client --save
پرچم save آن سبب خواهد شد تا این وابستگی علاوه بر نصب، در فایل package.json نیز درج شود.
کلاینت رسمی signalr، هم جاوا اسکریپتی است و هم تایپ‌اسکریپتی. به همین جهت به سادگی توسط یک برنامه‌ی تایپ اسکریپتی Angular قابل استفاده است. کلاس‌های آن‌را در مسیر node_modules\@aspnet\signalr-client\dist\src می‌توانید مشاهده کنید.
در ابتدا، فایل app.component.ts را به نحو ذیل تغییر می‌دهیم:
import { Component, OnInit } from "@angular/core";
import { HubConnection } from "@aspnet/signalr-client";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
  hubPath = "http://localhost:5000/message";
  messages: string[] = [];

  ngOnInit(): void {
    const connection = new HubConnection(this.hubPath);
    connection.on("send", data => {
      this.messages.push(data);
    });
    connection.start().then(() => {
      // connection.invoke("send", "Hello");
      console.log("connected.");
    });
  }
}
در اینجا در ابتدا، کلاس HubConnection از ماژول aspnet/signalr-client@ دریافت شده‌است. سپس بر این اساس در ngOnInit، یک وهله از آن که به مسیر Hub تعریف شده‌ی برنامه اشاره می‌کند، ایجاد خواهد شد. هر زمانیکه پیامی از سمت سرور دریافت گردید، این پیام را به لیست messages، که یک آرایه است اضافه می‌کنیم. در آخر برای راه اندازی این اتصال، متد start آ‌ن‌را فراخوانی خواهیم کرد. در اینجا می‌توان یک متد سمت سرور را فراخوانی کرد و یا برقراری اتصال را در کنسول developers مرورگر نمایش داد.
آرایه‌ی messages را به نحو ذیل توسط یک حلقه در قالب این کامپوننت نمایش خواهیم داد:
<div>
  <h1>
    The messages from the server:
  </h1>
  <ul>
    <li *ngFor="let message of messages">
      {{message}}
    </li>
  </ul>
</div>
پس از آن به ریشه‌ی پروژه‌ی کلاینت مراجعه کرده و دستور ذیل را صادر می‌کنیم تا برنامه‌ی Angular ساخته شده و در مرورگر پیش فرض سیستم نمایش داده شود:
  ng serve -o
در این حالت برنامه در آدرس  http://localhost:4200/ قابل دسترسی خواهد بود.


همانطور که مشاهده می‌کنید، پیام خطای ذیل را صادر کرده‌است:
 Failed to load http://localhost:5000/message: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.
علت اینجا است که برنامه‌ی Angular بر روی پورت 4200 کار می‌کند و برنامه‌ی وب ما بر روی پورت 5000 تنظیم شده‌است. به همین جهت نیاز است CORS را در برنامه‌ی وب تنظیم کرد تا امکان یک چنین دسترسی صادر شود.
برای این منظور به فایل آغازین برنامه‌ی وب مراجعه کرده و سرویس‌های AddCors را به مجموعه‌ی سرویس‌های برنامه اضافه می‌کنیم:
public void ConfigureServices(IServiceCollection services)
{
    services.AddSignalR();
    services.AddCors(options =>
            {
                options.AddPolicy("CorsPolicy",
                    builder => builder
                        .AllowAnyOrigin()
                        .AllowAnyMethod()
                        .AllowAnyHeader()
                        .AllowCredentials());
            });
    services.AddMvc();
}
پس از آن در متد Configure، این سیاست دسترسی باید مورد استفاده قرار گیرد؛ و گرنه این تنظیمات کار نخواهد کرد. محل قرارگیری آن نیز باید پیش از سایر تنظیمات باشد:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   app.UseCors(policyName: "CorsPolicy");
اکنون اگر مجددا برنامه‌ی Angular را Refresh کنیم، در console توسعه دهندگان مرورگر، مشاهده خواهیم کرد که اتصال برقرار شده‌است:


در آخر برای آزمایش برنامه، به آدرس http://localhost:5000 یا همان برنامه‌ی وب، مراجعه کرده و پیامی را ارسال کنید. بلافاصله مشاهده خواهید کرد که این پیام توسط کلاینت Angular دریافت شده و نمایش داده می‌شود:



کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید: SignalRCore2Sample.zip
برای اجرا آن، ابتدا به پوشه‌ی SignalRCore2WebApp مراجعه کرده و دو فایل bat آن‌را به ترتیب اجرا کنید. اولی وابستگی‌ها‌ی برنامه را بازیابی می‌کند و دومی برنامه را بر روی پورت 5000 ارائه می‌دهد.
سپس به پوشه‌ی SignalRCore2Client مراجعه کرده و در آنجا نیز دو فایل bat ابتدایی آن‌را به ترتیب اجرا کنید. اولی وابستگی‌های برنامه‌ی Angular را بازیابی می‌کند و دومی برنامه‌ی Angular را بر روی پورت 4200 اجرا خواهد کرد.
اشتراک‌ها
Server-side processing با DataTable در ASP.NET Core

 In this article, we will learn how to use JQuery Datatable in ASP.NET Core with Server Side Processing. We will also be building a simple real-world implementation to help understand JQuery Datatable to it’s fullest. You can find the source code of the entire implementation here. Let’s begin 

Server-side processing با DataTable در ASP.NET Core
اشتراک‌ها
دریافت کتاب ساخت برنامه های تجاری با WPF و الگوی MVVM

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

در این کتاب نه تنها با پترن MVVM آشنا می‌شوید بلکه در راه رسیدن به این موضوع نویسنده شما را با انواع وجه‌های مختلف در ساخت یک برنامه‌ی تجاری آشنا و آماده خواهد کرد! 

دریافت کتاب ساخت برنامه های تجاری با WPF و الگوی MVVM
نظرات مطالب
Owin چیست ؟ قسمت اول
بله، به همین معنی است
البته دقت کنید، پیاده سازی OWIN کار ساده ای نیست، و به سرعت نمی‌توان شاهد پیاده سازی آن بر روی هاست‌های مختلف بود، و این پروسه با سرعت فعلی از نظر من مدتی طول خواهد کشید.
برای مثال Katana که یک پیاده سازی قابل استفاده و خوب از آن به شمار می‌رود کار شرکت مایکروسافت است و سایر پیاده سازی Open Source سایر تیم‌ها که بالطبع امکان مانور شرکت مایکروسافت را ندارند، کمی طول می‌کشد تا واقعا آماده استفاده شود.
و همچنین پیاده سازی‌های فعلی در قسمت هایی مانند Web Socket‌ها و سایر مسائل پیچیده دارای ضعف هایی هستند.
درست مانند استاندارد HTML 5 که بر روی مرورگرهای مختلف به میزان‌های مختلفی پیاده سازی شده است.
به بیان دیگر پیاده سازی OWIN صفر و صدی نیست، بلکه هر پیاده سازی ممکن است در داخل خود دارای ضعف‌ها و یا نواقصی باشد.
علاوه بر این اگر شما در کد نویسی ASP.NET MVC خود، بی جهت به امکانات پایه ASP.NET ایجاد وابستگی کنید، نیز در این عمل دچار مشکل خواهید شد، برای همین بدیهتا کاری را که می‌توانید با Action Filter انجام دهید را نباید با یک Http Module انجام دهید و ...
مهم‌ترین کار طراحی برنامه هایی که می‌نویسید به صورت سازگار با OWIN است که در پست‌های بعدی قرار است به همین قسم از مطالب بپردازیم
البته من آینده خوبی برای OWIN قائلم، و نفع آن در کوتاه مدت و بلند مدت کاملا آشکار و واضح است، کما این که در مطلب به آن اشاره شد.
برای مشاهده پیاده سازی‌های مختلف OWIN می‌توانید به سایت owin.org مراجعه کنید.
موفق و پایدار باشید
اشتراک‌ها
کتابخانه jquery-typeahead
The jQuery Typeahead plugin provides autocomplete preview on search inputs similar to google search with builtin options and deep customization. It is a simple clientside library that will improve the user experience on your website search input!  Demo
کتابخانه jquery-typeahead
نظرات مطالب
بررسی سایز و پسوند فایل آپلود شده قبل از ارسال به سرور
سلام 
ببخشید قسمت دوم که نمایش سایز فایل است را من هر کاری کردم نتونستم انجام بدم.
یه HTML ساده ساختم و کدهای شما رو روش قرار دادم ولی هیچ اتفاقی نیافتاد.
البته اینم بگم که رو سایت jsfiddle  جواب میده و مشکلی نیست ، می‌خواهم بدونم که به جز Library jQuery آیا چیز دیگری باید اضافه کنم ؟
ممنون میشم اگه فایل تستی هم قرار بدید