مطالب
CoffeeScript #13

بخش‌های بد

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

Reserved words

کلمات کلیدی خاصی در جاوااسکریپت وجود دارد مانند class، enum و const که برای نسخه‌های بعدی جاوااسکریپت در آینده رزرو شده‌اند. استفاده از این کلمات در برنامه‌های جاوااسکریپت می‌تواند نتایج غیرقابل پیش بینی داشته باشد. برخی از مرورگرهای به خوبی از عهده‌ی این کار برمی‌آیند و بعضی دیگر به طور کامل جلوی استفاده از این‌ها را گرفته‌اند. CoffeeScript بعد از تشخیص استفاده از یک کلمه‌ی کلیدی، با یک راه کار خاص، از این موضوع می‌گریزد.

به عنوان مثال، فرض کنید می‌خواهیم از کلمه کلیدی class به عنوان یک خصوصیت در یک شیء استفاده کنیم:

myObj = {
  delete: "I am a keyword!"
}
myObj.class = ->
پس از کامپایل، پارسر CoffeeScript متوجه استفاده شما از کلمه کلیدی رزرو شده می‌شود و آنها را در بین "" قرار می‌دهد.
var myObj;
myObj = {
  "delete": "I am a keyword!"
};
myObj["class"] = function() {};

Equality comparisons

مقایسه برابری ضعف دیگری است که در جاوااسکریپت باعث ایجاد رفتاری گیج کننده و اغلب باعث ایجاد اشکالاتی در کد نوشته شده می‌شود. به مثال زیر توجه کنید:

""           ==   "0"// false
0            ==   ""// true
0            ==   "0"// true
false        ==   "false"// false
false        ==   "0"// true
false        ==   undefined// false
false        ==   null// false
null         ==   undefined// true
" \t\r\n"    ==   0// true
مطمئنم که شما هم با من موافقید که همه‌ی مقایسه‌های بالا بسیار مبهم هستند و استفاده از آن‌های می‌توانند منجر به نتایج غیر منتظره شوند و همچنین مشکلاتی را پیش بیاورند.
راه حل این کار استفاده از عملگر برابری سختگیرانه است، که از 3 مساوی تشکیل شده است: === عملگر برابر سخت گیرانه دقیقا مانند عملگر برابری عادی عمل می‌کند و تنها نوع داده‌ها را بررسی می‌کند که با هم برابر باشند.
توصیه می‌شود که همیشه از عملگر برابری سختگیرانه استفاده کنید و هرجا لازم بود قبل مقایسه عمل تبدیل نوع داده‌ها را انجام دهید.
CoffeeScript این مشکل را به صورت کامل حل کرده است؛ یعنی هر جایی که عمل مقایسه == انجام شود به === تبدیل می‌شود. شما باید به صورت صریح نوع داده‌ها را قبل از مقایسه تبدیل کرده باشید.
نکته: در مقایسه‌ها رشته خالی ""، null ،undefined و عدد 0 همگی false برمی گردانند.
alert "Empty Array"  unless [].length
alert "Empty String" unless ""
alert "Number 0"      unless 0
که پس از کامپایل می‌شود:
if (![].length) {
  alert("Empty Array");
}

if (!"") {
  alert("Empty String");
}

if (!0) {
  alert("Number 0");
}
در صورتیکه می‌خواهید به صورت صریح null و یا undefined را بررسی کنید، می‌توانید از عملگر ? CoffeeScript استفاده کنید:
alert "This is not called" unless ""?
پس از کامپایل می‌شود:
if ("" == null) {
  alert("This is not called");
}
با اجرای مثال بالا alert اجرای نمی‌شود چون رشته خالی با null برابر نیست.

Function definition


خیلی جالب است که در جاوااسکریپت می‌توانید تابعی را بعد از اینکه فراخوانی کردید، تعریف کنید. به عنوان مثال، کد زیر به صورت کامل اجرا می‌شود:
wem();
function wem() {alert("hi");}
این به دلیل دامنه (scope) تابع است. تمام توابع قبل از اجرای برنامه، به بالا برده می‌شوند و در همه جا در دامنه‌ای که در آن تعریف شده‌اند، قابل دسترسی می‌باشند؛ حتی اگر قبل از تعریف واقعی در منبع، فراخوانی شده باشد. مشکل اینجاست که عمل بالابردن توابع در مرورگرها با یکدیگر متفاوت است. به مثال زیر توجه کنید:
if (true) {
  function declaration() {
    return "first";
  }
} else {
  function declaration() {
    return "second";
  }
}
declaration();
در بعضی از مرورگرها مانند Firefox ، تابع ()declaration مقدار "first" را برگشت خواهد داد و در دیگر مرورگرها مانند Chrome، مقدار "second" برگشت داده خواهد شد. در حالیکه به نظر می‌رسد که قسمت else هیچگاه اجرا نخواهد شد.
در صورتیکه علاقمند به کسب اطلاعات بیشتری درباره‌ی نحوه تعریف توابع، هستید باید راهنمای آقای Juriy Zaytsev را مطالعه کنید. به صورت خلاصه، رفتار نسبتا مبهم مرورگرها می‌تواند منجر به ایجاد مشکلاتی در مسیر نوشتن یک پروژه شوند.
همه چیز در CoffeeScript در نظر گرفته شده است و بهترین روش برای حل این مشکل، حذف کلمه function و به جای آن استفاده از عبارت (expression) تابع است.

Number property lookups

نقصی که در پارسر جاوااسکریپت در مواجه با نماد نقطه (dot notation) بر روی اعداد وجود دارد، تفسیر آن به ممیز شناور، بجای مراجعه به ویژگی‌های آن است. برای مثال کد جاوااسکریپت زیر باعث ایجاد خطای نحوی می‌شود:
5.toString();
پارسر جاوااسکریپت بعد از نقطه به دنبال یک عدد دیگر می‌گردد و با برخورد با ()toString، باعث ایجاد یک Unexpected token می‌شود. راه حل این مشکل، استفاده از پرانتز یا اضافه کردن یک نقطه دیگر است.
(5).toString();
5..toString();
خوشبختانه پارسر CoffeeScript به اندازه‌ی کافی هوشمندانه با این مسئله برخورد می‌کند و هر زمانی که شما دسترسی به ویژگی‌های اعداد را داشته باشید، به صورت خودکار با اضافه کردن دوتا نقطه (همانند مثال بالا) جلوی ایجاد خطا را می‌گیرد.
مطالب
WebStorage
webstorage تقریبا فناوری جدیدی است که برای نگهداری ثابت داده‌ها بر روی سیستم کاربر استفاده می‌شود. webstorage مزایای زیادی برای برنامه‌های تحت وب دارد. برای مثال با استفاده از آن میتوان فعالیت‌های کاربر را رصد کرد، بدون اینکه کد و دیتابیس سمت سرور را دخالت دهیم. حتی اگر سیستم کاربر آفلاین هم بشود برنامه می‌تواند همچنان به فعالیتش ادامه دهد. در این مقاله به مزایای این روش می‌پردازیم.

