مطالب
توسعه برنامه های Cross Platform با Xamarin Forms & Bit Framework - قسمت سوم
در قسمت قبل، محیط توسعه نرم افزار مد نظرمان را ایجاد کردیم و توانستیم پروژه پیش فرض Xamarin forms را بیلد کنیم. حالا قصد داریم تا یک مثال ساده را با هم بررسی کنیم و آن را بر روی ویندوز تست کنیم. در قسمت بعدی نیز همین مثال ساده را بر روی Android و در قسمت بعدتر نیز بر روی iOS تست می‌کنیم. پس از اطمینان از اینکه امکان تست برنامه را بر روی هر سه پلتفرم یافته‌اید، بر روی آموزش موارد بیشتری از Xamarin Forms تمرکز می‌کنیم.
برای شروع، Xaml Live را از Visual Studio Marketplace دانلود کنید.
سپس در صورتیکه ابزار git را ندارید، آن را نیز دانلود و نصب کنید و بعد دستور git clone https://github.com/ysmoradi/XamApp را در Command line وارد کنید. دقت کنید پروژه را در جایی Clone نکنید که مسیر فولدر آن طولانی شود. این پروژه، یک پروژه آماده برای تست و تغییر است و ما برای بررسی نحوه اجرا آن در UWP - Android - iOS‌ از آن استفاده می‌کنیم. خود کدها در جلسات بعدتر بررسی خواهند شد. تنها چیزی که الآن اهمیت دارد اطمینان از راه اندازی شدن محیط توسعه نرم افزار شما به بهترین شکل ممکن است.
   
با ساختار پروژه و جزئیات آن در گذر زمان بیشتر آشنا می‌شویم، ولی به صورت کلی این Solution دارای 4 پروژه است:
XamApp - XamApp.Android - XamApp.iOS - XamApp.UWP
- در XamApp، تقریبا تمامی پروژه نوشته می‌شود. این مورد شامل منطق برنامه است که با CSharp نوشته می‌شود و ظاهر برنامه که با XAML نوشته می‌شود. اگر چه که می‌توان ظاهر برنامه را نیز با CSharp زد، انجام این کار به هیچ وجه توصیه نمی‌شود. در مورد Xaml نیز بعد از راه اندازی این مثال در Windows-Android-iOS صحبت خواهیم کرد.
- XamApp.Android پروژه‌ای است که اگر Set as start up project شود، به شما اجازه تست کدهای داخل XamApp را بر روی گوشی یا Emulator‌های اندرویدی می‌دهد. همچنین از طریق این پروژه می‌توانید پابلیش apk را بگیرید و به امکانات پایه Android مانند Activity - Fragment - Android XML - Intent و ... در همان زبان CSharp دسترسی داشته باشید. استفاده از این پروژه، مطلب قسمت بعدی این دوره آموزشی است.
- XamApp.iOS پروژه‌ای است که اگر Set as start up project شود، به شما اجازه تست کدهای داخل XamApp را بر روی گوشی یا Emulator‌های iOS ای می‌دهد. همچنین از طریق این پروژه می‌توانید پابلیش ipa را بگیرید و به امکانات پایه iOS مانند Delegate - Storyboard و ... در همان زبان CSharp دسترسی داشته باشید. در آموزش بعد از آموزش Android، به iOS نیز خواهیم پرداخت.
- XamApp.UWP پروژه‌ای است که اگر Set as start up project شود، به شما اجازه تست کدهای داخل XamApp را بر روی ویندوز خودتان می‌دهد. همچنین از طریق این پروژه می‌توانید پابلیش appxbundle یا msi را بگیرید و به امکانات پایه Windows در همان زبان CSharp دسترسی داشته باشید. UWP مخفف Universal windows platform است.

برای شروع پروژه‌های XamApp.Android و XamApp.iOS را Unload کنید، زیرا در این قسمت با آنها کاری نداریم. همچنین پروژه XamApp.UWP را Set as start up project کنید. 
فقط برای یکبار از منوی Tools در ویژوال استدیو، Options را باز کنید و در قسمت جستجوی آن، عبارت Intellitrace را بنویسید و اگر چیزی پیدا شد، تیک Enable Intellitrace را بردارید تا غیر فعال شود. همچنین مجدد Suppress JIT optimization را جستجو کنید و تیک آن را بزنید تا فعال شود.
دکمه F5 را بزنید و برنامه را اجرا کنید. یک دکمه می‌بینید که Text آن عبارت + است. اگر بر روی آن کلیک کنید، می‌بینید که متن Label بالای آن می‌شود Button tapped 1 time
فایل HelloWorldView.xaml را در فولدر Views در پروژه XamApp، پیدا کنید و نگاهی به کد Label و Button بیاندازید که درون StackLayout هستند و StackLayout خود داخل ContentPage است که در نتیجه صفحه اول ما کل فضایی را که دارد، به StackLayout اختصاص یافته‌است.
    <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
        <Label Text="{Binding StepsCount, StringFormat='{}Button tapped {0} times!'}" />
        <Button Command="{Binding IncreaseStepsCountCommand}" Text="+" />
    </StackLayout>
