نظرات مطالب
AngularJS #4
 http.get به صورت async اجرا میشه و نه synchronous. یعنی زمانیکه فراخوانی شد، سطر return بعدی اجرا میشه و صبر نمی‌کنه تا به اینجا برسه. بهتره از promises استفاده کنید (راه حل استانداردش): ^ و ^
مطالب
معرفی و روش استفاده از Dispatcher در WPF
باید این سوال را از خودمان بپرسیم که اصلا چه نیازی به استفاده از Dispatcher در WPF می‌باشد و این که ما چه نیازی داریم با ساختمان Thread آشنا شویم؟

می‌دانید که در پروژه و نرم افزارهایی که توسعه داده می‌شوند بعضی مواقع مشاهده می‌کنیم قسمتی از برنامه نیاز به زمان یا پردازش بیشتری دارد تا عملیات خود را به اتمام برساند و رابط کاربری (User Interface) برنامه در این حین منتظر می‌ماند و یا به اصطلاح (Freeze) می‌شود تا یک پردازش طولانی به اتمام برسد و بعد رابط کاربری به کار خود ادامه می‌دهد و بعضی مواقع پنجره Windows explorer is not responding را مشاهده کرده‌اید که با کلیک بر روی Close the program از آن گذر می‌کنیم. در حالی که برنامه ما باید حالت Responsive  داشته باشد و نباید برنامه با یک‌چنین مواردی روبه رو شود.

ساختمان Thread :
تمامی اشیاء (Objects) در این مدل به دو گروه تقسیم بندی می‌شوند:
Single-Threaded Apartment
Multi-Threaded Apartment
ساختمان  Single-Threaded Apartment (STA) :

STA شامل فقط یک Thread می‌باشد و تمامی اشیاء در این ساختمان فقط می‌توانند متدی را که در این Thread صدا زده می‌شود دریافت کنند؛ یا به عبارتی دیگر هنگامیکه شیء به Thread ای متصل می‌شود دیگر شما قادر نخواهید بود اشیاء UI را به صورت مستقیم و یا از طریق Thread‌های دیگر تغییر دهید. لازم به ذکر است WPF از مدل STA پشتیبانی می‌کند که شامل نکات  زیر می‌باشد:
1. یک Thread در کل برنامه اجرا می‌شود و شامل همه Object‌های WPF می‌باشد.
2. عناصر یا المان‌های  WPF، قابلیت تنظیم Thread Affinity را دارند. منظور این می‌باشدکه Thread‌ها نمی‌توانند با یکدیگر ارتباط  برقرار کنند.
3. اشیاء WPF که قابلیت Thread Affinityرا دارند، از یک Object Dispatcher مشتق می‌شوند.
4. اشیاء WPF متعلق به Thread ای می‌باشند که توسط آن ایجاد شده است و Thread دیگر قادر به دسترسی مستقیم به این اشیاء نمی‌باشد.

ساختمان Multi-Threaded Apartment (MTA) :

MTA  شامل یک یا چندین Thread می‌باشد. تمامی اشیاء در این مدل می‌توانند از هر Thread ای فراخوانی شوند. در حقیقت رجیستر‌های CPU و Ram که در اختیار برنامه قرار داده شده تکه تکه می‌شوند و برنامه ما تعداد Thread‌های مورد نظر را اجرا می‌کند و یکی از راه حل‌های بر طرف کردن اینکه رابط کاربری ما در حالت انتظار باقی نماند استفاده از پردازش کارها بصورت غیر همزمان (Asynchronous) می‌باشد که در اصطلاح Multithreading نامیده می‌شود. 

WPF Dispatcher :

WPF از مدل STA پشتیبانی می‌کند. زمانیکه برنامه WPF  اجرا می‌شود، به طور خودکار یک Dispatcher Object ساخته می‌شود و متد Run صدا زده می‌شود و از آن برای آماده سازی صف پیام‌ها استفاده می‌شود که مدیریت یک صف از کارها بر عهده آن است و کارهای UI را در یک صف FIFO اجرا می‌کند. WPF یک Dispatcher را برای UI Thread ایجاد می‌کند. بنابراین شما نمی‌توانید یک dispatcher دیگر برای آن تعریف کنید. 