WebStorage در برابر کوکی ها
یکی از روش‌های سنتی ذخیره اطلاعات در سیستم کاربر، کوکی‌ها در بستر Http هستند. تفاوت‌های زیادی بین این دو وجود دارد که تعدادی از آن‌ها را در زیر بررسی می‌کنیم:

  • مکانیزم ذخیره سازی:
کوکی‌ها داده‌های ساخت یافته‌ای هستند که از وب سرور به سمت مرورگر کاربر به عنوان پاسخی در ازای درخواستی ارسال می‌شوند. درخواست و پاسخ کوکی‌ها شامل بخش هدر بوده که اطلاعات آن باعث شناسایی کوکی برای مدیریت و شناسایی آن‌ها می‌گردد تا هر موقع درخواستی صورت بگیرد، به سمت سرور برگشت خواهد خورد.
به طور خلاصه اینکه کوکی‌ها توسط درخواست‌ها و پاسخ‌ها ایجاد یا به روز می‌شوند. در نتیجه داده‌ها چه تغییر کرده باشند چه تغییر نکرده باشند، همیشه بخشی از هدر Http هستند. در سوی دیگر webstorage به طور کامل به صورت کلاینتی پیاده سازی گشته است و وب سرور را درگیر کار خودشان نمی‌کنند و کارایی بهتری را ارائه می‌دهند. از آنجا که همه چیز در خود سیستم کاربر اتفاق می‌افتد، در صورت از دست دادن کانکشن شبکه، کاربر می‌تواند همچنان به فعالیت‌های به روزرسانی و تغییر ادامه دهد.

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

  • محدودیت حجمی
محدودیت کوکی‌ها و webstorage از نظر حجم ذخیره سازی بین مروگرهای مختلف، متفاوت است. ولی در حالت کلی در بیشتر مرورگرها محدودیت حجم 4 کیلوبایت برای کوکی‌ها موجود است. (این ابزار می تواند نهایت حجمی را که که مرورگر شما از کوکی پشتیبانی می‌کند، نشان دهد.)
در مورد webstorage استاندارد W3C محدودیتی اعلام نکرده است و تصمیم گیری بر سر این موضوع را به سازندگان مرورگرها واگذار کرده است. ولی در حالت کلی حجم بیشتری از کوکی را ذخیره میکند و عموما تا 5 مگابایت توانایی ذخیره سازی وجود دارد. بدین ترتیب حجم آن 124,527% بیشتر از کوکی‌ها است. (این ابزار می‌تواند نهایت حجمی را که مرورگر شما از webstorage پشتیبانی می‌کند، ببینید).

انواع Webstorage 
دو نوع متد ذخیره سازی در webstorage داریم:
  • session storage
  • local storage

  • SessionStorage
داده‌هایی که بدین صورت ذخیره می‌شوند تنها تا زمانی دوام آورده و پایدار هستند که session مرورگر فعال است؛ یعنی تا زمانیکه کاربر در سایت فعلی حضور دارد.
استفاده از این روش برای ذخیره سازی‌های موقت عالی است. برای نمونه مقادیر ورودی فرمی که کاربر در حال کار با آن است، میتواند به طور موقت ذخیره شوند تا زمانی که کاربر بتواند تمامی مراحل را طی کرده، بدون اینکه ارجاعی به دیتابیس سمت سرور داشته باشد. همچنین ذخیره موقت داده‌ها می‌تواند به کاربر کمک کند تا در صورت reresh‌های ناگهانی یا بسته شدن‌های ناگهانی مرورگرها، نیازی به ورود مجدد داده‌ها نداشته باشد.
  • LocalStorage
در این روش داده‌ها با از دست رفتن session مرورگر جاری از بین نمی‌رود و برای بازدیدهای آتی کاربر از سایت، داده‌ها همچنان در دسترس هستند.
پشتیبانی مرورگرها
وب سایت caniuse گزارش می‌دهد که اکثر مرورگرها پشتیبانی خوبی از webstorage دارند.

با اینکه توصیه نامه W3C از پایان کار پیاده سازی این قابلیت خبر میدهد ولی در حال حاضر که این مقاله تدوین شده است هنوز نهایی اعلام نشده است. برای پشتیبانی مرورگرهای قدیمی از webstorage می‌توان از فایل جاوااسکریپتی Store.js کمک گرفت.


مفاهیم امنیتی و محافظت از داده ها

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

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

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

  • اگر کاربر قابلیت webstorage را غیرفعال کرده باشد.
  • اگر فضایی برای کاربر باقی نمانده باشد.
  • با محدودیت حجمی webstorage مواجه شده است.
  • با مواجه شدن با خطاها یک استثنا صادر می‌شود که می‌توانید آن را دریافت و کنترلی را روی برنامه تحت وب داشته باشید. یک نمونه استثنا  QuotaExceededError  


IndexedDB

یکی از فرایندهای ذخیره سازی داده‌ها که همان مزایای webstorage را ارائه میدهد indexed Database API است. این قابلیت از HTML 5 اضافه شده است و قسمتی از مشخصات webstorage شناخته نمی‌شود. برای همین مستنداتی در حوزه‌ی webstorage برای آن پیدا نخواهید کرد ولی قابلیت‌هایی فراتر از webstorage  دارد.

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

در قسمت آتی نحوه کدنویسی آن را فرا خواهیم گرفت.

مطالب
آموزش Prism #2
در پست قبلی توضیح کلی درباره فریم ورک Prism داده شد. در این بخش قصد داریم آموزش‌های داده شده در پست قبلی را با هم در یک مثال مشاهده کنیم. در پروژه‌های ماژولار طراحی و ایجاد زیر ساخت قوی برای مدیریت ماژول‌ها بسیار مهم است. Prism فریم ورکی است که فقط چارچوب و قواعد اصول طراحی این گونه پروژه‌ها را در اختیار ما قرار می‌دهد. در پروژه‌های ماژولار هر ماژول باید در یک اسمبلی جدا قرار داشته باشد که ساختار پیاده سازی آن می‌تواند کاملا متفاوت با پیاده سازی سایر ماژول‌ها باشد.
 برای شروع  باید فایل‌های اسمبلی Prism رو دانلود کنید(لینک دانلود).
تشریح پروژه:
می‌خواهیم برنامه ای بنویسیم که دارای سه ماژول زیر است.:
  1. ماژول Navigator : برای انتخاب و Switch کردن بین ماژول‌ها استفاده می‌شود؛
  2. ماژول طبقه بندی کتاب‌ها : لیست طبقه بندی کتاب‌ها را به ما نمایش می‌دهد؛
  3. ماژول لیست کتاب‌ها : عناوین کتاب‌ها به همراه نویسنده و کد کتاب را به ما نمایش می‌دهد.