StackLayout نوعی Layout ساده‌است که بسته به تنظیم Orientation اش، می‌تواند Vertical یا Horizontal باشد. آیتم‌های داخل‌اش را که در این مثال Label و Button هستند، یا عمودی یا افقی می‌چیند که پیش فرض‌اش عمودی است. Horizontal Options و Vertical Options اش هم که هر دو Center هستند باعث می‌شود آیتم‌ها دقیقا در وسط StackLayout بنشینند. چون تمامی فضای ContentPage به StackLayout اختصاص یافته‌است، عملا Label و Button در وسط برنامه ظاهر می‌شوند.
Label ما دارای Text ای است که به StepsCount متصل یا Bind شده است ( به کمک Binding StepsCount).
StepsCount عددی است که در ابتدا صفر است و با کلیک دکمه، مقدار آن یکی یکی افزایش می‌یابد. این کد در HelloWorldViewModel.cs و به زبان CSharp نوشته شده‌است. StringFormat نیز در اینجا عملکردی معادل StringFormat در CSharp را دارد.
‌Button دارای Command ای است که به متدی در CSharp به نام IncreaseStepsCount متصل شده‌است.
حال نگاهی به کد CSharp بیندازیم:
    public class HelloWorldViewModel : BitViewModelBase
    {
        public int StepsCount { get; set; }

        public BitDelegateCommand IncreaseStepsCountCommand { get; set; }

        public HelloWorldViewModel()
        {
            IncreaseStepsCountCommand = new BitDelegateCommand(IncreaseStepsCount);
        }

        async Task IncreaseStepsCount()
        {
            StepsCount += 1;
        }
    }
ظاهر برنامه در فایل Xaml ای نوشته شده به نام HelloWorldView.xaml و منطق برنامه در کلاسی است به نام HelloWorldViewModel که این View و ViewModel انتهای نام این دو، از معماری MVVM یا Model - View - View Model می‌آید که در سال 2006~2007 و با معرفی WPF کم کم معروف شد و ما نیز از آن در این مثال داریم استفاده می‌کنیم.
StepsCount که در View به Text آن Label وصل شده بود، در CSharp یک Property از جنس int است. Command ما با نام IncreaseStepsCountCommand به متدی وصل شده‌است که کارش اضافه کردن یکی یکی StepsCount است.

در حالت عادی اگر بخواهید این برنامه را تغییر دهید که مثلا به جای یکی یکی بالا بردن StepsCount، آن را یکی یکی کم کند، ابتدا برنامه را Stop می‌کنید و سپس در Xaml برای Button مربوطه، Text را از + به - تغییر می‌دهید. همچنین کد CSharp را نیز عوض می‌کنید که می‌شود:
StepsCount -= 1
و مجددا F5 را می‌زنید. این روش قطعا خیلی Productive نیست و زمان زیادی را از شما می‌گیرد. شما می‌توانید با Break کردن اجرای برنامه به تغییر کدهای CSharp بپردازید. همچنین بدون Break کردن می‌توانید کدهای Xaml را تغییر دهید و به این روی، خیلی سریع‌تر پروژه را پیش ببرید.
با مشاهده این ویدئو می توانید درک بهتری از عملکرد Edit & continue داشته باشید. دقت کنید که در زمان تغییر ظاهر و منطق، اگر مثلا عدد، تا 17 افزایش داده شده بود برای تست، روی 17 می‌ماند و به صفر بر نمی‌گردد. در واقع کل برنامه Reload نمی‌شود و این تفاوت Edit & continue با Hot reload موجود در سایر ابزارهاست.