نکته : دیاگرام زیر نمایش می‌هد که تمامی اشیاء WPF از DispatcherObject مشتق شده‌اند.

نکته : زمانیکه برنامه WPF  اجرا می‌شود دو Thread ساخته می‌شود:
1.  UI Thread (Main Thread) 
2.  Render Thread
 

UI Thread : مسئولیت تمامی ورودی‌های کاربر، handle events, paints screen و اجرای کدهای برنامه را بر عهده دارد.

Render Thread : در Background اجرا می‌شود و برای Render صفحه نمایش WPF استفاده می‌شود.

نکته: همانطور که آشنا شدیم WPF نمی‌تواند UI Thread را از طریق یک Thread دیگر به روز رسانی کند و یا به عبارتی دیگر یک Thread نمی‌تواند بصورت مستقیم به اشیایی که توسط Thread دیگر ایجاد شده، دسترسی داشته باشد.

Dispatcher برای این کار دو متد را در اختیار ما قرار می‌دهد :

متد Invoke : یک Action یا Delegate را میگیرد و متد آن‌را به صورت همزمان اجرا می‌کند. این مورد به این معنا است که تا زمانی که اجرای متد کامل نگردد، عملیاتی صورت نخواهد گرفت و یا به عبارتی دیگر فراخوان را تا زمانیکه زمانبندی به پایان برسد، در حالت مسدود نگهداری خواهد کرد.

مثال :

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        Task.Factory.StartNew(() =>
            {
                InvokeMethodExample();
            });
    }
 
    private void InvokeMethodExample()
    {
        Thread.Sleep(2000);
        Dispatcher.Invoke(() =>
            {
                btn1.Content = "By Invoke";
            });
    }
}
متد BeginInvoke : یک Delegate را می‌گیرد و متد آن‌را به صورت ناهمزمان اجرا می‌کند. این مورد به این معنا است که قبل از آنکه متد فراخوانی گردد، برگشت داده می‌شود و یا به عبارتی دیگر به ما اجازه می‌دهد تا Thread جاری را با کنترل دوباره به جریان بیندازیم.
مثال :
public MainWindow()
{
    InitializeComponent();
    Task.Factory.StartNew(() =>
        {
            BeginInvokeExample();
        });
}
 
private void BeginInvokeExample()
{
    DispatcherOperation op = Dispatcher.BeginInvoke((Action)(() => {
         btn1.Content = "By BeginInvoke";
        }));
}
BeginInvoke یک شیء DispatcherOperation برگشت می‌دهد. این شیء یا Object برای دانستن اینکه وضعیت عملیات کامل شده است یا خیر می‌تواند استفاده شود و همچین دو رویداد Aborted و Completed را فراهم می‌کند. 

مطالب
CoffeeScript #12

بخش‌های بد

جاوااسکریپت یک زبان پیچیده است که شما برای کار با آن، نیاز است قسمت‌هایی را که باید از آن‌ها دوری کنید و قسمت‌های مهمی را که باید استفاده کنید، بشناسید. همانطور که Sun Tzu گفته "دشمن خود را بشناس"، ما نیز در این قسمت می‌خواهیم برای شناخت بیشتر قسمت‌های تاریک و روشن جاوااسکریپت به آن بپردازیم.

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

اول از قسمت‌هایی که توسط CoffeeScript حل شده‌اند شروع می‌کنیم.

A JavaScript Subset

with یک دستور بسیار زمانبر است و مضر شناخته شده است و نباید از آن استفاده کنید. with با ایجاد یک ساختار خلاصه نویسی، برای جستجو بر روی خصوصیات اشیاء در نظر گرفته شده بود. برای نمونه به جای نوشتن:

dataObj.users.vahid.email = "info@vmt.ir";
می‌توانید به این صورت این کار را انجام دهید:
with(dataObj.users.vahid) {
  email = "info@vmt.ir";
}
مفسر جاوااسکریپت دقیقا نمی‌داند که شما می‌خواهید چه کاری را با with انجام دهید، و به شیء مشخص شده فشار می‌آورد تا اول اسم همه مراجعه شده‌ها را جستجو کند. این عمل واقعا به عملکرد و کارآیی لطمه می‌زند. یعنی مترجم، تمام انواع بهینه سازی‌های JIT را خاموش می‌کند. همچنین پیشنهادهایی مبنی بر حذف کامل آن از نسخه‌های بعدی جاوااسکریپت نیز مطرح شده است.
همه چیز برای عدم استفاده از with در نظر گرفته شده است. CoffeeScript یک قدم جلوتر از همه برداشته و with را از syntax خود حذف کرده است. به عبارت دیگر در صورتیکه شما از آن استفاده کنید، کامپایلر CoffeeScript خطا صادر می‌کند.