*در این پروژه از UnityContainer برای مباحث Dependency Injection استفاده شده است.
ابتدا یک پروژه WPF در Vs.Net ایجاد کنید(در اینجا من نام آن را  FirstPrismSample گذاشتم). قصد داریم یک صفحه طراحی کنیم که دو ماژول مختلف در آن لود شود. ابتدا باید Shell پروژه رو طراحی کنیم. یک Window جدید به نام Shell بسازید و کد زیر را در آن کپی کنید.
<Window x:Class="FirstPrismSample.Shell"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:com="http://www.codeplex.com/CompositeWPF"
    Title="Prism Sample By Masoud Pakdel" Height="400" Width="600" WindowStartupLocation="CenterScreen">
    <DockPanel>
      <ContentControl com:RegionManager.RegionName="WorkspaceRegion" Width="400"/>
      <ContentControl com:RegionManager.RegionName="NavigatorRegion"  DockPanel.Dock="Left" Width="200" />     
    </DockPanel>
</Window>
در این صفحه دو ContentControl تعریف کردم یکی به نام Navigator و دیگری به نام Workspace. به وسیله RegionName که یک AttachedProperty است هر کدوم از این نواحی را برای Prism تعریف کردیم. حال باید یک ماژول برای Navigator و دو ماژول دیگر یکی برای طبقه بندی کتاب‌ها و دیگری برای لیست کتاب‌ها بسازیم.

#پروژه Common
قبل از هر چیز یک پروژه Common می‌سازیم و مشترکات بین ماژول‌ها رو در آن قرار می‌دهیم(این پروژه باید به تمام ماژول‌ها رفرنس داده شود).  این مشترکات شامل :
  • کلاس پایه ViewModel
  • کلاس ViewRequestEvent
  • کلاس ModuleService

کد کلاس ViewModelBase که فقط اینترفیس INotifyPropertyChanged رو پیاده سازی کرده است:

using System.ComponentModel;

namespace FirstPrismSample.Common
{
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void RaisePropertyChangedEvent( string propertyName )
        {
            if ( PropertyChanged != null )
            {
                PropertyChangedEventArgs e = new PropertyChangedEventArgs( propertyName );
                PropertyChanged( this, e );
            }
        }
    }
}
کلاس ViewRequestEvent که به صورت زیر است:
using Microsoft.Practices.Composite.Presentation.Events;

namespace FirstPrismSample.Common.Events
{
    public class ViewRequestedEvent : CompositePresentationEvent<string>
    {
    }
}
توضیح درباره CompositePresentationEvent :
در طراحی و توسعه پروژه‌های ماژولار نکته ای که باید به آن دقت کنید این است که ماژول‌های پروژه نباید به هم وابستگی مستقیم داشته باشند در عین حال ماژول‌ها باید بتوانند با هم در ارتباط باشند. CPE یا CompositePresentationEventدقیقا برای این منظور به وجود آمده است. CPE که در این جا طراحی کردم فقط کلاسی است که از CompositePresentationEventارث برده است و دلیل آن که به صورت string generic استفاده شده است این است که می‌خواهیم در هر درخواست نام ماژول درخواستی را داشته باشیم و به همین دلیل نام آن را ViewRequestedEvent گذاشتم.

توضیح درباره EventAggregator

EventAggregator یا به اختصار EA مکانیزمی است در پروژهای ماژولار برای اینکه در Composite UI‌ها بتوانیم بین کامپوننت‌ها ارتباط برقرار کنیم. استفاده از EA وابستگی بین ماژول‌ها را  از بین خواهد برد. برنامه نویسانی که با MVVM Light آشنایی دارند از قابلیت Messaging موجود در این فریم ورک برای ارتباط بین View و  ViewModel استفاده می‌کنند. در Prism این عملیات توسط EA انجام می‌شود. یعنی برای ارتباط با View‌ها باید از EA تعبیه شده در Prism استفاده کنیم. در ادامه مطلب، چگونگی استفاده از EA را خواهید آموخت.
اینترفیس IModuleService که فقط شامل یک متد است:
namespace FirstPrismSample .Common
{
    public interface IModuleServices
    {     
        void ActivateView(string viewName);
    }
}
کلاس ModuleService که اینترفیس بالا را پیاده سازی کرده است:
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;

namespace FirstPrismSample.Common
{
    public class ModuleServices : IModuleServices
    {     
        private readonly IUnityContainer m_Container;  
     
        public ModuleServices(IUnityContainer container)
        {
            m_Container = container;
        }      
   
        public void ActivateView(string viewName)
        {        
            var regionManager = m_Container.Resolve<IRegionManager>();

            // غیر فعال کردن ویو
            IRegion workspaceRegion = regionManager.Regions["WorkspaceRegion"];
            var views = workspaceRegion.Views;
            foreach (var view in views)
            {
                workspaceRegion.Deactivate(view);
            }

            //فعال کردن ویو انتخاب شده 
            var viewToActivate = regionManager.Regions["WorkspaceRegion"].GetView(viewName);
            regionManager.Regions["WorkspaceRegion"].Activate(viewToActivate);
        }
    }
}
متد ActivateView نام view مورد نظر برای فعال سازی را دریافت می‌کند. برای فعال کردن View ابتدا باید سایر view‌های فعال در RegionManager را غیر فعال کنیم. سپس فقط view مورد نظر در RegionManager انتخاب و فعال می‌شود.

*نکته: در هر ماژول ارجاع به اسمبلی‌های Prism مورد نیاز است.

#ماژول طبقه بندی کتاب ها:
برای شروع یک Class Library جدید به نام ModuleCategory به پروژه اضافه کنید. یک UserControl به نام CategoryView بسازید و کد‌های زیر را در آن کپی کنید.
<UserControl x:Class="FirstPrismSample.ModuleCategory.CategoryView "
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             Background="LightGray" FlowDirection="RightToLeft" FontFamily="Tahoma">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBlock Text=" طبقه بندی ها"/>
        <ListView Grid.Row="1"  Margin="10" Name="lvCategory">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="کد" Width="50" />
                    <GridViewColumn Header="عنوان" Width="200"  />                  
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</UserControl>
یک کلاس به نام CategoryModule بسازید که اینترفیس IModule رو پیاده سازی کند.
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;
using FirstPrismSample.Common;
using FirstPrismSample.Common.Events;
using Microsoft.Practices.Composite.Presentation.Events;

namespace FirstPrismSample.ModuleCategory
{
    [Module(ModuleName = "ModuleCategory")]
    public class CategoryModule : IModule
    {      
        private readonly IUnityContainer m_Container;
        private readonly string moduleName = "ModuleCategory";
            
        public CategoryModule(IUnityContainer container)
        {
            m_Container = container;
        }   
      
        ~CategoryModule()
        {
            var eventAggregator = m_Container.Resolve<IEventAggregator>();
            var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>();       
            viewRequestedEvent.Unsubscribe(ViewRequestedEventHandler);
        }
     