همچنین با کوچک و بزرگ کردن برنامه اجرا شده به سایز گوشی‌ها و تبلت‌های مختلف عملا می‌توانید برنامه را در سایزهای مختلف تست کنید. توجه داشته باشید که در Xamarin Forms مقدار دهی به طول و عرض و ... در تمامی پلتفرم‌ها و Device‌ها فارغ از Resolution یکسان است و در همه جا Width=64 عملا به یک سانتی متر اشاره دارد. علاوه بر این بدون اینکه صفحه مانیتور شما Touch باشد، می‌تواند حتی Touch را نیز تست کنید که برای این کار می‌توانید از Simulator استفاده کنید. به این صورت که به جای Local Machine گزینه Simulator را انتخاب می‌کنید.
 

 

برای پابلیش پروژه نیز می‌توانید از آموزش‌های بر روی وب استفاده کنید که شامل ارائه برنامه به استفاده کنندگان با یا بدون Microsoft Store است که از فرمت نه چندان جالب appxbundle استفاده می‌کند و ما از این آموزش عبور می‌کنیم و به ذکر این نکته بسنده می‌کنیم که نسخه بعدی Visual Studio 2017 یعنی 15.9 قابلیت ساختن msix یا Windows installer را نیز دارد که از هر چیزی بهتر است و برای پابلیش بهتر است تا ارائه نسخه Stable بعدی ویژوال استودیو که احتمالا در طی کمتر از یک ماه دیگر ارائه می‌شود، صبر کنید. دقت کنید علاوه بر کامپیوتر، لپ تاپ و تبلت‌های ویندوزی، برنامه‌ی شما بر روی XBox نیز می‌تواند کار کند.

در قسمت بعدی، همین پروژه را بر روی Android نیز اجرا می‌کنیم.

مطالب
Pipeها در Angular 2 – قسمت سوم – Pipeهای Pure و Impure
 در قسمت قبل بیان شد که Angular برای اعمال Pipe بر روی Template expressions باید تمامی رخدادهای برنامه را تحت نظر قرار داده و با مشاهده‌ی هر تغییری بر روی عبارت ورودی Pipe، فراخوانی Pipe را آغاز کند. از جمله این رخدادها می‌توان به رخدادهای mouse move، timer tick، server response و فشرده شدن کلیدهای ماوس و یا کیبورد اشاره کرد. واضح است که بررسی تغییرات عبارت در این همه رخداد می‌تواند مخرب باشد و بر روی کارآئی (Performance) تاثیر منفی خواهد گذاشت. اما Angular برای حل این مشکل و همچنین هنگام مشاهده سریع تغییرات هنگام استفاده از Pipeها، الگوریتم‌های سریع و ساده‌ای در نظر گرفته است که آن‌ها را در این بخش مورد برسی قرار خواهیم داد.


Pipeهای Pure و Impure

Pipeها کلا در دو دسته‌ی Pure و Impure قرار می‌گیرند. هنگام ساخت Pipe سفارشی در صورتیکه نوع Pipe مشخص نشود، به صورت پیش فرض از نوع Pure خواهد بود. برای تعریف Pipeهایی از نوع Impure کافی است در متادیتای Pipe@، پرچم Pure را به مقدار false تنظیم کنید.
@Pipe({ name: 'impurePipe', pure: false })
تفاوت این Pipeها در زمان فراخوانی دوباره آنها است.


Pure Pipe

این نوع Pipeها تنها زمانی فراخوانی مجدد می‌شوند که یک تغییر محض (Pure Change) بر روی عبارت ورودی آنها رخ دهد. هر نوع تغییری بر روی عبارات ورودی از جنس string ، number ، Boolean ، Symbol و عبارات اولیه، یا هرنوع تغییری در ارجاع یک شیء مانند  Date ، Array ، Function و Object نیز تغییر محض محسوب می‌شود. به عنوان مثال هیچکدام از تغییرات زیر یک تغییر محض محسوب نمی‌شوند:
numbers.push(10);
obj.name = ‘javad’;
زیرا با اضافه شدن عنصری به یک آرایه یا تغییر خصوصیتی از یک شیء، باعث تغییری در ارجاع آنها نمی‌شود و همانطور که اشاره شد، در عبارات از نوع آرایه و Object، فقط تغییر در ارجاع آن‌ها یک تغییر محض محسوب می‌شود.
حالا می‌توان به این نتیجه رسید که اضافه شدن مقداری به آرایه یا به‌روزرسانی یک property از object، باعث فراخوانی مجدد Pure Pipe نخواهد نشد. شاید این نوع از Pipeها محدود کننده باشند، اما بسیار سریع هستند (برسی تغییر در ارجاع یک شیء بسیار سریعتر از بررسی کامل یک شیء، صورت می‌گیرد).


Impure Pipe

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