Global variables

به طور پیش فرض تمامی برنامه‌های جاوااسکریپت در دامنه global اجرا می‌شوند و تمامی متغیرهایی که ساخته می‌شوند به طور پیش فرض در ناحیه‌ی global قرار می‌گیرند. اگر شما بخواهید متغیری را در ناحیه‌ی local ایجاد کنید، باید از کلمه کلیدی var استفاده کنید.

usersCount = 1;        // Global
var groupsCount = 2;   // Global

(function(){              
  pagesCount = 3;      // Global
  var postsCount = 4;  // Local
})()
اکثر اوقات شما می‌خواهید متغیر local ایی را ایجاد کنید و نه global. توسعه دهندگان باید همیشه به یاد داشته باشند که قبل از مقداردهی اولیه‌ی هر متغیری، کلمه‌ی کلیدی var را قرار دهند یا با انواع و اقسام مشکلات، هنگامی که متغیرها به طور تصادفی با یکدیگر برخورد و یا بازنویسی بر روی یکدیگر انجام می‌دهند، روبرو شوند.
خوشبختانه CoffeeScript به کمک شما می‌آید و به طور کامل انتساب متغیرهای global را به طور ضمنی از بین می‌برد. به عبارت دیگر کلمه کلیدی var در CoffeeScript رزرو شده است و در صورت استفاده خطا صادر می‌شود.
به صورت پیش فرض به طور ضمنی متغیرها local ایجاد می‌شوند و خیلی سخت می‌شود متغیر global ایی را بدون انتساب آن به عنوان خصوصیتی از شیء window ایجاد کرد.
outerScope = true
do ->
  innerScope = true
نتیجه‌ی کامپایل آن می‌شود:
var outerScope;
outerScope = true;
(function() {
  var innerScope;
  return innerScope = true;
})();
همانطور که مشاهده می‌کنید CoffeeScript مقداردهی اولیه متغیر را (با استفاده از var) به صورت خودکار در context ایی که برای اولین بار استفاده شده است انجام می‌دهد. باید مواظب باشید تا از نام متغیر خارجی مجددا استفاده نکنید که این اتفاق ممکن است در کلاس یا تابع با عمق زیاد ایجاد شود. برای مثال، در اینجا به صورت تصادفی متغیر package در یک تابع کلاس بازنویسی شده است:
package = require('./package')

class Test
  build: ->
    # Overwrites outer variable!
    package = @testPackage.compile()

  testPackage: ->
    package.create()
برای ایجاد متغیرهای global باید از انتساب آنها به عنوان خصوصیتی از شیء window استفاده کرد.
  class window.Asset
    constructor: ->
با تضمین متغیرهای global به صورت صریح و روشن به جای به طور ضمنی بودن آنها، CoffeeScript یکی از منابع اصلی ایجاد مشکلات در جاوااسکریپت را حذف کرد‌ه‌است.

Semicolons

جاوااسکریپت اجباری برای نوشتن ";" ندارد، بنابراین ممکن است یک سری از دستورات از قلم بیافتند. با این حال در پشت صحنه‌ی کامپایلر جاوااسکریپت به ";" احتیاج دارد. به طوری که parser جاوااسکریپت به صورت خودکار هر زمانی که نتواند ارزیابی از دستورات داشته باشد، یک بار دیگر با ";" این کار را انجام می‌دهد و درصورت موفقیت، پیام خطایی مبنی بر نبود ";" را صادر می‌کند.
متاسفانه این یک ایده بد است. چرا که ممکن است تغییر رفتاری در کد نوشته شده به وجود آید. به مثال زیر توجه کنید. به نظر کد نوشته شده صحیح است؛ درسته؟
function() {}
(window.options || {}).property
اشتباه است، حداقل با توجه به parser، یک خطای syntax صادر می‌شود. در مورد دوم نیز parser، ";" اضافه نمی‌کند و کد نوشته شده به کد یک خطی تبدیل می‌شوند.
function() {}(window.options || {}).property
حالا شما می‌توانید این موضوع را ببینید که چرا parser خطا داده‌است. وقتی شما در حال نوشتن کد جاوااسکریپتی هستید، باید بعد از هر دستور از ";" استفاده کنید. خوشبختانه در تمام زمانیکه درحال نوشتن کد CoffeeScript هستید، نیازی به نوشتن ";" ندارید. در زمانیکه کد CoffeeScript نوشته شده کامپایل می‌شود، به صورت خودکار ";" را در جای مناسبی قرار می‌دهد.