        public void Initialize()
        {           
            var regionManager = m_Container.Resolve<IRegionManager>();
            regionManager.Regions["WorkspaceRegion"].Add(new CategoryView(), moduleName);
         
            var eventAggregator = m_Container.Resolve<IEventAggregator>();
            var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>();
            viewRequestedEvent.Subscribe(this.ViewRequestedEventHandler, true);
        }
       
        public void ViewRequestedEventHandler(string moduleName)
        {
            if (this.moduleName != moduleName) return;
          
            var moduleServices = m_Container.Resolve<IModuleServices>();
            moduleServices.ActivateView(moduleName);
        }      
    }
}
چند نکته :
*ModuleAttribute استفاده شده در بالای کلاس برای تعیین نام ماژول استفاده می‌شود. این Attribute دارای دو خاصیت دیگر هم است :
  1. OnDemand : برای تعیین اینکه ماژول باید به صورت OnDemand (بنا به درخواست) لود شود.
  2. StartupLoaded : برای تعیین اینکه ماژول به عنوان ماژول اول پروزه لود شود.(البته این گزینه Obsolute شده است)

*برای تعریف ماژول کلاس مورد نظر حتما باید اینترفیس IModule را پیاده سازی کند. این اینترفیس فقط شامل یک متد است به نام Initialize.

*در این پروژه چون View‌های برنامه صرفا جهت نمایش هستند در نتیجه نیاز به ایجاد ViewModel برای آن‌ها نیست. در پروژه‌های اجرایی حتما برای هر View باید ViewModel متناظر با آن تهیه شود.

توضیح درباره متد Initialize

در این متد ابتدا با استفاده از Container موجود RegionManager را به دست می‌آوریم. با استفاده از RegionManager می‌تونیم یک CompositeUI طراحی کنیم. در فایل Shell مشاهده کردید که یک صفحه به دو ناحیه تقسیم شد و به هر ناحیه هم یک نام اختصاص دادیم. دستور زیر به یک ناحیه اشاره خواهد داشت:

regionManager.Regions["WorkspaceRegion"]
در خط بعد با استفاده از EA یا Event Aggregator توانستیم CPE را بدست بیاوریم. متد Subscribe در کلاس CPE  یک ارجاع قوی به delegate مورد نظر ایجاد می‌کند(پارامتر دوم این متد که از نوع boolean است) که به این معنی است که این delegate هیچ گاه توسط GC جمع آوری نخواهد شد. در نتیجه، قبل از اینکه ماژول بسته شود باید به صورت دستی این کار را انجام دهیم که مخرب را برای همین ایجاد کردیم. اگر به کد‌های مخرب دقت کنید می‌بینید که با استفاده از EA توانستیم ViewRequestEventHandler را Unsubscribe کنیم به دلیل اینکه از ارجاع قوی با strong Reference در متد Subscribe استفاده شده است.
دستور moduleService.ActiveateView ماژول مورد نظر را در region مورد نظر هاست خواهد کرد.

#ماژول لیست کتاب ها:
ابتدا یک Class Library به نام ModuleBook بسازید  و همانند ماژول قبلی نیاز به یک Window و یک کلاس داریم:
BookWindow که کاملا مشابه به CategoryView است.
<UserControl x:Class="FirstPrismSample.ModuleBook.BookView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Background="LightGray" FontFamily="Tahoma" FlowDirection="RightToLeft">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBlock Text="لیست کتاب ها"/>
        <ListView Grid.Row="1" Margin="10" Name="lvBook">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="کد" Width="50"  />
                    <GridViewColumn Header="عنوان" Width="200" />
                    <GridViewColumn Header="نویسنده" Width="150" />
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</UserControl>

کلاس BookModule که پیاده سازی  و توضیحات آن کاملا مشابه به CategoryModule می‌باشد.
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.Presentation.Events;
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;
using FirstPrismSample.Common;
using FirstPrismSample.Common.Events;

namespace FirstPrismSample.ModuleBook
{
    [Module(ModuleName = "moduleBook")]
    public class BookModule : IModule
    {      
        private readonly IUnityContainer m_Container;
        private readonly string moduleName = "ModuleBook";     
    
        public BookModule(IUnityContainer container)
        {
            m_Container = container;          
        }     
       
        ~BookModule()
        {           
            var eventAggregator = m_Container.Resolve<IEventAggregator>();
            var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>();
          
            viewRequestedEvent.Unsubscribe(ViewRequestedEventHandler);
        }     
     
        public void Initialize()
        {           
            var regionManager = m_Container.Resolve<IRegionManager>();
            var view = new BookView();
            regionManager.Regions["WorkspaceRegion"].Add(view, moduleName);
            regionManager.Regions["WorkspaceRegion"].Deactivate(view);
      
            var eventAggregator = m_Container.Resolve<IEventAggregator>();
            var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>();
            viewRequestedEvent.Subscribe(this.ViewRequestedEventHandler, true);
        }     
      
        public void ViewRequestedEventHandler(string moduleName)
        {           
            if (this.moduleName != moduleName) return;
         
            var moduleServices = m_Container.Resolve<IModuleServices>();
            moduleServices.ActivateView(m_WorkspaceBName);
        }
    }
}
#ماژول Navigator
برای این ماژول هم ابتدا View مورد نظر را ایجاد می‌کنیم:
<UserControl x:Class="FirstPrismSample.ModuleNavigator.NavigatorView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
    <Grid>
        <StackPanel VerticalAlignment="Center">
            <TextBlock Text="انتخاب ماژول" Foreground="Green" HorizontalAlignment="Center"
                VerticalAlignment="Center" FontFamily="Tahoma" FontSize="24" FontWeight="Bold" />
        <Button Command="{Binding ShowModuleCategory}" Margin="5" Width="125">طبقه بندی کتاب ها</Button>
        <Button Command="{Binding ShowModuleBook}" Margin="5" Width="125">لیست کتاب ها</Button>
        </StackPanel>
        </Grid>
</UserControl>
حال قصد داریم برای این View یک ViewModel بسازیم. نام آن را INavigatorViewModel خواهیم گذاشت:
public interface INavigatorViewModel
    {    
        ICommand ShowModuleCategory { get; set; }       
        ICommand ShowModuleBook { get; set; }

        string ActiveWorkspace { get; set; }       

        IUnityContainer Container { get; set; }

        event PropertyChangedEventHandler PropertyChanged;
    }
 *در اینترفیس بالا دو Command داریم که هر کدام وظیفه لود یک ماژول را بر عهده دارند.
 *خاصیت ActiveWorkspace برای تعیین workspace فعال تعریف شده است.

