مطالب
نمایش یک فایل PDF در WinForms ، WPF و سیلورلایت

شاید PDF را بشود تنها فرمت گزارشگیری دانست که همه‌جا و در تمام سیستم عامل‌ها پشتیبانی می‌شود. از ویندوز تا لینوکس از وب تا WPF تا سیلورلایت تا همه جا و از همه مهم‌تر اینکه خروجی آن دقیقا همان چیزی است که کاربر نهایی می‌خواهد: من می‌خوام اون چیزی رو که می‌بینم، دقیقا همان را، بدون کم و کاست و با همان صفحه بندی، بتوانم چاپ کنم.
برای تولید PDF می‌شود از کتابخانه‌ی iTextSharp استفاده کرد اما برای نمایش آن حداقل در ویندوز بهترین راه حل استفاده از COM Components‌ شرکت Adobe است که به همراه برنامه رایگان Adobe PDF reader ارائه می‌شود. در ادامه نحوه‌ی استفاده از این Active-X را بررسی خواهیم کرد.

نمایش PDF در WPF
در تمام حالت‌ها هدف این است که به نحوی به اکتیوایکس شرکت Adobe دسترسی پیدا کنیم؛ یا با اضافه کردن آن به پروژه یا استفاده از امکانات یکپارچه مرورگرها. در WPF از زمان ارائه سرویس پک یک دات نت سه و نیم (به بعد)، کنترل مرورگر وب هم به جمع کنترل‌های قابل استفاده در آن اضافه شده است. در اینجا به سادگی چند سطر زیر می‌شود یک فایل PDF را در WPF نمایش داد:
<Window x:Class="WpfAppTests.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Pdf Report" Height="495" WindowState="Maximized"
WindowStartupLocation="CenterScreen" Width="703">
<Grid>
<WebBrowser x:Name="WebBrowser1"/>
</Grid>
</Window>
و بعد هم در کدهای برنامه تنها کافی است که مقدار Source کنترل WebBrowser را مقدار دهی کرد:
WebBrowser1.Source = new Uri(PdfFilePath);


نمایش PDF در WinForms
اکتیوایکس نمایش دهنده PDF شرکت Adobe اساسا در فایل ذیل قرار گرفته است:
C:\Program Files\Common Files\Adobe\Acrobat\ActiveX\AcroPDF.dll
بنابراین برای استفاده از آن در یک برنامه‌ی WinForms باید مراحل ذیل طی شود:
الف) در VS.NET‌ از طریق منوی Tools گزینه‌ی Choose toolbox items ، برگه‌ی Com components را انتخاب کنید.
ب) سپس گزینه‌ی Adobe PDF reader که به همان مسیر dll فوق اشاره می‌کند را انتخاب نمائید و بر روی دکمه‌ی OK‌ کلیک کنید.
ج) اکنون این کنترل جدید را بر روی فرم برنامه قرار دهید. به صورت خودکار COMReference های متناظر به پروژه اضافه می‌شوند.

اکنون نحوه‌ی استفاده از این شیء COM به همراه آزاد سازی منابع مرتبط به شرح زیر خواهند بود:
using System.Windows.Forms;

namespace WindowsFormsAppTests
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Load += Form1_Load;
this.FormClosing += Form1_FormClosing;
}

void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
axAcroPDF1.Dispose();
}

void Form1_Load(object sender, System.EventArgs e)
{
axAcroPDF1.LoadFile(PdfFilePath);
axAcroPDF1.setShowToolbar(true);
axAcroPDF1.Show();
}
}
}

نمایش PDF در Silverlight

در Silverlight هم از نسخه‌ی 4 به بعد کنترل WebBrowser همانند آنچه که در WPF موجود است، اضافه شده است؛ اما این کنترل فقط در حالت اجرای در خارج از مرورگر برنامه Silverlight در دسترس می‌باشد. بنابراین روش دیگری را باید انتخاب کرد. این روش بر اساس تعامل سیلورلایت با کدهای HTML صفحه کار می‌کند. یک IFrame مخفی را در صفحه بالای شیء مرتبط با سیلورلایت قرار خواهیم داد. سپس در سیلورلایت Src این IFrame را به مسیر فایل PDF تنظیم می‌کنیم و همین. به این ترتیب فایل PDF نمایش داده می‌شود.
این IFrame به صورت زیر در همان صفحه‌ی aspx ایی که object مرتبط با Silverlight نمایش داده می‌شود قرار می‌گیرد:
<iframe id="pdfFrame" style="visibility:hidden; position:absolute"><b>No Content</b></iframe>
<div id="silverlightControlHost">
سپس در کدهای سیلورلایت، ابتدا این IFrame یافت شده:
var iFrame = HtmlPage.Document.GetElementById("pdfFrame");
در ادامه بر اساس اطلاعات مکانی یک Grid ساده به نام pdfHost که در صفحه قرار گرفته، این iFrame بالاتر از سطح Grid (بر اساس z-index تنظیم شده) نمایش داده می‌شود:
var gt = pdfHost.TransformToVisual(Application.Current.RootVisual);
var offset = gt.Transform(new Point(0, 0));
var controlLeft = (int)offset.X;
var controlTop = (int)offset.Y;
iFrame.SetStyleAttribute("left", string.Format("{0}px", controlLeft));
iFrame.SetStyleAttribute("top", string.Format("{0}px", controlTop));
iFrame.SetStyleAttribute("visibility", "visible");
iFrame.SetStyleAttribute("height", string.Format("{0}px", pdfHost.ActualHeight));
iFrame.SetStyleAttribute("width", string.Format("{0}px", pdfHost.ActualWidth));
iFrame.SetStyleAttribute("z-index", "1000");
و در آخر نام فایلی را که می‌خواهیم مشاهده کنیم به یک صفحه‌ی aspx در همان سایت ارسال می‌کنیم:
iFrame.SetProperty("src", "ShowPdf.aspx?file=" + fileName);
کدهای این صفحه در حد یک Response.Redirect ساده برای نمایش دادن فایل pdf در مرورگر کافی هستند. در کل در اینجا سیلورلایت تنها نقش انتخاب فایل را به عهده دارد و کار اصلی را خود مرورگر انجام می‌دهد.