مطالب
C# 7.1 - async Main
پس از فعالسازی C# 7.1، اولین ویژگی جدید C# 7.1، متدهای Main برنامه‌های کنسول (و همچنین WPF و WinForms) هستند که اینبار قابلیت تعریف async را نیز دارند.


نحوه‌ی کار با متدهای async، در متد‌های Main نگارش‌های پیش از C# 7.1

برای کار با متدهای Async نیاز است از واژه‌ی کلیدی await استفاده شود و با قید این واژه، ضروری است واژه‌ی کلیدی async نیز به امضای متد دربرگیرنده‌ی عملیات اضافه گردد؛ اما در نگارش‌های پیشین زبان #C، امکان async تعریف کردن متد Main وجود نداشت. در این حالت می‌بایستی به صورت ذیل عمل می‌شد:
static void Main(string[] args)
{
  MainAsync().GetAwaiter().GetResult();
  Console.ReadLine();
}

private static async Task MainAsync()
{
   using (StreamReader reader = File.OpenText("Program.cs"))
   {
       var message = await reader.ReadToEndAsync().ConfigureAwait(false);
       Console.Write(message);
   }
}
در این حالت برای کار با متد ReadToEndAsync ابتدا نیاز است جهت استفاده‌ی از واژه‌ی کلیدی async، این متد را در یک متد دربرگیرنده‌ی مجزا تعریف کرد تا بتوان تعاریف متداول اعمال غیرهمزمان را به آن متد، که حساسیتی بر روی امضای آن وجود ندارد، اضافه کرد؛ مانند متد MainAsync فوق. سپس می‌توان این متد را به همراه GetAwaiter().GetResult در متد Main اصلی فراخوانی کرد.


نحوه‌ی کار با متدهای async در متدهای Main برنامه‌های مبتنی بر C# 7.1

در زبان سی‌شارپ، متدهای Main برنامه‌های کنسول می‌توانند خروجی‌هایی از نوع void و int داشته باشند؛ به همراه آرگومانی از نوع []string و یا بدون آرگومان. اکنون در سی‌شارپ 7.1، دو امضای دیگر نیز به این مجموعه، جهت کار با اعمال Async اضافه شده‌است: async Task و یا <async Task<int
در این حالت مثال قبل را می‌توان به صورت ذیل خلاصه کرد:
static async Task Main(string[] args)
{
   using (StreamReader reader = File.OpenText("Program.cs"))
   {
      var message = await reader.ReadToEndAsync().ConfigureAwait(false);
      Console.Write(message);
   }
   Console.ReadLine();
}
اولین تغییر، افزودن نوع خروجی async Task به متد Main است. سپس دیگر نیازی به تعریف یک متد دربرگیرنده‌ی دیگر جهت اعمال این امضاء وجود ندارد و می‌توان محتوای متد کمکی MainAsync را مستقیما درون متد Main ذکر کرد.


نگاهی به پشت صحنه‌ی کامپایل async Task Main در C# 7.1