حال به پیاده سازی مثال بالا می‌پردازیم:
public class NavigatorViewModel : ViewModelBase, INavigatorViewModel
    {        
        public NavigatorViewModel(IUnityContainer container)
        {
            this.Initialize(container);
        }   
       
        public ICommand ShowModuleCategory { get; set; }
      
        public ICommand ShowModuleBook { get; set; }      
              
        public string ActiveWorkspace { get; set; }       

        public IUnityContainer Container { get; set; }        
     
        private void Initialize(IUnityContainer container)
        {
            this.Container = container;
            this.ShowModuleCategory = new ShowModuleCategoryCommand(this);
            this.ShowModuleBook = new ShowModuleBookCommand(this);
            this.ActiveWorkspace = "ModuleCategory";
        }        
    }
تنها نکته مهم در کلاس بالا متد Initialize است که دو Command مورد نظر را پیاده سازی کرده است. ماژول پیش فرض هم ماژول طبقه بندی کتاب‌ها یا ModuleCategory در نظر گرفته شده است.  همان طور که می‌بینید پیاده سازی Command‌ها بالا توسط دو کلاس ShowModuleCategoryCommand و ShowModuleBookCommand انجام شده که در زیر کد‌های آن‌ها را می‌بینید.
#کد کلاس ShowModuleCategoryCommand  
public class ShowModuleCategoryCommand : ICommand
    {      
        private readonly NavigatorViewModel viewModel;
        private const string workspaceName = "ModuleCategory";         

        public ShowModuleCategoryCommand(NavigatorViewModel viewModel)
        {
            this.viewModel = viewModel;
        }          

        public bool CanExecute(object parameter)
        {
            return viewModel.ActiveWorkspace != workspaceName;
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
     
        public void Execute(object parameter)
        {
            CommandServices.ShowWorkspace(workspaceName, viewModel);
        }      
    }
#کد کلاس ShowModuleBookCommand  
public class ShowModuleBookCommand : ICommand
    {
        private readonly NavigatorViewModel viewModel;
        private readonly string workspaceName = "ModuleBook";

        public ShowModuleBookCommand( NavigatorViewModel viewModel )
        {
            this.viewModel = viewModel;
        }

        public bool CanExecute( object parameter )
        {
            return viewModel.ActiveWorkspace != workspaceName;
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute( object parameter )
        {
            CommandServices.ShowWorkspace( workspaceName , viewModel );
        }
    }
با توجه به این که فرض است با متد‌های Execute و CanExecute و CanExecuteChanged آشنایی دارید از توضیح این مطالب خودداری خواهم کرد. فقط کلاس CommandServices  در متد Execute دارای متدی به نام ShowWorkspace است که کد‌های زیر را شامل می‌شود:
public static void ShowWorkspace(string workspaceName, INavigatorViewModel viewModel)
  {           
            var eventAggregator = viewModel.Container.Resolve<IEventAggregator>();
            var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>();
            viewRequestedEvent.Publish(workspaceName);
        
            viewModel.ActiveWorkspace = workspaceName;
 }
در این متد با استفاده از CPE که در پروژه Common ایجاد کردیم ماژول مورد نظر را لود خواهیم کرد. و بعد از آن مقدار ActiveWorkspace جاری در ViewModel به نام ماژول تغییر پیدا می‌کند. متد Publish در CPE این کار را انجام خواهد دارد.

عدم وابستگی ماژول ها
همان طور که می‌بینید ماژول‌های پروژه به هم Reference داده نشده اند حتی هیچ Reference هم به پروژه اصلی یعنی جایی که فایل App.xaml قرار دارد، داده نشده است ولی در عین حال باید با هم در ارتباط باشند. برای حل این مسئله این ماژول‌ها باید در فولدر bin پروژه اصلی خود را کپی کنند. بهترین روش استفاده از Pre-Post Build Event خود VS.Net است. برای این کار از پنجره Project Properties وارد برگه Build Events شوید و از قسمت Post Build Event Command Line  استفاده کنید و کد زیر را در آن کپی نمایید:
xcopy "$(TargetDir)FirstPrismSample.ModuleBook.dll" "$(SolutionDir)FirstPrismSample\bin\$(ConfigurationName)\Modules\" /Y
قطعا باید به جای FirstPrismSample نام Solution خود و به جای ModuleBook نام ماژول را وارد نمایید.

مانند:


مراحل بالا برای هر ماژول باید تکرار شود(ModuleNavigation , ModuleBook , ModuleCategory). بعد از Rebuild  پروژه در فولدر bin پروژه اصلی یک فولدر به نام Module ایجاد می‌شود که اسمبلی هر ماژول در آن کپی خواهد شد.

ایجاد Bootstrapper
حال نوبت به Bootstrapper میرسد(در پست قبلی در باره مفهوم Bootstrapper شرح داده شد). در پروژه اصلی یعنی جایی که فایل App.xaml قرار دارد کلاس زیر را ایجاد کنید.
    public class Bootstrapper : UnityBootstrapper
    {     
        protected override void ConfigureContainer()
        {
            base.ConfigureContainer();
            Container.RegisterType<IModuleServices, ModuleServices>();
        }
   
        protected override DependencyObject CreateShell()
        {
            var shell = new Shell();
            shell.Show();
            return shell;
        }
   
        protected override IModuleCatalog GetModuleCatalog()
        {           
            var catalog = new DirectoryModuleCatalog();
            catalog.ModulePath = @".\Modules";
            return catalog;
        }
    }
متد ConfigureContainer برای تزریق وابستگی به وسیله UnityContainer استفاده می‌شود. در این متد باید تمامی Registration‌های مورد نیاز برای DI را انجام دهید. نکته مهم این است که عملیات وهله سازی و Initialization برای  Container  در متد base کلاس UnityBootstrapper انجام خواهد شد پس همیشه باید متد base این کلاس در ابتدای این متد فراخوانی شود در غیر این صورت با خطا متوقف خواهید شد.
متد CreateShell برای ایجاد و وهله سازی از Shell پروژه استفاده می‌شود. در این جا یک وهله از Shell Window برگشت داده می‌شود.
متد GetModuleCatalog برای تعیین مسیر ماژول‌ها در پروژه کاربرد دارد. در این متد با استفاده از خاصیت ModulePath کلاس DirectoryModuleCatalog تعیین کرده ایم که ماژول‌های پروژه در فولدر Modules موجود در bin اصلی پروژه قرار دارد. اگر به دستورات کپی در Post Build Event قسمت قبل توجه کنید می‌بینید که دستور ساخت فولدر وجود دارد.
"$(SolutionDir)FirstPrismSample\bin\$(ConfigurationName)\Modules\" /Y
*نکته: اگر استفاده از این روش برای شناسایی ماژول‌ها توسط Bootstrapper را چندان جالب نمی‌دانید می‌تونید از MEF استفاده کنید که اسمبلی ماژول‌های پروژه را به راحتی شناسایی می‌کند و در اختیار Bootsrtapper قرار می‌دهد(از آن جا در مستندات مربوط به Prism، بیشتر به استفاده از MEF تاکید شده است من هم در پست‌های بعدی، مثال‌ها را با MEF پیاده سازی خواهم کرد)

در پایان باید فایل App.xaml را تغییر دهید به گونه ای که متد Run در کلاس Bootstapper ابتدا اجرا شود.
public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            var bootstrapper = new Bootstrapper();
            bootstrapper.Run();
        }
    }