مثال: قصد داریم Pipe سفارشی را پیاده سازی کنیم تا آرایه‌ای از اعداد را دریافت و فقط اعداد زوج را فیلتر کرده و نمایش دهد.
برای این منظور یک فایل جدید را با نام even-numbers.pipe.ts با محتویات زیر ایجاد می‌کنیم: 
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'evenNumbers'
})
export class EvenNumbersPipe implements PipeTransform {
  transform(numbers: Array<number>): Array<number> {
    var x=numbers.filter(r => r % 2 == 0);
    return x;
  }
}
همانطور که مشخص است این Pipe در متد transform، آرایه‌ای از اعداد را دریافت کرده و فقط اعداد زوج را بازگشت می‌دهد. حالا باید Pipe تعریف شده خود را در AppModule در قسمت declares تعریف کنیم.
// . . .
import { EvenNumbersPipe } from './pipes/even-numbers.pipe'
@NgModule({
  declarations: [
    . . .
    EvenNumbersPipe
  ],
 . . .
})
export class AppModule { }

سپس در کامپوننت مورد نظر خود متغیری را به نام numbers از نوع آرایه، با مقدار اولیه‌ی اعداد از یک تا ده، تعریف می‌کنیم:
numbers: Array<number> = [1,2,3,4,5,6,7,8,9,10];
برای نمایش این اعداد در رابط کاربری تگ‌های زیر را به قالب کامپوننت خود اضافه می‌کنیم:
<h1>All numbers</h1>
<span *ngFor="let number of numbers">
  {{number}}
</span>
همچنین با استفاده از تگ‌های زیر یک input برای اضافه کردن مقدار جدید به آرایه درنظر می‌گیریم:
<p>
  <input type="text" #number />
  <input type="button" (click)="numbers.push(number.value)" value="Add number"/>
</p>

تگ‌های زیر را نیز برای اعمال Pipe نمایش اعداد زوج، به قالب کامپوننت اضافه می‌کنیم:
<h1>even numbers</h1>
<span *ngFor="let number of numbers | evenNumbers">
  {{number}}
</span>
بعد از اجرای برنامه، یک عدد جدید زوج را به آرایه اضافه کنید. متوجه خواهید شد با اینکه لیست اعداد در قسمت All numbers به‌روز می‌شوند، ولی Pipe، متوجه تغییری بر روی آرایه نشده‌است و همچنان اعداد قبلی را نمایش می‌دهد. دلیل این امر همانطور که قبلا نیز اشاره شد، بخاطر Pure بودن Pipe و عدم فراخوانی مجدد این نوع Pipe‌ها در زمان اضافه شدن مقداری به آرایه یا تغییری در خصوصیت یک شیء است.

برای حل این مشکل، هنگام اضافه شدن عدد به آرایه، اگر ارجاع آرایه را تغییر دهیم، Pure Pipe متوجه تغییرات خواهد شد و لیست اعداد را به‌روز رسانی می‌کند (تغییر در ارجاع یک شیء، از نوع تغییرات محض است):
<p>
  <input type="text" #number />
  <input type="button" (click)="numbers = numbers.concat(number.value)" value="Add number"/>
</p>
با تغییر نحوه اضافه شدن عنصر به آرایه به شکل بالا خواهیم دید که با افزودن اعداد جدید، لیست اعداد زوج نیز در لحظه اعمال خواهند شد. این راه‌حل همیشه کارآمد نخواهد بود. همیشه تشخیص محل اضافه شدن عنصر به آرایه در برنامه کار ساده‌ای نیست تا در آنجا ارجاع آرایه را نیز تغییر دهیم. راه‌حل، استفاده از Impure Pipe است. کافی است متادیتای Pipe@ را هنگام تعریف به شکل زیر تغییر دهید:
@Pipe({
  name: 'evenNumbers',
  pure: false
})
export class EvenNumbersPipe implements PipeTransform {
   //…
}