در عمل، کامپایلر سی‌شارپ جهت حفظ سازگاری با نگارش‌های قبلی، مجددا همان متد static void Main را تولید می‌کند و عملیاتی را که در مورد نگارش‌های پیشین توضیح داده شد، تکرار خواهد کرد:
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace ConsoleCS71
{
    internal class Program
    {
        private static async Task Main(string[] args)
        {
            StreamReader reader = File.OpenText("message.txt");
            try
            {
                string str = await reader.ReadToEndAsync();
                string message = str;
                str = (string)null;
                Console.Write(message);
                message = (string)null;
            }
            finally
            {
                if (reader != null)
                    reader.Dispose();
            }
            reader = (StreamReader)null;
            Console.ReadLine();
        }
        [SpecialName]
        private static void <Main>(string[] args)
        {
          Program.Main(args).GetAwaiter().GetResult();
        }
    }
}
همانطور که مشاهده می‌کنید، در اینجا نیز متد Main اصلی همان static void قدیمی است و محتوای داخل متد Main را به یک متد کمکی دربرگیرنده منتقل کرده و در نهایت GetAwaiter().GetResult را بر روی این متد فراخوانی کرده‌است.


در یک برنامه‌ی سی‌شارپ می‌توان بیش از یک متد Main داشت

تعریف بیش از یک متد Main در برنامه‌های سی‌شارپ مجاز است:
namespace ConsoleApp5
{
    class Class1
    {
        static void Main(string[] args)
        {
        }
    }

    class Class2
    {
        static void Main(string[] args)
        {
        }
    }
}
در این حالت تنها کافی است به خواص پروژه مراجعه کرده و متد Main آغازگر را انتخاب کرد:


اما ... اگر دقت کنید، متد async Task Main در اینجا ایندکس نشده‌است که به نظر کمبود نگارش فعلی VS 2017 است.
مطالب
فراخوانی داینامیک اسمبلی‌های آفیس در برنامه

در بسیاری از پروژه‌های دات نت، نیاز به استفاده از فایلهای نرم افزار آفیس، از قبیل ورد و اکسل و ... وجود دارد. برای مثال گاهی لازم است اطلاعات  یک گرید، یا هر منبع داده‌ای، در قالب اکسل به کاربر نمایش داده شود. بدین شکل که این فایلها در زمان اجرا ساخته شده و به کاربر نمایش داده شود .حال فرض کنید شما روی سیستم خودتان Office2007 را نصب کرده اید و به اسمبلی‌های این ورژن دسترسی دارید. البته بدون نیاز به نصب آفیس نیز میتوان به این توابع دسترسی داشت و از آنها در برنامه استفاده کرد که همان استفاده از  Primary Interop Assemblies میباشد. مشکلی که ممکن است پیش آید این است که در کامپیوترهای کاربران ممکن است ورژن‌های مختلفی از آفیس نصب باشد مانند 2003 -2007-2010-2013 و اگر با ورژن اسمبلی‌هایی که فراخوانی‌های فایلهای اکسل از طریق آن انجام شده باشد متفاوت باشد، برنامه اجرا نمی‌شود .

در حالت معمول برای نمایش یک فایل آفیس مثل اکسل در برنامه، ابتدا اسمبلی مربوطه را (اکسل در این مثال) که به نام Microsoft.Office.Interop.Excel میباشد به اسمبلی‌های برنامه اضافه کرده (از طریق add reference) و برای نمایش یک فایل اکسل در زمان اجرا از کدهای زیر استفاده مینماییم :

try
            {
               var application = (Microsoft.Office.Interop.Excel.Application)Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));  
               Workbook  wrkBook;
                var  wbk = application.Workbooks;
                 wrkBook = wbk.Add();
                wrkBook.Activate();
                application.Visible = true;
             }
            catch (Exception ex)
            {
                Error(ex.Message);
            }

حال اگر آفیس 2010 به عنوان مثال در سیستم ما نصب باشد، ورژن این اسمبلی 14 می‌باشد و اگر این برنامه را در کامپیوتر کلاینتی که آفیس 2007 بر روی آن نصب باشد انتشار دهیم اجرا نمیشود. برای حل این مشکل بنده با استفاده از روش dynamic این موضوع را حل کردم و بنظر می‌رسد راه‌های دیگری نیز برای حل آن وجود داشته باشد.