اجرای پروژه:
بعد از اجرا، با انتخاب ماژول مورد نظر اطلاعات ماژول در Workspace Content Control لود خواهد شد.

ادامه دارد...


اشتراک‌ها
آموزش تایپ اسکریپت در کمتر از دو ساعت

00:00:00 Intro
00:04:40 Installation
00:13:11 Configs
00:21:46 Basics
00:26:57 Interface & Type
00:28:49 Optionals
00:36:40 Array & Tuple
00:41:05 Litteral Type & Enum
00:46:00 Null & Void
00:56:01 Any & Unknown
01:11:01 Extends
01:12:40 Interface vs Type
01:16:58 Never
01:28:50 Intersection
01:34:03 Generics
01:49:03 Conclusion 

آموزش تایپ اسکریپت در کمتر از دو ساعت
اشتراک‌ها
ساخت یک برنامه‌ی دخل و خرج با ASP.NET Core 6x

BEGINNERS Budget App .NET 6, ASP.NET C# MVC (FULL PROJECT) - YouTube

Chapters
0:00 - Intro
2:45 - Create Project
3:32 - Examine Files
5:56 - Cleaning Up
8:00 - Create Database
10:24 - Repository
14:50 - View Models
17:35 - List of Transactions
21:25 - Add Transactions
33:40 - Update Transactions
36:45 - Delete Transactions
39:20 - Categories List
43:00 - Insert Categories
46:00 - Update Categories
47:30 - Delete Categories
49:15 - Frontend Validation
49:50 - Transactions Filter
55:00 - Styling 

ساخت یک برنامه‌ی دخل و خرج با ASP.NET Core 6x
نظرات مطالب
بررسی خطاهای ممکن در حین راه اندازی اولیه برنامه‌های ASP.NET Core در IIS
یک نکته‌ی تکمیلی: دریافت خطای «HTTP Error 500.30 - ANCM In-Process Start Failure» پس از فعالسازی هاست درون پروسه‌ای از نگارش 2.2 به بعد
در نگارش فعلی این ماژول درون پروسه‌ای، متد Directory.GetCurrentDirectory بجای اینکه مسیر برنامه را باز گرداند، مسیر C:\windows\system32\inetsrv یا همان پروسه‌ی IIS را بر می‌گرداند و باید به این موضوع دقت داشت. برای مثال ممکن است مسیر بانک اطلاعاتی برنامه پیدا نشود و یا فایل تنظیمی قابل خواندن نباشد. چند مثال از این نوع: - ASP.NET Core Configuration Issue with In Process Hosting   

و یا کلا آن‌را در فایل csproj خاموش کنید (هر چند اگر SDK متناسبی را نصب کرده باشید (آخرین نگارش پایدار آن‌را)، این مشکل نباید وجود داشته باشد):
Change this section ...

<PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
  </PropertyGroup>

to the following ...

<PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
    <AspNetCoreModuleName>AspNetCoreModule</AspNetCoreModuleName>
  </PropertyGroup>
مطالب
کدامیک از بسته‌های NET Core. را باید دریافت کنیم؟
زمانیکه به صفحه‌ی دریافت نگارش‌های مختلف NET Core. مراجعه می‌کنیم، بسته‌های مختلفی از یک نگارش قابل مشاهده هستند و در بدو امر واضح نیست که کدامیک را باید دریافت کرد. در این مطلب تفاوت‌های بین این بسته‌ها را مرور خواهیم کرد.


کدام نگارش‌های NET Core. بر روی سیستم شما نصب هستند؟

پیش از انجام هرکاری نیاز است بررسی کنیم کدامیک از بسته‌های ارائه شده، بر روی سیستم جاری نصب هستند. برای انجام اینکار دستور زیر را در خط فرمان صادر کنید:
 dotnet --info
اگر این دستور کار نکرد و خطایی را دریافت کردید، یعنی NET Core. اصلا بر روی سیستم شما نصب نیست. برنامه dotnet.exe جزئی از runtime نصب شده‌است و به صورت خودکار به path سیستم اضافه می‌شود. به همین جهت است که در صورت نصب آن، فرمان dotnet در هر مسیری قابل اجرا است.
Runtime تنها ویژگی‌های اساسی جهت اجرای برنامه‌های از پیش کامپایل شده‌ی NET Core. را با اجرای فرمانی مانند dotnet mydll.dll و یا اجرای دستور dotnet --info برای دریافت اطلاعاتی از جزئیات این ویژگی‌ها، به همراه دارد. اما برای کار با سورس کدها، build، publish و هر کار دیگری با آن‌ها، حتما باید SDK نیز نصب شود.

خروجی فرمان فوق بر روی سیستم من چنین چیزی است:
 C:\Users\Vahid>dotnet --info
.NET Core SDK (reflecting any global.json):
 Version: 2.1.301
 Commit: 59524873d6

Runtime Environment:
 OS Name:   Windows
 OS Version:  10.0.17134
 OS Platform: Windows
 RID: win10-x64
 Base Path: C:\Program Files\dotnet\sdk\2.1.301\

Host (useful for support):
  Version: 2.1.1
  Commit:  6985b9f684

.NET Core SDKs installed:
  2.1.300 [C:\Program Files\dotnet\sdk]
  2.1.301 [C:\Program Files\dotnet\sdk]

.NET Core runtimes installed:
  Microsoft.NETCore.App 2.1.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]

To install additional .NET Core runtimes or SDKs:
  https://aka.ms/dotnet-download
اولین شماره نگارش نمایش داده شده‌ی در این لیست (2.1.301)، شماره نگارش SDK فعال است. سپس شماره نگارش 2.1.1 به معنای شماره نگارش Runtime فعال بر روی سیستم است که هاست dotnet.exe به شمار می‌رود. سپس لیست SDKها و Runtimeهای نصب شده‌ی بر روی سیستم را نمایش می‌دهد.
باید دقت داشت که بر روی یک سیستم می‌توان چندین SDK و چندین Runtime مختلف را نصب کرد و هر پروژه از شماره نگارش خاصی استفاده کند. شماره نگارش runtime استفاده شده‌ی در پروژه‌ها در فایل csproj، توسط مدخل زیر مشخص می‌شود:
 <TargetFramework>netcoreapp2.1</TargetFramework>