کسانیکه با Angular 1.x آشنایی دارند، شاید اکنون متوجه این شده‌اند که چرا در Angular به مشابه Angular 1.x دیگر خبری ازfilter و orderBy نیست. با توجه به اینکه این دو فیلتر فقط با عبارات از نوع object سروکار داشتند، پیاده‌سازی آنها فقط با Impure Pipeها امکان پذیر بود و با توجه به اینکه Impure Pipeها در هر بار چرخه تغییرات کامپوننت اجرا خواهند شد، باعث کندی در صفحات خواهند شد. 
مطالب
استفاده از چندین بانک اطلاعاتی به صورت همزمان در EF Code First
یکی از روش‌های تهیه‌ی برنامه‌های چند مستاجری، ایجاد بانک‌های اطلاعاتی مستقلی به ازای هر مشتری است؛ یا نمونه‌ی دیگر آن، برنامه‌هایی هستند که اطلاعات هر سال را در یک بانک اطلاعاتی جداگانه نگه‌داری می‌کنند. در ادامه قصد داریم، نحوه‌ی کار با این بانک‌های اطلاعاتی را به صورت همزمان، توسط EF Code first و در حالت استفاده از الگوی واحد کار و تزریق وابستگی‌ها، به همراه فعال سازی خودکار مباحث migrations و به روز رسانی ساختار تمام بانک‌های اطلاعاتی مورد استفاده، بررسی کنیم.


مشخص سازی رشته‌های متفاوت اتصالی

فرض کنید برنامه‌ی جاری شما قرار است از دو بانک اطلاعاتی مشخص استفاده کند که تعاریف رشته‌های اتصالی آن‌ها در وب کانفیگ به صورت ذیل مشخص شده‌اند:
  <connectionStrings>
    <clear />
    <add name="Sample07Context" connectionString="Data Source=(local);Initial Catalog=TestDbIoC;Integrated Security = true" providerName="System.Data.SqlClient" />
    <add name="Database2012" connectionString="Data Source=(local);Initial Catalog=testdb2012;Integrated Security = true" providerName="System.Data.SqlClient" />
  </connectionStrings>
البته، ذکر این مورد کاملا اختیاری است و می‌توان رشته‌های اتصالی را به صورت پویا نیز در زمان اجرا مشخص و مقدار دهی کرد.


تغییر Context برنامه جهت پذیرش رشته‌های اتصالی پویای قابل تغییر در زمان اجرا

اکنون که قرار است کاربران در حین ورود به برنامه، بانک اطلاعاتی مدنظر خود را انتخاب کنند و یا سیستم قرار است به ازای کاربری خاص، رشته‌ی اتصالی خاص او را به Context ارسال کند، نیاز است Context برنامه را به صورت ذیل تغییر دهیم:
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using EF_Sample07.DomainClasses;
 
namespace EF_Sample07.DataLayer.Context
{
    public class Sample07Context : DbContext, IUnitOfWork
    {
        public DbSet<Category> Categories { set; get; }
        public DbSet<Product> Products { set; get; }
 
        /// <summary>
        /// It looks for a connection string named Sample07Context in the web.config file.
        /// </summary>
        public Sample07Context()
            : base("Sample07Context")
        {
        }
 
        /// <summary>
        /// To change the connection string at runtime. See the SmObjectFactory class for more info.
        /// </summary>
        public Sample07Context(string connectionString)
            : base(connectionString)
        {
            //Note: defaultConnectionFactory in the web.config file should be set.
        }
 
 
        public void SetConnectionString(string connectionString)
        {
            this.Database.Connection.ConnectionString = connectionString;
        }
    }
}
در اینجا دو متد سازنده را مشاهده می‌کنید. سازنده‌ی پیش فرض، از رشته‌ای اتصالی با نامی مساوی Sample07Context استفاده می‌کند و سازنده‌ی دوم، امکان پذیرش یک رشته‌ی اتصالی پویا را دارد. مقدار پارامتر ورودی آن می‌تواند نام رشته‌ی اتصالی و یا حتی مقدار کامل رشته‌ی اتصالی باشد. حالت پذیرش نام رشته‌ی اتصالی زمانی مفید است که همانند مثال ابتدای بحث، این نام‌ها را پیشتر در فایل کانفیگ برنامه ثبت کرده باشید و حالت پذیرش مقدار کامل رشته‌ی اتصالی، جهت مقدار دهی پویای آن بدون نیاز به ثبت اطلاعاتی در فایل کانفیگ برنامه مفید است.

یک متد دیگر هم در اینجا در انتهای کلاس به نام SetConnectionString تعریف شده‌است. از این متد در حین ورود کاربر به سایت می‌توان استفاده کرد. برای مثال حداقل دو نوع طراحی را می‌توان درنظر گرفت:
الف) کاربر با برنامه‌ای کار می‌کند که به ازای سال‌های مختلف، بانک‌های اطلاعاتی مختلفی دارد و در ابتدای ورود، یک drop down انتخاب سال کاری برای او درنظر گرفته شده‌است (علاوه بر سایر ورودی‌های استانداردی مانند نام کاربری و کلمه‌ی عبور). در این حالت بهتر است متد SetConnectionString نام رشته‌ی اتصالی را بر اساس سال انتخابی، در حین لاگین دریافت کند که اطلاعات آن در فایل کانفیگ سایت پیشتر مشخص شده‌است.
ب) کاربر یا مشتری پس از ورود به سایت، نیاز است صرفا از بانک اطلاعاتی خاص خودش استفاده کند. بنابراین اطلاعات تعریف کاربران و مشتری‌ها در یک بانک اطلاعاتی مجزا قرار دارند و پس از لاگین، نیاز است رشته‌ی اتصالی او به صورت پویا از بانک اطلاعاتی خوانده شده و سپس توسط متد SetConnectionString تنظیم گردد.