نظرات مطالب
معرفی فریم ورک Blueprint CSS
بسته به نیاز و کاربرد باید ابزار مناسبی رو انتخاب کرد. آیا شما نیاز به تمام عناصر موجود در فریم ورک توئیتر bootstrap دارید؟ من نیازی نداشتم به همین جهت انتخاب انجام شده هم بسیار سبک، کم حجم و متناسب بوده. از توئیتر bootstrap هم فقط دکمه‌های css ایی آن برای من جالب بود. نیازی به مابقی آن نداشتم.

مطالب
رمزنگاری خودکار فیلدها توسط Entity Framework Core
از EF Core 2.1 به بعد، قابلیت جدیدی تحت عنوان «تبدیلگرهای مقدار»، به آن اضافه شده‌است. برای مثال در EF Core، زمانیکه اطلاعات Enums، در بانک اطلاعاتی ذخیره می‌شوند، معادل عددی آن‌ها درج خواهند شد. اگر علاقمند باشید تا بجای این مقادیر عددی دقیقا همان رشته‌ی تعریف کننده‌ی Enum درج شود، می‌توان یک «تبدیلگر مقدار» را برای آن نوشت. برای مثال در موجودیت Rider زیر، خاصیت Mount از نوع یک enum است.
public class Rider
{
    public int Id { get; set; }
    public EquineBeast Mount { get; set; }
}

public enum EquineBeast
{
    Donkey,
    Mule,
    Horse,
    Unicorn
}
برای اینکه در حین درج رکوردهای Rider در بانک اطلاعاتی دقیقا از مقادیر رشته‌ای EquineBeast استفاده شود، می‌توان به صورت زیر عمل کرد:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Rider>()
        .Property(e => e.Mount)
        .HasConversion(
            v => v.ToString(),
            v => (EquineBeast)Enum.Parse(typeof(EquineBeast), v));
}
در اینجا در حین تعریف جزئیات نگاشت یک مدل می‌توان متد جدید HasConversion را نیز فراخوانی کرد. پارامتر اول آن، روش تبدیل مقدار enum را به یک رشته، جهت درج در بانک اطلاعاتی و پارامتر دوم آن، روش تبدیل مقدار رشته‌ای خوانده شده‌ی از بانک اطلاعاتی را جهت وهله سازی یک Rider داری خاصیت enum، مشخص می‌کند.

نکته 1: مقادیر نال، هیچگاه به تبدیلگرهای مقدار، ارسال نمی‌شوند. اینکار پیاده سازی آن‌ها را ساده‌تر می‌کند و همچنین می‌توان آن‌ها را بین خواص نال‌پذیر و نال‌نپذیر، به اشتراک گذاشت. بنابراین برای مقادیر نال نمی‌توان تبدیلگر نوشت.

نکته 2: کاری که در متد HasConversion فوق انجام شده‌است، در حقیقت وهله سازی ضمنی یک ValueConverter و استفاده از آن است. می‌توان اینکار را به صورت صریح نیز انجام داد:
var converter = new ValueConverter<EquineBeast, string>(
    v => v.ToString(),
    v => (EquineBeast)Enum.Parse(typeof(EquineBeast), v));
modelBuilder
    .Entity<Rider>()
    .Property(e => e.Mount)
    .HasConversion(converter);
مزیت اینکار این است که اگر قرار شد برای چندین خاصیت از تبدیلگر مقدار مشابهی استفاده کنیم، می‌توان از یک converter تعریف شده بجای تکرار کدهای آن استفاده کرد.


تبدیلگرهای مقدار توکار EF Core

برای بسیاری از اعمال متداول، در فضای نام Microsoft.EntityFrameworkCore.Storage.ValueConversion، تعدادی تبدیلگر مقدار تدارک دیده شده‌اند که به این شرح می‌باشند:
BoolToZeroOneConverter: تبدیلگر bool به صفر و یک
BoolToStringConverter: تبدیلگر bool به Y و یا N
BoolToTwoValuesConverter: تبدیلگر bool به دو مقداری دلخواه
BytesToStringConverter: تبدیلگر آرایه‌ای از بایت‌ها به یک رشته‌ی Base64-encoded
CastingConverter: تبدیلگر یک نوع به نوعی دیگر
CharToStringConverter: تبدیلگر char به string
DateTimeOffsetToBinaryConverter: تبدیلگر DateTimeOffset به یک مقدار 64 بیتی باینری
DateTimeOffsetToBytesConverter: تبدیلگر DateTimeOffset به آرایه‌ای از بایت‌ها
DateTimeOffsetToStringConverter: تبدیلگر DateTimeOffset به رشته
DateTimeToBinaryConverter: تبدیلگر DateTime به یک مقدار 64 بیتی با درج DateTimeKind
DateTimeToStringConverter: تبدیلگر DateTime به یک رشته
DateTimeToTicksConverter: تبدیلگر DateTime به ticks آن
EnumToNumberConverter: تبدیلگر Enum به عدد متناظر با آن
EnumToStringConverter: تبدیلگر Enum به رشته
GuidToBytesConverter: تبدیلگر Guid به آرایه‌ای از بایت‌ها
GuidToStringConverter: تبدیلگر Guid به رشته
NumberToBytesConverter: تبدیلگر اعداد به آرایه‌ای از بایت‌ها
NumberToStringConverter: تبدیلگر اعداد به رشته
StringToBytesConverter: تبدیلگر رشته به آرایه‌ای از بایت‌های UTF8 معادل آن
TimeSpanToStringConverter: تبدیلگر TimeSpan به رشته
TimeSpanToTicksConverter: تبدیلگر TimeSpan به ticks آن