در مورد SDK اینطور نیست و همواره از آخرین SDK نصب شده (همان شماره نگارش SDK فعال فوق) استفاده می‌شود، مگر اینکه فایل ویژه‌ای به نام global.json را در ریشه‌ی اصلی solution قرار دهید؛ با این محتوای فرضی:
 {
  "sdk": {
        "version": "2.1.300-rc.31211"
  }
}
در این حالت پروژه‌ی جاری را وادار می‌کنید بجای استفاده‌ی از آخرین SDK نصب شده‌ی بر روی سیستم، از نگارش SDK خاصی استفاده کند.
البته در اکثر موارد نیازی به انجام این کار نیست؛ چون SDK، با تمام نگارش‌های قبلی سازگار است و همواره استفاده‌ی از آخرین SDK نصب شده توصیه می‌شود. به همین جهت فایل global.json را پس از ایجاد یک solution جدید مشاهده نمی‌کنید؛ مگر اینکه خودتان به دلایل خاصی آن‌را اضافه و مقید نمائید.


تفاوت بسته‌های مختلف قابل دریافت NET Core. در چیست؟

زمانیکه برای دریافت آخرین نگارش NET Core. به سایت آن مراجعه می‌کنیم، به ازای هر نگارش، یک چنین لیستی قابل مشاهده است:
• .NET Core Runtime
• .NET Core SDK
• .NET Core Hosting Bundle
• Visual Studio
• ASP.NET Core Installer
و اکنون سؤالی که مطرح می‌شود این است: کدامیک را باید دریافت کرد؟

Visual Studio

اگر کاربر ویندوز هستید، با نصب آخرین نگارش Visual Studio، می‌توانید به همراه آن، آخرین نگارش SDK ،runtime و اجزای هاست برنامه‌های ASP.NET Core بر روی IIS را نیز بر روی سیستم خود نصب کنید.


NET Core SDK.

هدف از ارائه‌ی بسته‌ی SDK، انجام فرآیندهای build‌، اجرا و مدیریت امور مرتبط با NET Core.، بدون استفاده از Visual Studio و بر روی تمام سیستم عامل‌های پشتیبانی شده‌است. زمانیکه یک بسته‌ی SDK را نصب می‌کنید، به همراه آن این موارد نیز نصب می‌شوند:
• .NET Core SDK 
• .NET Core Runtime 
• ASP.NET Core Runtime
به همین جهت حجم آن از بسته‌ی تکی runtime بیشتر است و با نصب آن دیگر نیازی به دریافت مجزای بسته‌های runtime نیست.

بنابراین دلیل نصب آن می‌تواند شامل یکی از موارد زیر باشد:
 - بر روی سیستمی که در حال توسعه‌ی برنامه‌های مبتنی بر NET Core. هستید. این تمام چیزی است که به آن نیاز دارید.
 - بر روی سروری که نیاز است دستور dotnet را برای انجام فرآیندهای build/publish اجرا کند.


NET Core Runtime.

بسته‌های Runtimes، کوچکترین بسته‌ی ممکن در این لیست هستند و هدف از آن‌ها صرفا اجرای برنامه‌های کامپایل شده‌ی NET Core. در سکوهای کاری مختلف پشتیبانی شده‌ی توسط آن است.
باید دقت داشت که اگر برنامه‌ی شما از «ASP.NET Core meta package» استفاده می‌کند، این بسته در runtime لحاظ نشده‌است و در یک چنین حالتی باید بسته‌ی ASP.NET Core را به صورت جداگانه دریافت و نصب کنید. هرچند اگر از این متاپکیج‌ها استفاده نکنید و بسته‌های مورد نیاز را به صورت مستقیم به برنامه‌ی خود اضافه کنید، این بسته‌ها جزئی از فایل‌های publish نهایی بوده و در این حالت برنامه توسط بسته‌ی runtime نیز قابل اجرا است.
در این حالت برنامه‌ی dotnet بجز اجرای برنامه‌ها و ارائه‌ی اطلاعاتی در مورد خود آن، کارهای دیگری را مانند build و یا publish، نمی‌تواند انجام دهد و برنامه در این حالت باید کاملا از پیش کامپایل شده باشد.

بنابراین دلیل نصب آن می‌تواند شامل یکی از موارد زیر باشد:
- برای اجرای برنامه‌های از پیش کامپایل شده‌ای که به همراه تمام وابستگی‌های مورد نیاز هم هستند.
- برای اجرای برنامه‌های وبی که از ASP.NET Meta packages استفاده نمی‌کنند


ASP.NET Core Installer

همانطور که در توضیحات بسته‌ی runtime عنوان شد، این بسته، متاپکیج‌های ASP.NET Core را به همراه ندارد. اگر به آن‌ها نیاز دارید، باید آن‌ها را به صورت جداگانه توسط ASP.NET Core installer نصب کنید که شامل این موارد است:
- The ASP.NET Runtime Meta Packages
- Microsoft.AspNetCore.App
- Microsoft.AspNetCore.All
 
NET Core Windows Hosting Pack.

نصب این بسته برای هاست برنامه‌های ASP.NET Core در ویندوز و بر روی IIS ضروری است و شامل این اجزا می‌شود:
- 32 bit and 64 .NET Core Runtimes
- ASP.NET Runtime Packages (Microsoft.AspNetCode.App/All)
- IIS Hosting Components
بنابراین این بسته شامل تمام موارد یاد شده‌است منهای قابلیت‌های SDK برای build و publish برنامه‌ها.



بنابراین به صورت خلاصه

برای سرورها این موارد را نصب کنید:
- در ویندوز: Windows Server Hosting Bundle
- برای Mac و لینوکس:  .NET Core Runtime + ASP.NET Core Runtimes

برای سیستم توسعه‌ی شخصی این موارد را نصب کنید:
- SDK
- اگر از ویندوز استفاده می‌کنید: Visual Studio هم به همراه SDK نصب می‌شود.

برای اجرای برنامه‌های از پیش کامپایل شده که به همراه تمام وابستگی‌های مورد نیاز هم هستند:
- تنها Runtime را نصب کنید.
اگر این برنامه‌ی از پیش کامپایل شده از ASP.NET Runtime Meta packages استفاده می‌کند:
- ASP.NET Runtimes را نیز نصب کنید.
نظرات مطالب
توسعه برنامه های Cross Platform با Xamarin Forms & Bit Framework - قسمت سوم
بله. Intellitrace با Edit & Continue قسمت CSharp (و نه XAML) مشکل داره. البته این توی آپدیت بعدی ویژوال استدیو حل شده. Intellitrace خودش خیلی امکان مهم و جالبی هست. هم Network monitor هست که مثلا Http Client داره از کلاینت چه Request هایی می‌زنه و هم کلی امکانات خفن دیگه.