مدیریت سشن‌های رشته‌ی اتصالی جاری

پس از اینکه کاربر، در حین ورود مشخص کرد که از چه بانک اطلاعاتی قرار است استفاده کند و یا اینکه برنامه بر اساس اطلاعات ثبت شده‌ی او تصمیم‌گیری کرد که باید از کدام رشته‌ی اتصالی استفاده کند، نگهداری این رشته‌ی اتصالی نیاز به سشن دارد تا به ازای هر کاربر متصل به سایت منحصربفرد باشد. در مورد مدیریت سشن‌ها در برنامه‌های وب، از نکات مطرح شده‌ی در مطلب «مدیریت سشن‌ها در برنامه‌های وب به کمک تزریق وابستگی‌ها» استفاده خواهیم کرد:
using System;
using System.Threading;
using System.Web;
using EF_Sample07.DataLayer.Context;
using EF_Sample07.ServiceLayer;
using StructureMap;
using StructureMap.Web;
using StructureMap.Web.Pipeline;
 
namespace EF_Sample07.IoCConfig
{
    public static class SmObjectFactory
    {
        private static readonly Lazy<Container> _containerBuilder =
            new Lazy<Container>(defaultContainer, LazyThreadSafetyMode.ExecutionAndPublication);
 
        public static IContainer Container
        {
            get { return _containerBuilder.Value; }
        }
 
        public static void HttpContextDisposeAndClearAll()
        {
            HttpContextLifecycle.DisposeAndClearAll();
        }
 
        private static Container defaultContainer()
        {
            return new Container(ioc =>
            {
                // session manager setup
                ioc.For<ISessionProvider>().Use<DefaultWebSessionProvider>();
                ioc.For<HttpSessionStateBase>()
                   .Use(ctx => new HttpSessionStateWrapper(HttpContext.Current.Session));
 
                ioc.For<IUnitOfWork>()
                   .HybridHttpOrThreadLocalScoped()
                   .Use<Sample07Context>()
                    // Remove these 2 lines if you want to use a connection string named Sample07Context, defined in the web.config file.
                   .Ctor<string>("connectionString")
                   .Is(ctx => getCurrentConnectionString(ctx));
 
                ioc.For<ICategoryService>().Use<EfCategoryService>();
                ioc.For<IProductService>().Use<EfProductService>();
 
                ioc.For<ICategoryService>().Use<EfCategoryService>();
                ioc.For<IProductService>().Use<EfProductService>();
 
                ioc.Policies.SetAllProperties(properties =>
                {
                    properties.OfType<IUnitOfWork>();
                    properties.OfType<ICategoryService>();
                    properties.OfType<IProductService>();
                    properties.OfType<ISessionProvider>();
                });
            });
        }
 