برای نمونه در این لیست، EnumToStringConverter نیز وجود دارد. بنابراین نیازی به تعریف دستی آن مانند مثال ابتدای بحث نیست و می‌توان به صورت زیر از آن استفاده کرد:
var converter = new EnumToStringConverter<EquineBeast>();
modelBuilder
    .Entity<Rider>()
    .Property(e => e.Mount)
    .HasConversion(converter);
نکته: تمام تبدیل کننده‌های مقدار توکار EF Core، بدون حالت هستند. بنابراین می‌توان یک تک وهله‌ی از آن‌ها را بین چندین خاصیت به اشتراک گذاشت.


تعیین نوع تبدیلگر مقدار، جهت ساده سازی تعاریف

برای حالاتی که تبدیلگر مقدار توکاری تعریف شده‌است، صرفا تعریف نوع تبدیل، کفایت می‌کند:
modelBuilder
    .Entity<Rider>()
    .Property(e => e.Mount)
    .HasConversion<string>();
برای نمونه در اینجا با ذکر نوع رشته، تبدیل enum به string به صورت خودکار انجام خواهد شد. معادل اینکار، تعریف نوع سمت بانک اطلاعاتی این خاصیت است:
public class Rider
{
    public int Id { get; set; }

    [Column(TypeName = "nvarchar(24)")]
    public EquineBeast Mount { get; set; }
}
در این حالت حتی نیازی به تعریف HasConversion هم نیست.


نوشتن تبدیلگر خودکار مقادیر خواص، به نمونه‌ای رمزنگاری شده

پس از آشنایی با مفهوم «تبدیلگرهای مقدار» در +EF Core 2.1، اکنون می‌توانیم یک نمونه‌ی سفارشی از آن‌را نیز طراحی کنیم:
namespace DbConfig.Web.DataLayer.Context
{
    public class MyAppContext : DbContext
    {
      // …

        protected override void OnModelCreating(ModelBuilder builder)
        {
            var encryptedConverter = new ValueConverter<string, string>(
               convertToProviderExpression: v => new string(v.Reverse().ToArray()), // encrypt
               convertFromProviderExpression: v => new string(v.Reverse().ToArray()) // decrypt
            );

            // Custom application mappings
            builder.Entity<ConfigurationValue>(entity =>
            {
                entity.Property(e => e.Value).IsRequired().HasConversion(encryptedConverter);
            });
        }
    }
}
در اینجا معکوس کردن رشته‌ها به عنوان الگوریتم ساده‌ی رمزنگاری اطلاعات انتخاب شده‌است. نحوه‌ی اعمال این ValueConverter جدید را نیز ملاحظه می‌کنید.
می‌توان قسمت HasConversion را به صورت زیر خودکار کرد:
ابتدا یک Attribute جدید را به نام Encrypted به برنامه اضافه می‌کنیم:
using System;

namespace Test
{
    [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
    public sealed class EncryptedAttribute : Attribute
    { }
}
هدف از این Attribute خالی، صرفا نشانه گذاری خاصیت‌هایی است که قرار است به صورت رمزنگاری شده در بانک اطلاعاتی ذخیره شوند؛ مانند خاصیت Value زیر:
namespace DbConfig.Web.DomainClasses
{
    public class ConfigurationValue
    {
        public int Id { get; set; }
        public string Key { get; set; }