در مورد Suppress JIT ... هم مسئله اینه که الآن کتابخونه‌های .NET ای دارن موقعی که Nuget package می‌سازن، سورس کدهاشون رو هم داخلشون embed می‌کنن تا موقع دیباگ بدون استفاده از چیزی مثل Resharper بتونید توی کدهاشون Step in کنید با F11 و داخل کدهای اون‌ها رو هم اگه خواستید دیباگ کنید که خیلی فوق العاده است. این امکان به صورت پیش فرض روی پروژه‌های DotNetCore کار می‌کنه، اما روی FullDotNetFramework و UWP (با این که پایه UWP هم DotNetCore هست) باید این Suppress JIT ... رو بزنید تا این امکان به درستی کار کنه. در واقع با غیر فعال سازی Optimization‌های JIT برای کدها در زمان Debug باعث از بین نرفتن این قابلیت دیباگ کتابخونه‌های 3rd party می‌شوید. این مورد بر خلاف مورد قبلی اصلا حالت هزینه به فایده نداره و فقط فایده است و انجام اش بدید. فقط هم برای دیباگ هست و ربطی به سرعت اجرای برنامه در محیط مشتری نداره.
مطالب
اتصال SQL Server به MySQL

اگر SQL Server و MySQL بر روی سیستم شما نصب است، روشی ساده برای انتقال اطلاعات بین این دو وجود دارد که نیازی به دخالت هیچ نوع برنامه‌ی جانبی نداشته و با امکانات موجود قابل مدیریت است.

ایجاد یک Linked server

برای اینکه SQL Server را به MySQL متصل کنیم می‌توان بین این دو یک Linked server تعریف کرد و سپس دسترسی به بانک‌های اطلاعاتی MySQL همانند یک بانک اطلاعاتی محلی SQL Server خواهد شد که شرح آن در ادامه ذکر می‌شود.
ابتدا نیاز است تا درایور ODBC مربوط به MySQL دریافت و نصب شود. آن‌را می‌توانید از اینجا دریافت کنید : (+)
سپس management studio را گشوده و در قسمت Server objects ، بر روی گزینه‌ی Linked servers کلیک راست نمائید. از منوی ظاهر شده، گزینه‌ی New linked server را انتخاب کنید:


در ادامه، باید تنظیمات زیر را در صفحه‌ی باز شده وارد کرد:


در قسمت Linked server و Product name ، نام دلخواهی را وارد کنید.
Provider انتخابی باید از نوع Microsoft OLE DB Provider for ODBC Drivers باشد.
مهم‌ترین تنظیم آن، قسمت Provider string است که باید به صورت زیر وارد شود (در غیر اینصورت کار نمی‌کند):
DRIVER={MySQL ODBC 5.1 Driver}; SERVER=localhost; DATABASE=testdb; USER=root; PASSWORD=mypass; OPTION=3;PORT=3306; CharSet=UTF8;
در اینجا نام دیتابیس پیش فرض، نام کاربری اتصال به MySQL و Password و غیره را می‌توان تنظیم کرد.
پس از انجام این تنظیمات بر روی دکمه‌ی Ok کلیک کنید تا Linked server ساخته شود:


اگر لیست بانک‌های اطلاعاتی را مشاهده نمودید، یعنی اتصال به درستی برقرار شده است.

تنظیمات ثانویه:

تا اینجا اس کیوال سرور به MySQL متصل شده است، اما برای استفاده بهینه از امکانات موجود نیاز است تا یک سری تغییرات دیگر را هم اعمال کرد.

تنظیم MSDASQL Provider :
در همان قسمت Linked provider ، ذیل قسمت Providers ، گزینه‌ی MSDASQL را انتخاب کرده و بر روی آن کلیک راست نمائید. سپس صفحه‌ی خواص آن‌را انتخاب کنید تا بتوان تنظیمات زیر را به آن اعمال کرد. این پروایدر جهت اتصال به MySQL مورد استفاده قرار می‌گیرد.



فعال سازی RPC :

برای اینکه بتوان از طریق SQL Server رکوردی را در یکی از جداول بانک‌های اطلاعاتی MySQL متصل شده ثبت نمود، می‌توان از دستور زیر استفاده کرد:
EXECUTE('insert into testdb.testtable(f1,f1) values(1,''data'')') at mysql

اینجا testdb نام بانک اطلاعاتی اتصالی MySQL است و testTable هم نام جدول مورد نظر. MySQL ایی که در آخر عبارت ذکر شده همان نام linked server ایی است که پیشتر تعریف کردیم.
به محض سعی در اجرای این کوئری خطای زیر ظاهر می‌شود:
Server 'mysql' is not configured for RPC.

برای رفع این مشکل، مجددا به صفحه‌ی خواص همان liked server ایجاد شده مراجعه کنید. در قسمت Server options دو گزینه مرتبط به RPC باید فعال شوند:



و اکنون برای کوئری گرفتن از اطلاعات ثبت شده هم از عبارت زیر می‌توان استفاده کرد:
SELECT * FROM OPENQUERY(mysql, 'SELECT * FROM testdb.testtable')

در این کوئری، MySQL نام Linked server ثبت شده است و testdb هم یکی از بانک‌های اطلاعاتی MySQL مورد نظر.


انتقال تمام اطلاعات یک جدول از بانک اطلاعاتی MySQL به SQL Server

پس از برقراری اتصال، اکنون import کامل یک جدول MySQL به SQL Server به سادگی اجرای کوئری زیر می‌باشد:
SELECT * INTO MyDb.dbo.testtable FROM openquery(MYSQL, 'SELECT * FROM testdb.testtable')

در این کوئری، MySQL همان Linked server تعریف شده است. MyDB نام بانک اطلاعاتی موجود در SQL Server جاری است و testtable هم جدولی است که قرار است اطلاعات testdb.testtable بانک اطلاعاتی MySQL به آن وارد شود.

با اطلاعات فارسی هم (در سمت SQL Server) مشکلی ندارد. همانطور که مشخص است، در اطلاعات provider string ذکر شده‌، مقدار charset به utf8 تنظیم شده و همچنین اگر نوع collation فیلدهای تعریف شده در MySQL نیز به utf8_persian_ci تنظیم شده باشد، با مشکل ثبت اطلاعات فارسی به صورت ???? مواجه نخواهید شد.


نکته:
اگر بانک اطلاعاتی MySQL شما بر روی local host نصب نیست، جهت فعال سازی دسترسی ریموت به آن، می‌توان به یکی از نکات زیر مراجعه کرد و سپس این اطلاعات جدید باید در همان قسمت provider string مرتبط با تعریف linked server وارد شوند:


مطالب مشابه:

هیچکدام از این روش‌ها قابل استفاده نبودند چون provider string صحیحی را نهایتا تولید نمی‌کنند. همچنین تمام این روش‌ها مبتنی است بر ایجاد DSN در کنترل پنل که اصلا نیازی به‌ آن نیست و اضافی است.

اشتراک‌ها
کتابخانه sysend.js
sysend.js is small library that allow to send message between pages that are open in the same browser. They need to be in same domain. The library don't use any dependencies and use HTML5 LocalStorage API. You can send any object that can be serialized to JSON or just send empty notification.  Demo

Tested on GNU/Linux in Chromium 34, FireFox 29, Opera 12.16 (64bit) 
کتابخانه sysend.js