در این روش با توجه به ورژن آفیسی که بر روی سیستم کاربر نصب شده اسمبلی مربوطه را از سیستم کاربر لود کرده و فایلهای آفیس را اجرا مینماییم. در ابتدا تشخیص میدهیم چه ورژنی از آفیس بر روی سیستم کاربر نصب است :

 string strVersion = null;
                dynamic objEApp =Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));
                if (objEApp.Version == "12.0")
                {
                    strVersion = "2007";
                }
                else if (objEApp.Version == "14.0")
                {
                    strVersion = "2010";
                }

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

            string strEVersionSubKey = "\\Excel.Application\\CurVer";       
            string strValue = null; //Value Present In Above Key
            string strVersion = null; //Determines Excel Version 
            RegistryKey rkVersion = null; //Registry Key To Determine Excel Version 
            rkVersion = Registry.ClassesRoot.OpenSubKey(strEVersionSubKey, false); //Open Registry Key 
            if ((rkVersion != null)) //If Key Exists
            {
                strValue = (string)rkVersion.GetValue(string.Empty); //Get Value 
                strValue = strValue.Substring(strValue.LastIndexOf(".") + 1); //Store Value 
                switch (strValue) //Determine Version
                {
                    case "11":
                        strVersion = "2003";
                        break;
 
                    case "12":
                        strVersion = "2007";
                        break;
 
                    case "14":
                        strVersion = "2010";
                        break;
 
                }
حال با استفاده از تابع ()assembly.load اسمبلی مورد نیاز را لود کرده و در برنامه استفاده مینماییم : 
if (strVersion == "2007")
                {
                    string strAssemblyOff2007 =
                        "Microsoft.Office.Interop.Excel, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c";
                    try
                    {
                        Assembly xslExcelAssembly = Assembly.Load(strAssemblyOff2007); //Load Assembly
                        Type type = xslExcelAssembly.GetTypes().Single(t => t.Name == "ApplicationClass");
                        dynamic application = Activator.CreateInstance(type);
                        var workbooks = application.Workbooks;
                        var workbook = workbooks.Add();
                        var worksheet = workbook.Worksheets[1];
                        workbook.Activate();
                        application.Visible = true;
                    }
                    catch (Exception ex)
                    {
                    }
                }
در این حالت بدون اینکه بدانیم بر روی سیستم کاربر چه ورژنی از آفیس نصب است میتوان فایلهای آفیس را در زمان اجرا لود کرده و استفاده کرد . 
بازخوردهای دوره
صفحات مودال در بوت استرپ 3
با فرض اینکه شما در فراخوانی مودال اول مشکلی نداشته باشید . فرض میکنیم شما عکس رو با ajax فراخوانی کرده و در مودال دوم نمایش می‌دهید . فقط کافیست شما در مودال اول یک دکمه قرا دهید و با اون دکمه مودال دوم رو فراخوانی کنید . یک فایل نمونه پیوست شد.
testmodal-(2).html

من خودم اکثر اوقات با json مقادیر مورد نظر رو در مودال نشون میدم ، چیزی شبیه به این :
  function gettData(code) {
        $.ajax({

            url: '@Url.Action(MVC.X.Y())',
            data: { code: code },
            type: "POST",
            success: function (data) {
                $("#title").html(data.Title);
                $("#user-image").html(data.imagePath); 
            },
            error: function (response) {

            }

        });
    }
رویداد کلیک ردیف‌های لیست داخل مودال اول :
     $(document).on('click', '.item', function () {
            
            getData($(this).data("code"));
            $("#SecondModal").modal('toggle');
        });
بازخوردهای دوره
مدیریت نگاشت ConnectionIdها در SignalR به کاربران واقعی سیستم
- البته این متد با این امضاء از نگارش جدید SiganlR حذف شده‌است. نسخه‌ی به روز رسانی شده‌ی آن در اینجا.
+ بحثی در اینجا شده و پیشنهاد کردند این کد را در سمت کلاینت اضافه کنید:
window.onbeforeunload = function (e) {
    $.connection.hub.stop();
};
بازخوردهای دوره
افزونه‌ای برای کپسوله سازی نکات ارسال یک فرم ASP.NET MVC به سرور توسط jQuery Ajax
نیاز به کمی تغییر دارد. اگر خروجی اکشن متد شما به این نحو باشد:
return json(new { result = "ok" });
در سمت اسکریپت در $.ajax:
//...
success: function (data) {
                    if (data) {
                        alert("json data: " + data.result);
                    }                    
                }
اشتراک‌ها
Visual Studio Code 1.16 منتشر شد

The new version ships with the Extract function refactoring for TypeScript. Also, HTML tags are now closed automatically. 

Visual Studio Code 1.16 منتشر شد