        [Encrypted]
        public string Value { get; set; }
    }
}
پس از آن، متد OnModelCreating را به صورت زیر اصلاح می‌کنیم تا به کمک Reflection و اطلاعات موجودیت‌های ثبت شده‌ی در سیستم، متد SetValueConverter را بر روی خواصی که دارای EncryptedAttribute هستند، به صورت خودکار فراخوانی کند:
namespace DbConfig.Web.DataLayer.Context
{
    public class MyAppContext : DbContext
    {
        protected override void OnModelCreating(ModelBuilder builder)
        {
            var encryptedConverter = new ValueConverter<string, string>(
               convertToProviderExpression: v => new string(v.Reverse().ToArray()), // encrypt
               convertFromProviderExpression: v => new string(v.Reverse().ToArray()) // decrypt
            );

            foreach (var entityType in builder.Model.GetEntityTypes())
            {
                foreach (var property in entityType.GetProperties())
                {
                    var attributes = property.PropertyInfo.GetCustomAttributes(typeof(EncryptedAttribute), false);
                    if (attributes.Any())
                    {
                        property.SetValueConverter(encryptedConverter);
                    }
                }
            }
        }


تاثیر ValueConverter‌ها بر روی اعمال متداول کار با بانک اطلاعاتی

از دیدگاه برنامه، ValueConverterهای تعریف شده، هیچگونه تاثیری را بر روی کوئری نوشتن و یا ثبت و ویرایش اطلاعات ندارند و عملکرد آن‌ها کاملا از دیدگاه سایر قسمت‌های برنامه مخفی است. برای مثال در برنامه، فرمان به روز رسانی خاصیت Value را با مقدار .A new value to test صادر کرده‌ایم (مقدار دهی متداول)، اما همانطور که ملاحظه می‌کنید، نمونه‌ی رمزنگاری شده‌ی آن به صورت خودکار در بانک اطلاعاتی درج شده‌است (پارامتر p0):
 Executed DbCommand (22ms) 
   [Parameters=[@p1='1', 
                @p0='.tset ot eulav wen A' (Nullable = false) (Size = 4000)],
CommandType='Text', CommandTimeout='180']
SET NOCOUNT ON;
UPDATE [Configurations] SET [Value] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

و یا کوئری زیر
 db.Set<ConfigurationValue>().Where(x => x.Value.EndsWith("world!"))
به این نحو ترجمه خواهد شد:
SELECT [x].[Id], [x].[Key], [x].[Value]
FROM [Configurations] AS [x]
WHERE RIGHT([x].[Value], LEN(N'world!')) = N'!dlrow'
یعنی نیازی نیست تا مقداری را که در حال جستجوی آن هستیم، خودمان به صورت دستی رمزنگاری کرده و سپس در کوئری قرار دهیم. اینکار به صورت خودکار انجام می‌شود.
نظرات مطالب
اعمال تزریق وابستگی‌ها به مثال رسمی ASP.NET Identity
سلام مجدد؛ دو تا راهنمایی دیگه از دوستان می‌خواستم.
1- همانطور که عنوان شد برای راهکارهای single page app و راهکارهای صرفا WebApi بهتر است از روش jwt استفاده گردد. حالا اگر سیستم نرم افزاری ما ترکیبی باشه(ASS.NET MVC و WebApi و ...) آیا پیاده سازی و توسعه همین سیستم منطقی‌تر نیست؟
2- روال کاری که در ASP.NET Identity در اینجا و در مثال رسمی خود مایکروسافت هستش بصورت ثبت نام و فعال سازی احراز هویت دو مرحله ای بعد از ثبت نام و در کنترل پنل کاربر(User Profile) هستش. ولی اگه بخوایم راهکار ما بصورت ثبت نام کاربر(نام کاربری - رمز عبور - تلفن همراه و ...) باشه و پس از ثبت نام با ارسال کد فعال سازی به تلفن همراه یا ایمیل کاربر و وارد کردن این کد توسط کاربر در صفحه مخصوص خودش(Verify Code) باشه(سیستم احراز هویت برنامه هایی مثل تلگرام و ...) آیا تابع Register دوباره بازنویسی باید بشه(چه در MVC Controller چه در WebApi Controller) یا توابع جدیدی برای این کار تعریف بشه. البته خب فکر میکنم باید View Model‌های جدید تعریف بشه چون یه مقدار اون قسمت ViewModel‌های داخل پروژه(Register  و ...) برای بنده گنگ بود برای پیاده سازی این کار مثل FactorViewModel و  SendCodeViewModel.
ممنون میشم یه مقدار بنده رو راهنمایی کنید تو این مبحث یا اگه پیاده سازی و مثال خاصی با پیاده سازی در این زمینه می‌شناسید به بنده معرفی کنید.
مطالب
چرا TypeScript؟
زبان TypeScript به عنوان superset زبان JavaScript ارائه شده‌است و هدف آن، strong typing و ارائه‌ی قابلیت‌های پیشرفته‌ی زبان‌های شیءگرا، جهت نوشتن برنامه‌های کلاینت و سرور، با کمترین میزان خطاها است. زبان TypeScript چندسکویی و سورس باز است و در نهایت به نگارشی از JavaScript کامپایل می‌شود که با تمام مرورگرهای فعلی سازگاری دارد و یا در سمت سرور بدون مشکلی توسط NodeJS قابل درک است.
- TypeScript زبان توصیه شده‌ی توسعه‌ی برنامه‌های AngularJS 2 است و همچنین با سایر کتابخانه‌های معروف جاوا اسکریپتی مانند ReactJS و jQuery نیز سازگاری دارد. بنابراین اگر قصد دارید به AngularJS 2 مهاجرت کنید، اکنون فرصت خوبی است تا زبان TypeScript را نیز بیاموزید. همچنین WinJS نیز با TypeScript نوشته شده‌است.
- superset زبان JavaScript بودن به این معنا است که تمام کدهای جاوا اسکریپتی موجود، به عنوان کد معتبر TypeScript نیز شناخته می‌شوند و همین مساله مهاجرت به آن‌را ساده‌تر می‌کند. زبان‌های دیگری مانند Dart و یا CoffeeScript ، نسبت به JavaScript بسیار متفاوت به نظر می‌رسند؛ اما Syntax زبان TypeScript شباهت بسیار زیادی به جاوا اسکریپت و خصوصا ES 6 دارد. در اینجا تنها کافی است پسوند فایل‌های js را به ts تغییر دهید و از آن‌ها به عنوان کدهای معتبر TypeScript استفاده کنید.
- strong typing و معرفی نوع‌ها، کدهای نهایی نوشته شده را امن‌تر می‌کنند. به این ترتیب کامپایلر، پیش از اینکه کدهای شما در زمان اجرا به خطا بر بخورند، در زمان کامپایل، مشکلات موجود را گوشزد می‌کند. همچنین وجود نوع‌ها، سرعت توسعه را با بهبود ابزارهای مرتبط با برنامه نویسی، افزایش می‌دهند؛ از این جهت که مفهوم مهمی مانند Intellisense، با وجود نوع‌ها، پیشنهادهای بهتر و دقیق‌تری را ارائه می‌دهد. همچنین ابزارهای Refactoring نیز در صورت وجود نوع‌ها بهتر و دقیق‌تر عمل می‌کنند. این موارد مهم‌ترین دلایل طراحی TypeScript جهت توسعه و نگهداری برنامه‌های بزرگ نوشته شده‌ی با JavaScript هستند.
- Syntax زبان TypeScript به شدت الهام گرفته شده از زبان سی‌شارپ است. به همین جهت اگر با این زبان آشنایی دارید، درک مفاهیم TypeScript برایتان بسیار ساده خواهد بود.
- بهترین قسمت TypeScript، کامپایل شدن آن به ES 5 است (به این عملیات Transpile هم می‌گویند). در زبان TypeScript به تمام امکانات پیشرفته‌ی ES 6 مانند کلاس‌ها و ماژول‌ها دسترسی دارید، اما کد نهایی را که تولید می‌کند، می‌تواند ES 5 ایی باشد که هم اکنون تمام مرورگرهای عمده آن‌را پشتیبانی می‌کنند. با تنظیمات کامپایلر TypeScript، امکان تولید کدهای ES 3 تا ES 5 و همچنین ES 6 نیز وجود دارد. نمونه‌ی آنلاین این ترجمه را در TypeScript playground می‌توانید مشاهده کنید.
- TypeScript چندسکویی است. امکانات و کامپایلر این زبان، برای ویندوز، مک و لینوکس طراحی شده‌اند.
- TypeScript سورس باز است. طراحان اصلی آن، همان طراحان زبان سی‌شارپ در مایکروسافت هستند و هم اکنون این زبان به صورت سورس باز توسط این شرکت توسعه داده شده و در GitHub نگهداری می‌شود.


آماده سازی محیط‌های کار با TypeScript

برای کار با TypeScript، یک ادیتور متنی ساده، به همراه کامپایلر آن کفایت می‌کند. اما همانطور که عنوان شد، یکی از مهم‌ترین دلایل وجودی TypeScript، بهبود ابزارهای برنامه نویسی مرتبط با JavaScript است و اگر قرار باشد صرفا از یک ادیتور متنی ساده استفاده شود، فلسفه‌ی وجودی آن زیر سؤال می‌رود.

نصب TypeScript در ویژوال استودیو

در نگارش‌های جدید ویژوال استودیو، از VS 2013 Update 2 به بعد، قسمت ویژه‌ی TypeScript نیز قابل مشاهده‌است. البته این قسمت با به روز رسانی‌های TypeScript، نیاز به به روز رسانی دارد. به همین جهت به سایت رسمی آن مراجعه کرده و بسته‌های جدید مخصوص VS 2013 و یا 2015 آن‌را دریافت و نصب کنید.


همچنین افزونه‌ی Web Essentials نیز امکانات بیشتری را جهت کار با TypeScript به همراه دارد و امکان مشاهده‌ی خروجی جاوا اسکریپت تولیدی را در حین کار با فایل TypeScript فعلی میسر می‌کند. در سمت چپ صفحه TypeScript را خواهید نوشت و در سمت راست، خروجی JavaScript نهایی را بلافاصله مشاهده می‌کنید.


تصویر فوق مربوط به VS 2015 است. همچنین گزینه‌ی افزودن یک فایل و آیتم جدید نیز امکان افزودن فایل‌های TS را به همراه دارد.


نصب و تنظیم TypeScript در ویژوال استودیو کد

ویژوال استودیو کد، نگارش رایگان، سورس باز و چندسکویی ویژوال استودیو است که بر روی ویندوز، مک و لینوکس قابل اجرا است. ویژوال استودیو کد نیز به همراه پشتیبانی بسیار خوبی از TypeScript است، تا حدی که تمام ارائه‌های معرفی Anugular 2 توسط تیم مربوطه‌ی آن از گوگل، توسط ویژوال استودیو کد و یکپارچگی آن با TypeScript انجام شدند.


ویژوال استودیو کد بر مبنای فولدرها کار می‌کند و با گشودن یک پوشه در آن (با کلیک بر روی دکمه‌ی open folder آن)، امکان کار کردن با آن پوشه و فایل‌های موجود در آن را خواهیم یافت.
نکته‌ی مهم اینجا است که پس از نصب VS Code، برای فایل‌های با پسوند ts بلافاصله Intellisense مرتبط نیز مهیا است و نیاز به هیچگونه تنظیم اضافه‌تری ندارد. همچنین قابلیت‌های type safety این زبان نیز در این ادیتور به نحو واضحی مشخص هستند:


در ادامه ابتدا یک پوشه‌ی جدید خالی را ایجاد کنید و سپس این پوشه را در VS Code باز نمائید (از طریق منوی فایل، گزینه‌ی گشودن پوشه). سپس ماوس را بر روی نام این پوشه حرکت دهید:


همانطور که مشاهده می‌کنید، دکمه‌ی new file ظاهر می‌شود. در اینجا می‌توانید فایل جدیدی را به نام test.ts اضافه کنید.
در ادامه با فشردن دکمه‌های ctrl+shift+p، امکان انتخاب یک task runner را جهت کامپایل فایل‌های ts خواهیم داشت:


در اینجا ابتدا عبارت task< را وارد کنید و سپس از منوی باز شده، گزینه‌ی rub build task را انتخاب کنید:


پس از آن، در بالای صفحه مشاهده خواهید کرد که عنوان شده: «هنوز هیچ task runner ایی برای اینکار تنظیم نشده‌است»


برای این منظور بر روی دکمه‌ی configure task runner تصویر فوق که با رنگ آبی مشخص شده‌است، کلیک کنید. به این ترتیب یک فایل جدید به نام task.json ایجاد می‌شود که در پوشه‌ای به نام vscode. در ریشه‌ی پروژه (یا همان پوشه‌ی جاری) قرار می‌گیرد:


فایل task.json دارای تعاریفی است که کامپایلر TypeScript یا همان tsc را فعال می‌کند:
{
"version": "0.1.0",

// The command is tsc. Assumes that tsc has been installed using npm install -g typescript
"command": "tsc",

// The command is a shell script
"isShellCommand": true,

// Show the output window only if unrecognized errors occur.
"showOutput": "silent",

// args is the HelloWorld program to compile.
"args": ["HelloWorld.ts"],

// use the standard tsc problem matcher to find compile problems
// in the output.
"problemMatcher": "$tsc"
}
محتوای پیش فرض و ابتدایی این فایل را در قطعه کد فوق مشاهده می‌کنید. این فایل json را جهت تنظیمات کامپایلر TypeScript پروژه‌ی جاری، ویرایش خواهیم کرد. در این فایل دکمه‌ی ctrl+space را بفشارید. بلافاصله منوی تکمیل کننده‌ی این فایل ظاهر می‌شود. از ترکیب ctrl+space در قسمت‌های مختلف این فایل جهت دریافت توصیه‌های بیشتری نیز می‌توان استفاده کرد.
در اینجا قسمتی که نیاز به تنظیم دارد، خاصیت args است. مقادیر آن، پارامترهایی هستند که به کامپایلر typescript ارسال می‌شوند. برای نمونه آن‌را به صورت ذیل تغییر دهید:
"args": [
         "--target", "ES5",
         "--outdir", "js",
         "--sourceMap",
         "--watch",
         "test.ts"
    ],
پارامتر و سوئیچ target مشخص می‌کند که خروجی تولیدی باید با فرمت ES 5 باشد. همچنین فایل‌های js تولیدی را در پوشه‌ی js در ریشه‌ی پروژه یا پوشه‌ی جاری قرار دهد. پارامتر sourceMap مشخص می‌کند که علاوه بر فایل‌های js، فایل‌های map که بیانگر نگاشت بین فایل‌های ts و js هستند نیز تولید شوند. این فایل‌ها برای دیباگ برنامه بسیار مفید هستند. پارامتر watch، کلیه‌ی تغییرات پوشه‌ی جاری را تحت نظر قرار داده و به صورت خودکار کار کامپایل را انجام می‌دهد. در آخر نیز فایل و یا فایل‌های ts مدنظر ذکر می‌شوند.
برای اجرای کامپایلر، ابتدا از منوی view گزینه‌ی toggle output را انتخاب کنید تا بتوان خروجی نهایی کامپایلر را مشاهده کرد. سپس گزینه‌ی view->command pallet و اجرا tasks< را انتخاب کنید. در ادامه همانند مرحله‌ی قبل، یعنی گزینه‌ی run build task را اجرا کنید (که خلاصه‌ی این عملیات ctrl+shift+B است).

به این ترتیب پوشه‌ی js که در خاصیت args مشخص کردیم، تولید می‌شود:


البته این خطا هم در قسمت output نمایش داده می‌شود:
 error TS5023: Unknown option 'watch'
Use the '--help' flag to see options.

علت اینجا است که در تنظیمات فوق، خاصیت command به tsc تنظیم شده‌است و همانطور که در کامنت آن عنوان شده‌است، کامپایلر typescript را از طریق دستور npm install -g typescript دریافت می‌کند و نیازی به ذکر مسیر آن در اینجا نیست. بنابراین لازم است تا با npm و نصب typescript از طریق آن آشنا شد و به این ترتیب کامپایلر آن‌را به روز کرد تا دستور watch را شناسایی کند.


نصب TypeScript از طریق npm

همانطور که عنوان شد، TypeScript چندسکویی است و این مورد را از طریق npm یا NodeJS package manager انجام می‌دهد. برای این منظور به آدرس https://nodejs.org/en   مراجعه کرده و فایل نصاب آن‌را مخصوص سیستم عامل خود دریافت و سپس نصب کنید. Node.js یک runtime سمت سرور اجرای برنامه‌های جاوا اسکریپتی است. از آنجائیکه TypeScript در نهایت به JavaScript تبدیل می‌شود، استفاده از node.js انتخاب مناسبی جهت اجرا و توزیع آن در تمام سیستم عامل‌ها بوده‌است.
پس از نصب node.js، از package manager آن که npm نام دارد، جهت نصب TypeScript استفاده می‌شود. چون node.js به Path و مسیرهای اصلی ویندوز اضافه می‌شود، تنها کافی است دستور npm install -g typescript را در خط فرمان صادر کنید. در اینجا سوئیچ g به معنای global و دسترسی عمومی است.


همانطور که در این تصویر مشخص است، پس از صدور دستور نصب TypeScript، نگارش 1.8.9 آن نصب شده‌است. اما زمانیکه کامپایلر tsc را با پارامتر version اجرا می‌کنیم، شماره نگارش قدیمی 1.0.3.0 را نمایش می‌دهد. برای رفع این مشکل به مسیر C:\Program Files (x86)\Microsoft SDKs\TypeScript مراجعه کرده و پوشه‌ی 1.0 را به 1.0-old تغییر نام دهید.


اکنون اگر مجددا بررسی کنیم، نگارش صحیح قابل مشاهده است:


پس از این تغییرات اگر مجددا به VS Code باز گردیم و ctrl+shift+B را صادر کنیم (جهت اجرای مجدد task runner و اجرای tsc تنظیم شده) ، پیام ذیل مشاهده می‌شود:
 15:33:52 - Compilation complete. Watching for file changes.
به این معنا که اینبار پارامتر watch را شناسایی کرده‌است و دیگر از کامپایلر قدیمی tsc استفاده نمی‌کند. برای آزمایش آن، از منوی view گزینه‌ی split editor را انتخاب کنید و سپس در سمت چپ فایل test.ts و در سمت راست، فایل test.js کامپایل شده را باز کنید:


در اینجا چون پارامتر watch فعال شده‌است، هر تغییری که در فایل ts داده شود، بلافاصله کامپایل شده و در فایل js منعکس خواهد شد.


تنظیم VS Code جهت دیباگ کدهای TypeScript

در نوار ابزار کنار صفحه‌ی VS Code، بر روی دکمه‌ی دیباگ کلیک کنید:


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


در اینجا node.js را انتخاب کنید. با اینکار فایل جدیدی دیگری به نام launch.json به پوشه‌ی vscode. اضافه می‌شود. اگر به این فایل دقت کنید دو خاصیت name به نام‌های Launch و Attach در آن موجود هستند. این نام‌ها در یک دراپ داون، در کنار دکمه‌ی start دیباگ نیز ظاهر می‌شوند:


- در فایل launch.json، باید خاصیت "program": "${workspaceRoot}/app.js" را ویرایش کرد و app.js آن‌را به test.ts مثال جاری تغییر داد.
- سپس خاصیت "sourceMaps" آن نیز باید تغییر کرده و جهت استفاده‌ی از source mapهای تولیدی به true تنظیم شود.
- در آخر باید مسیر پوشه‌ی خروجی js را نیز تنظیم کرد: "outDir": "${workspaceRoot}/js"
همچنین باید دقت داشت چون externalConsole به false تنظیم شده‌است، خروجی این کنسول به output ویژوال استودیوکد منتقل می‌شود.

اکنون اگر بر روی دکمه‌ی سبز رنگ start کلیک کنید (دکمه‌ی F5)، امکان دیباگ سطر به سطر کد TypeScript را خواهید یافت:



فایل‌های نهایی json یاد شده‌ی در متن را از اینجا می‌توانید دریافت کنید:
 VSCodeTypeScript.zip
مطالب
معماری لایه بندی نرم افزار #3

Service Layer

نقش لایه‌ی سرویس این است که به عنوان یک مدخل ورودی به برنامه کاربردی عمل کند. در برخی مواقع این لایه را به عنوان لایه‌ی Facade نیز می‌شناسند. این لایه، داده‌ها را در قالب یک نوع داده ای قوی (Strongly Typed) به نام View Model، برای لایه‌ی Presentation فراهم می‌کند. کلاس View Model یک Strongly Typed محسوب می‌شود که نماهای خاصی از داده‌ها را که متفاوت از دید یا نمای تجاری آن است، بصورت بهینه ارائه می‌نماید. در مورد الگوی View Model در مباحث بعدی بیشتر صحبت خواهم کرد.

الگوی Facade یک Interface ساده را به منظور کنترل دسترسی به مجموعه ای از Interface‌ها و زیر سیستم‌های پیچیده ارائه می‌کند. در مباحث بعدی در مورد آن بیشتر صحبت خواهم کرد.

کلاسی با نام ProductViewModel را با کد زیر به پروژه SoCPatterns.Layered.Service اضافه کنید:

public class ProductViewModel
{
    Public int ProductId {get; set;}
    public string Name { get; set; }
    public string Rrp { get; set; }
    public string SellingPrice { get; set; }
    public string Discount { get; set; }
    public string Savings { get; set; }
}

برای اینکه کلاینت با لایه‌ی سرویس در تعامل باشد باید از الگوی Request/Response Message استفاده کنیم. بخش Request توسط کلاینت تغذیه می‌شود و پارامترهای مورد نیاز را فراهم می‌کند. کلاسی با نام ProductListRequest را با کد زیر به پروژه SoCPatterns.Layered.Service اضافه کنید:

using SoCPatterns.Layered.Model;

namespace SoCPatterns.Layered.Service
{
    public class ProductListRequest
    {
        public CustomerType CustomerType { get; set; }
    }
}

در شی Response نیز بررسی می‌کنیم که درخواست به درستی انجام شده باشد، داده‌های مورد نیاز را برای کلاینت فراهم می‌کنیم و همچنین در صورت عدم اجرای صحیح درخواست، پیام مناسب را به کلاینت ارسال می‌نماییم. کلاسی با نام ProductListResponse را با کد زیر به پروژه SoCPatterns.Layered.Service اضافه کنید:

public class ProductListResponse
{
    public bool Success { get; set; }
    public string Message { get; set; }
    public IList<ProductViewModel> Products { get; set; }
}

به منظور تبدیل موجودیت Product به ProductViewModel، به دو متد نیاز داریم، یکی برای تبدیل یک Product و دیگری برای تبدیل لیستی از Product. شما می‌توانید این دو متد را به کلاس Product موجود در Domain Model اضافه نمایید، اما این متدها نیاز واقعی منطق تجاری نمی‌باشند. بنابراین بهترین انتخاب، استفاده از Extension Method‌ها می‌باشد که باید برای کلاس Product و در لایه‌ی سرویس ایجاد نمایید. کلاسی با نام ProductMapperExtensionMethods را با کد زیر به پروژه SoCPatterns.Layered.Service اضافه کنید:

public static class ProductMapperExtensionMethods
{
    public static ProductViewModel ConvertToProductViewModel(this Model.Product product)
    {
        ProductViewModel productViewModel = new ProductViewModel();
        productViewModel.ProductId = product.Id;
        productViewModel.Name = product.Name;
        productViewModel.RRP = String.Format(“{0:C}”, product.Price.RRP);
        productViewModel.SellingPrice = String.Format(“{0:C}”, product.Price.SellingPrice);
        if (product.Price.Discount > 0)
            productViewModel.Discount = String.Format(“{0:C}”, product.Price.Discount);
        if (product.Price.Savings < 1 && product.Price.Savings > 0)
            productViewModel.Savings = product.Price.Savings.ToString(“#%”);
        return productViewModel;
    }
    public static IList<ProductViewModel> ConvertToProductListViewModel(
        this IList<Model.Product> products)
    {
        IList<ProductViewModel> productViewModels = new List<ProductViewModel>();
        foreach(Model.Product p in products)
        {
            productViewModels.Add(p.ConvertToProductViewModel());
        }
        return productViewModels;
    }
}

حال کلاس ProductService را جهت تعامل با کلاس سرویس موجود در Domain Model و به منظور برگرداندن لیستی از محصولات و تبدیل آن به لیستی از ProductViewModel، ایجاد می‌نماییم. کلاسی با نام ProductService را با کد زیر به پروژه SoCPatterns.Layered.Service اضافه کنید:

public class ProductService
{
    private Model.ProductService _productService;
    public ProductService(Model.ProductService ProductService)
    {
        _productService = ProductService;
    }
    public ProductListResponse GetAllProductsFor(
        ProductListRequest productListRequest)
    {
        ProductListResponse productListResponse = new ProductListResponse();
        try
        {
            IList<Model.Product> productEntities =
                _productService.GetAllProductsFor(productListRequest.CustomerType);
            productListResponse.Products = productEntities.ConvertToProductListViewModel();
            productListResponse.Success = true;
        }
        catch (Exception ex)
        {
            // Log the exception…
            productListResponse.Success = false;
            // Return a friendly error message
            productListResponse.Message = ex.Message;
        }
        return productListResponse;
    }
}

کلاس Service تمامی خطاها را دریافت نموده و پس از مدیریت خطا، پیغامی مناسب را به کلاینت ارسال می‌کند. همچنین این لایه محل مناسبی برای Log کردن خطاها می‌باشد. در اینجا کد نویسی لایه سرویس به پایان رسید و در ادامه به کدنویسی Data Layer می‌پردازیم.

Data Layer

برای ذخیره سازی محصولات، یک بانک اطلاعاتی با نام Shop01 ایجاد کنید که شامل جدولی به نام Product با ساختار زیر باشد:

برای اینکه کدهای بانک اطلاعاتی را سریعتر تولید کنیم از روش Linq to SQL در Data Layer استفاده می‌کنم. برای این منظور یک Data Context برای Linq to SQL به این لایه اضافه می‌کنیم. بر روی پروژه SoCPatterns.Layered.Repository کلیک راست نمایید و گزینه Add > New Item را انتخاب کنید. در پنجره ظاهر شده و از سمت چپ گزینه Data و سپس از سمت راست گزینه Linq to SQL Classes را انتخاب نموده و نام آن را Shop.dbml تعیین نمایید.

از طریق پنجره Server Explorer به پایگاه داده مورد نظر متصل شوید و با عمل Drag & Drop جدول Product را به بخش Design کشیده و رها نمایید.

اگر به یاد داشته باشید، در لایه Model برای برقراری ارتباط با پایگاه داده از یک Interface به نام IProductRepository استفاده نمودیم. حال باید این Interface را پیاده سازی نماییم. کلاسی با نام ProductRepository را با کد زیر به پروژه SoCPatterns.Layered.Repository اضافه کنید:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SoCPatterns.Layered.Model;

namespace SoCPatterns.Layered.Repository
{
    public class ProductRepository : IProductRepository
    {
        public IList<Model.Product> FindAll()
        {
            var products = from p in new ShopDataContext().Products
                                select new Model.Product
                                {
                                    Id = p.ProductId,
                                    Name = p.ProductName,
                                    Price = new Model.Price(p.Rrp, p.SellingPrice)
                                };
            return products.ToList();
        }
    }
}

در متد FindAll، با استفاده از دستورات Linq to SQL، لیست تمامی محصولات را برگرداندیم. کدنویسی لایه‌ی Data هم به پایان رسید و در ادامه به کدنویسی لایه‌ی Presentation و UI می‌پردازیم.

Presentation Layer

به منظور جداسازی منطق نمایش (Presentation) از رابط کاربری (User Interface)، از الگوی Model View Presenter یا همان MVP استفاده می‌کنیم که در مباحث بعدی با جزئیات بیشتری در مورد آن صحبت خواهم کرد. یک Interface با نام IProductListView را با کد زیر به پروژه SoCPatterns.Layered.Presentation اضافه کنید:

using SoCPatterns.Layered.Service;

public interface IProductListView
{
    void Display(IList<ProductViewModel> Products);
    Model.CustomerType CustomerType { get; }
    string ErrorMessage { set; }
}

این Interface توسط Web Form‌های ASP.NET و یا Win Form‌ها باید پیاده سازی شوند. کار با Interface‌ها موجب می‌شود تا تست View‌ها به راحتی انجام شوند. کلاسی با نام ProductListPresenter را با کد زیر به پروژه SoCPatterns.Layered.Presentation اضافه کنید:

using SoCPatterns.Layered.Service;

namespace SoCPatterns.Layered.Presentation
{
    public class ProductListPresenter
    {
        private IProductListView _productListView;
        private Service.ProductService _productService;
        public ProductListPresenter(IProductListView ProductListView,
            Service.ProductService ProductService)
        {
            _productService = ProductService;
            _productListView = ProductListView;
        }
        public void Display()
        {
            ProductListRequest productListRequest = new ProductListRequest();
            productListRequest.CustomerType = _productListView.CustomerType;
            ProductListResponse productResponse =
                _productService.GetAllProductsFor(productListRequest);
            if (productResponse.Success)
            {
                _productListView.Display(productResponse.Products);
            }
            else
            {
                _productListView.ErrorMessage = productResponse.Message;
            }
        }
    }
}

کلاس Presenter وظیفه‌ی واکشی داده ها، مدیریت رویدادها و بروزرسانی UI را دارد. در اینجا کدنویسی لایه‌ی Presentation به پایان رسیده است. از مزایای وجود لایه‌ی Presentation این است که تست نویسی مربوط به نمایش داده‌ها و تعامل بین کاربر و سیستم به سهولت انجام می‌شود بدون آنکه نگران دشواری Unit Test نویسی Web Form‌ها باشید. حال می‌توانید کد نویسی مربوط به UI را انجام دهید که در ادامه به کد نویسی در Win Forms و Web Forms خواهیم پرداخت.