        private static string getCurrentConnectionString(IContext ctx)
        {
            if (HttpContext.Current != null)
            {
                // this is a web application
                var sessionProvider = ctx.GetInstance<ISessionProvider>();
                var connectionString = sessionProvider.Get<string>("CurrentConnectionString");
                if (string.IsNullOrWhiteSpace(connectionString))
                {
                    // It's a default connectionString.
                    connectionString = "Database2012";
                    // this session value should be set during the login phase
                    sessionProvider.Store("CurrentConnectionStringName", connectionString);
                }
 
                return connectionString;
            }
            else
            {
                // this is a desktop application, so you can store this value in a global static variable.
                return "Database2012";
            }
        }
    }
}
در اینجا نحوه‌ی پویا سازی تامین رشته‌ی اتصالی را مشاهده می‌کنید. در مورد اینترفیس ISessionProvider و کلاس پایه HttpSessionStateBase پیشتر در مطلب «مدیریت سشن‌ها در برنامه‌های وب به کمک تزریق وابستگی‌ها» بحث شد.
نکته‌ی مهم این تنظیمات، قسمت مقدار دهی سازنده‌ی کلاس Context برنامه به صورت پویا توسط IoC Container جاری است. در اینجا هر زمانیکه قرار است وهله‌ای از Sample07Context ساخته شود، از سازنده‌ی دوم آن که دارای پارامتری به نام connectionString است، استفاده خواهد شد. همچنین مقدار آن به صورت پویا از متد getCurrentConnectionString که در انتهای کلاس تعریف شده‌است، دریافت می‌گردد.
در این متد ابتدا مقدار HttpContext.Current بررسی شده‌است. این مقدار اگر نال باشد، یعنی برنامه‌ی جاری یک برنامه‌ی دسکتاپ است و مدیریت رشته‌ی اتصالی جاری آن‌را توسط یک خاصیت Static یا Singleton تعریف شده‌ی در برنامه نیز می‌توان تامین کرد. از این جهت که در هر زمان، تنها یک کاربر در App Domain جاری برنامه‌ی دسکتاپ می‌تواند وجود داشته باشد و Singleton یا Static تعریف شدن اطلاعات رشته‌ی اتصالی، مشکلی را ایجاد نمی‌کند. اما در برنامه‌های وب، چندین کاربر در یک App Domain به سیستم وارد می‌شوند. به همین جهت است که مشاهده می‌کنید در اینجا از تامین کننده‌ی سشن، برای نگهداری اطلاعات رشته‌ی اتصالی جاری کمک گرفته شده‌است.

کلید این سشن نیز در این مثال مساوی CurrentConnectionStringName تعریف شده‌است. بنابراین در حین لاگین موفقیت آمیز کاربر، دو مرحله‌ی زیر باید طی شوند:
 sessionProvider.Store("CurrentConnectionString", "Sample07Context");
uow.SetConnectionString(WebConfigurationManager.ConnectionStrings[_sessionProvider.Get<string>("CurrentConnectionString")].ConnectionString);
ابتدا باید سشن CurrentConnectionStringName به بانک اطلاعاتی انتخابی کاربر تنظیم شود. برای نمونه در این مثال خاص، از نام رشته‌ی اتصالی مشخص شده‌ی در وب کانفیگ برنامه (مثال ابتدای بحث) به نام Sample07Context استفاده شده‌است.
سپس از متد SetConnectionString برای خواندن مقدار نام مشخص شده در سشن CurrentConnectionStringName کمک گرفته‌ایم. هرچند سازنده‌ی کلاس Context برنامه، هر دو حالت استفاده از نام رشته‌ی اتصالی و یا مقدار کامل رشته‌ی اتصالی را پشتیبانی می‌کند، اما خاصیت this.Database.Connection.ConnectionString تنها رشته‌ی کامل اتصالی را می‌پذیرد (بکار رفته در متد SetConnectionString).

تا اینجا کار پویا سازی انتخاب و استفاده از رشته‌ی اتصالی برنامه به پایان می‌رسد. هر زمانیکه قرار است Context برنامه توسط IoC Container نمونه سازی شود، به متد getCurrentConnectionString رجوع کرده و مقدار رشته‌ی اتصالی را از سشن تنظیم شده‌‌ای به نام CurrentConnectionStringName دریافت می‌کند. سپس از مقدار آن جهت مقدار دهی سازنده‌ی دوم کلاس Context استفاده خواهد کرد.


مدیریت migrations خودکار برنامه در حالت استفاده از چندین بانک اطلاعاتی

یکی از مشکلات کار با برنامه‌های چند دیتابیسی، به روز رسانی ساختار تمام بانک‌های اطلاعاتی مورد استفاده، پس از تغییری در ساختار مدل‌های برنامه است. از این جهت که اگر تمام بانک‌های اطلاعاتی به روز نشوند، کوئری‌های جدید برنامه که از خواص و فیلدهای جدید استفاده می‌کنند، دیگر کار نخواهند کرد. پویا سازی اعمال این تغییرات را می‌توان به صورت ذیل انجام داد:
using System;
using System.Data.Entity;
using System.Web;
using EF_Sample07.DataLayer.Context;
using EF_Sample07.IoCConfig;
 
namespace EF_Sample07.WebFormsAppSample
{
    public class Global : HttpApplication
    {
        void Application_Start(object sender, EventArgs e)
        {
            initDatabases();
        }
 
        private static void initDatabases()
        {
            // defined in web.config
            string[] connectionStringNames =
            {
                "Sample07Context",
                "Database2012"
            };
 
            foreach (var connectionStringName in connectionStringNames)
            {
                Database.SetInitializer(
                    new MigrateDatabaseToLatestVersion<Sample07Context, Configuration>(connectionStringName));
 
                using (var ctx = new Sample07Context(connectionStringName))
                {
                    ctx.Database.Initialize(force: true);
                }
            }
        }
 
        void Application_EndRequest(object sender, EventArgs e)
        {
            SmObjectFactory.HttpContextDisposeAndClearAll();
        } 
    }
}
نکته‌ی مهمی که در اینجا بکار گرفته شده‌است، مشخص سازی صریح سازنده‌ی شیء MigrateDatabaseToLatestVersion است. به صورت معمول در اکثر برنامه‌های تک دیتابیسی، نیازی به مشخص سازی پارامتر سازنده‌ی این کلاس نیست و در این حالت از سازنده‌ی بدون پارامتر کلاس Context برنامه استفاده خواهد شد. اما اگر سازنده‌ی آن‌را مشخص کنیم، به صورت خودکار از متد سازنده‌ای در کلاس Context استفاده می‌کند که پارامتر رشته‌ی اتصالی را به صورت پویا می‌پذیرد.
در این مثال خاص، متد initDatabases در حین آغاز برنامه فراخوانی شده‌است. منظور این است که اینکار در طول عمر برنامه تنها کافی است یکبار انجام شود و پس از آن است که EF Code first می‌تواند از رشته‌های اتصالی متفاوتی که به آن ارسال می‌شود، بدون مشکل استفاده کند. زیرا اطلاعات نگاشت کلاس‌های مدل برنامه به جداول بانک اطلاعاتی به این ترتیب است که کش می‌شوند و یا بر اساس کلاس Configuration به صورت خودکار به بانک اطلاعاتی اعمال می‌گردند.


کدهای کامل این مثال را که در حقیقت نمونه‌ی بهبود یافته‌ی مطلب «EF Code First #12» است، از اینجا می‌توانید دریافت کنید:
UoW-Sample
بازخوردهای دوره
صفحات مودال در بوت استرپ 3
سلام آقای نصیری
من همه مراحل رو با دقت و بدرستی انجام دادم...
اما هیچ مودالی باز نمیشه با ، هر دو فایل جاوا اسکریپت هم فراخوان کردم در layout...
با فایرباگ که برسی کردم دیدم این پیغام خطارو میده 
TypeError: $.validator is undefined
مشکل از کجا میتونه باشه؟
بازخوردهای دوره
صفحات مودال در بوت استرپ 3
فایل layout مثال پیوست شده را بررسی کنید. در آن لیست اسکریپت‌های مورد نیاز و همچنین ترتیب دقیق آ‌ن‌ها ذکر شده. برای مثال اول باید jQuery باشد و بعد اسکریپت jquery.validate و الی آخر. فایل‌های مربوطه هم در پوشه‌ی Scripts پروژه پیوست شده موجود است.
بازخوردهای دوره
صفحات مودال در بوت استرپ 3
مثال MVC آن کامل هست (پیوست شده در انتهای بحث در پوشه‌ی mvc-sample آن). شامل فایل‌های js، css و مدل و کنترلر، به همراه Viewها و layout آن که تعاریف لازم در آن هست و فایل‌های Viewهای Home که نحوه‌ی استفاده از آن ذکر شده‌اند.
نظرات اشتراک‌ها
Chrome و قابلیت DirectWrite
کلا فونتش را عوض کردم به این فونت خود گوگل.
فونت کودک، با سایر مرورگرها مشکلی نداشت؛ هرچند فایرفاکس این خطا را در کنسول خطاها نمایش می‌داد:
Error: downloadable font: table 'GSUB': OpenType layout data discarded
اطلاعات بیشتر در مورد GSUB
اشتراک‌ها
Visual Studio 2017 منتشر شد

دریافت نسخه‌ی سازمانی 60 روزه‌ی آن

برای تبدیل آن به نگارش کامل آفلاین (با حجم واقعی 20.3GB)، دستور ذیل را اجرا کنید:

vs_enterprise.exe --layout C:\vs2017offline --lang en-US
اطلاعات بیشتر: Create an offline installer for Visual Studio 2017  
Visual Studio 2017 منتشر شد
اشتراک‌ها
یک JavaScript UI Library خوب

What is Included

If you download the minified version of the library, the following JavaScript widgets are included

  • Layout
  • Grid
  • Toolbar
  • Tree
  • Tabs
  • Popup
  • Forms
  • Fields
  • Utilities  
یک JavaScript UI Library خوب