اشتراک‌ها
کتابخانه EntityFramework.Functions

یکی دیگر از  محدودیت‌های EF Codefirst عدم امکان استفاده از SQL Function‌ها و Stored Procedure هایی با خروجی‌های متفاوت است. لینک زیر کتابخانه EntityFramework.Functions را معرفی می‌کند که به کمک آن با این محدودیت EF Codefirst خداحافظی می‌کنید. 

کتابخانه EntityFramework.Functions
نظرات مطالب
محاسبه ی اختلاف زمان رخدادی در گذشته با زمان فعلی به فارسی
- در همان لینک آخری که آقای کیاست معرفی کردند، پیاده سازی date_diff به زبان‌های دیگر منجمله جاوا اسکریپت نیز هست. برای تبدیل شمسی به میلادی هم از کتابخانه moment مثلا کمک بگیرید.
+ معادل کاملتر مطلب جاری: «DNTPersianUtils.Core»
نظرات مطالب
افزودن تصدیق ایمیل به ASP.NET Identity در MVC 5
با تشکر از شما. لطفا View ایمیل ارسالی را (متن حاوی لینک) که توسط کتابخانه Postal پردازش می‌شود، نیز ارسال نمائید. چون الان به نظر متد SendEmailConfirmation مشخص نیست چه متنی را ارسال می‌کند و چطور آن متن را دریافت می‌کند.
نظرات مطالب
Blazor 5x - قسمت 34 - توزیع برنامه‌های Blazor بر روی IIS
نمونه‌ای از راه اندازی یک برنامه‌ی Blazor WASM در IIS

الف) به قسمت application pools در IIS Manager مراجعه کرده و گزینه‌ی add application pool را انتخاب کنید. سپس یک نمونه‌ی جدید را برای مثال به نام no-managed ایجاد کنید که net clr version. آن به گزینه‌ی no managed code اشاره می‌کند.
ب) پس از publish برنامه مطابق مطلب فوق (برای مثال با اجرای دستور dotnet publish -c Release) و معرفی مسیر پوشه‌ی publish به IIS (برای مثال به عنوان یک Application جدید ذیل default web site با کلیک راست بر روی default web site و انتخاب گزینه‌ی Add application)، این application pool جدید را به برنامه‌ی خود در IIS نسبت دهید. برای اینکار basic settings سایت را باز کرده و بر روی دکمه‌ی select که در کنار نام application pool هست، کلیک کرده و گزینه‌ی no-managed قسمت الف را انتخاب کنید.

نکته 1: برنامه‌های blazor wasm، یا standalone هستند و یا hosted. مورد standalone یعنی کاری به Web API ندارد و به خودی خود، به صورت یک سایت استاتیک قابل مشاهده‌است. حالت hosted یعنی به همراه web api هم هست و توسط دستور برای مثال «dotnet new blazorwasm -o BlazorIIS --hosted --no-https» ایجاد می‌شود که به همراه سه پوشه‌ی کلاینت، سرور و shared است. برای توزیع حالت متکی به خود، فقط محتویات پوشه‌ی publish، به عنوان مسیر برنامه، در IIS معرفی خواهند شد. در حالت hosted، مسیر اصلی، پوشه‌ی publish مربوط به پروژه‌ی سرور است؛ یعنی: Server\bin\Release\net5.0\publish. در این حالت پوشه‌ی Client\bin\Release\net5.0\publish باید به داخل همین پوشه‌ی publish سرور کپی شود. یعنی پوشه‌ی publish پروژه‌ی client باید به درون پوشه‌ی publish پروژه‌ی server کپی شود تا با هم یک برنامه‌ی قابل توزیع توسط IIS را تشکیل دهند.
در اینجا تنها فایل‌هایی که تداخل پیدا می‌کنند، فایل‌های web.config هستند که باید یکی شوند. فایل web.config برنامه‌ی web api با برنامه‌ی client یکی نیست، اما می‌توان محتویات این دو را با هم یکی کرد.
نکته 2: محل اجرای دستور dotnet publish -c Release مهم است. اگر این دستور را در کنار فایل sln پروژه‌ی hosted اجرا کنید، سه خروجی نهایی publish را تولید می‌کند و پس از آن باید فایل‌های کلاینت را به سرور، به صورت دستی کپی کرد. اما اگر دستور publish را درون پوشه‌ی سرور اجرا کنید، کار کپی فایل‌های کلاینت را به درون پوشه‌ی نهایی publish تشکیل شده، به صورت خودکار انجام می‌دهد؛ علت اینجا است که اگر به فایل csproj. پروژه‌ی سرور دقت کنید، ارجاعی را به پروژه‌ی کلاینت نیز دارد (هر چند ما از کدهای آن در برنامه‌ی web api استفاده نمی‌کنیم). این ارجاع تنها کمک حال دستور dotnet publish -c Release است و کاربرد دیگری را ندارد.

ج) اکنون اگر برنامه را برای مثال با مسیر فرضی جدید http://localhost/blazortest اجرا کنید، خطای 500.19 را دریافت می‌کنید. علت آن‌را در مطلب «بررسی خطاهای ممکن در حین راه اندازی اولیه برنامه‌های ASP.NET Core در IIS» بررسی کرده‌ایم. باید IIS URL Rewrite ماژول را نصب کرد؛ تا این مشکل برطرف شود. همچنین دلیل دیگر مشاهده‌ی این خطا، عدم نصب بسته‌ی هاستینگ متناظر با شماره نگارش NET. مورد استفاده‌است (اگر برنامه‌ی شما از نوع hosted است و web api هم دارد).
د) پس از آن باز هم برنامه اجرا نمی‌شود! اگر در خطاها دقت کنید، به دنبال اسکریپت‌هایی شروع شده از مسیر localhost است و نه از پوشه‌ی جدید blazortest. برای رفع این مشکل باید فایل publish\wwwroot\index.html را مطابق نکته‌ی base-URL که کمی بالاتر ذکر شد، ویرایش کرد تا blazor بداند که فایل‌ها، در چه مسیری قرار دارند (و در ریشه‌ی سایت واقع نشده‌اند):
<head>
<base href="/blazortest/" />
اکنون برنامه بدون مشکل بارگذاری و اجرا می‌شود.
مطالب
پیاده سازی Password Policy سفارشی توسط ASP.NET Identity
برای فراهم کردن یک تجربه کاربری ایمن‌تر و بهتر، ممکن است بخواهید پیچیدگی password policy را سفارشی سازی کنید. مثلا ممکن است بخواهید حداقل تعداد کاراکتر‌ها را تنظیم کنید، استفاده از چند حروف ویژه را اجباری کنید،  جلوگیری از استفاده نام کاربر در کلمه عبور و غیره. برای اطلاعات بیشتر درباره سیاست‌های کلمه عبور به این لینک مراجعه کنید. بصورت پیش فرض ASP.NET Identity کاربران را وادار می‌کند تا کلمه‌های عبوری بطول حداقل 6 کاراکتر وارد نمایند. در ادامه نحوه افزودن چند خط مشی دیگر را هم بررسی می‌کنیم.

با استفاده از ویژوال استودیو 2013 پروژه جدیدی خواهیم ساخت تا از ASP.NET Identity استفاده کند. مواردی که درباره کلمه‌های عبور می‌خواهیم اعمال کنیم در زیر لیست شده اند.

  • تنظیمات پیش فرض باید تغییر کنند تا کلمات عبور حداقل 10 کاراکتر باشند
  • کلمه عبور حداقل یک عدد و یک کاراکتر ویژه باید داشته باشد
  • امکان استفاده از 5 کلمه عبور اخیری که ثبت شده وجود ندارد
در آخر اپلیکیشن را اجرا می‌کنیم و عملکرد این قوانین جدید را بررسی خواهیم کرد.


ایجاد اپلیکیشن جدید

در Visual Studio 2013 اپلیکیشن جدیدی از نوع ASP.NET MVC 4.5 بسازید.

در پنجره Solution Explorer روی نام پروژه کلیک راست کنید و گزینه Manage NuGet Packages را انتخاب کنید. به قسمت Update بروید و تمام انتشارات جدید را در صورت وجود نصب کنید.

بگذارید تا به روند کلی ایجاد کاربران جدید در اپلیکیشن نگاهی بیاندازیم. این به ما در شناسایی نیازهای جدیدمان کمک می‌کند. در پوشه Controllers فایلی بنام AccountController.cs وجود دارد که حاوی متدهایی برای مدیریت کاربران است.

  • کنترلر Account از کلاس UserManager استفاده می‌کند که در فریم ورک Identity تعریف شده است. این کلاس به نوبه خود از کلاس دیگری بنام UserStore استفاده می‌کند که برای دسترسی و مدیریت داده‌های کاربران استفاده می‌شود. در مثال ما این کلاس از Entity Framework استفاده می‌کند که پیاده سازی پیش فرض است.
  • متد Register POST یک کاربر جدید می‌سازد. متد CreateAsync به طبع متد 'ValidateAsync' را روی خاصیت PasswordValidator فراخوانی می‌کند تا کلمه عبور دریافتی اعتبارسنجی شود.
var user = new ApplicationUser() { UserName = model.UserName };  
var result = await UserManager.CreateAsync(user, model.Password);
  
if (result.Succeeded)
{
    await SignInAsync(user, isPersistent: false);  
    return RedirectToAction("Index", "Home");
}

در صورت موفقیت آمیز بودن عملیات ایجاد حساب کاربری، کاربر به سایت وارد می‌شود.


قانون 1: کلمه‌های عبور باید حداقل 10 کاراکتر باشند

بصورت پیش فرض خاصیت PasswordValidator در کلاس UserManager به کلاس MinimumLengthValidator تنظیم شده است، که اطمینان حاصل می‌کند کلمه عبور حداقل 6 کاراکتر باشد. هنگام وهله سازی UserManager می‌توانید این مقدار را تغییر دهید.
  • مقدار حداقل کاراکترهای کلمه عبور به دو شکل می‌تواند تعریف شود. راه اول، تغییر کنترلر Account است. در متد سازنده این کنترلر کلاس UserManager وهله سازی می‌شود، همینجا می‌توانید این تغییر را اعمال کنید. راه دوم، ساختن کلاس جدیدی است که از UserManager ارث بری می‌کند. سپس می‌توان این کلاس را در سطح global تعریف کرد. در پوشه IdentityExtensions کلاس جدیدی با نام ApplicationUserManager بسازید.
public class ApplicationUserManager : UserManager<ApplicationUser>
{
    public ApplicationUserManager(): base(new UserStore<ApplicationUser>(new ApplicationDbContext()))
    {
        PasswordValidator = new MinimumLengthValidator (10);
    }
}

  کلاس UserManager یک نمونه از کلاس IUserStore را دریافت می‌کند که پیاده سازی API‌های مدیریت کاربران است. از آنجا که کلاس UserStore مبتنی بر Entity Framework است، باید آبجکت DbContext را هم پاس دهیم. این کد در واقع همان کدی است که در متد سازنده کنترلر Account وجود دارد.

یک مزیت دیگر این روش این است که می‌توانیم متدهای UserManager را بازنویسی (overwrite) کنیم. برای پیاده سازی نیازمندهای بعدی دقیقا همین کار را خواهیم کرد.


  • حال باید کلاس ApplicationUserManager را در کنترلر Account استفاده کنیم. متد سازنده و خاصیت UserManager را مانند زیر تغییر دهید.
 public AccountController() : this(new ApplicationUserManager())
         {
         }
  
         public AccountController(ApplicationUserManager userManager)
         {
             UserManager = userManager;
         }
         public ApplicationUserManager UserManager { get; private set; }
حالا داریم از کلاس سفارشی جدیدمان استفاده می‌کنیم. این به ما اجازه می‌دهد مراحل بعدی سفارشی سازی را انجام دهیم، بدون آنکه کدهای موجود در کنترلر از کار بیافتند.
  • اپلیکیشن را اجرا کنید و سعی کنید کاربر محلی جدیدی ثبت نمایید. اگر کلمه عبور وارد شده کمتر از 10 کاراکتر باشد پیغام خطای زیر را دریافت می‌کنید.


قانون 2: کلمه‌های عبور باید حداقل یک عدد و یک کاراکتر ویژه داشته باشند

چیزی که در این مرحله نیاز داریم کلاس جدیدی است که اینترفیس IIdentityValidator را پیاده سازی می‌کند. چیزی که ما می‌خواهیم اعتبارسنجی کنیم، وجود اعداد و کاراکترهای ویژه در کلمه عبور است، همچنین طول مجاز هم بررسی می‌شود. نهایتا این قوانین اعتبارسنجی در متد 'ValidateAsync' بکار گرفته خواهند شد.
  • در پوشه IdentityExtensions کلاس جدیدی بنام CustomPasswordValidator بسازید و اینترفیس مذکور را پیاده سازی کنید. از آنجا که نوع کلمه عبور رشته (string) است از <IIdentityValidator<string استفاده می‌کنیم.
public class CustomPasswordValidator : IIdentityValidator<string> 
{  
    public int RequiredLength { get; set; }

    public CustomPasswordValidator(int length)
    {
        RequiredLength = length;
    }
  
    public Task<IdentityResult> ValidateAsync(string item)
    {
        if (String.IsNullOrEmpty(item) || item.Length < RequiredLength)
        {
            return Task.FromResult(IdentityResult.Failed(String.Format("Password should be of length {0}",RequiredLength)));
        }
  
    string pattern = @"^(?=.*[0-9])(?=.*[!@#$%^&*])[0-9a-zA-Z!@#$%^&*0-9]{10,}$";
   
    if (!Regex.IsMatch(item, pattern))  
    {
        return Task.FromResult(IdentityResult.Failed("Password should have one numeral and one special character"));
    }
  
    return Task.FromResult(IdentityResult.Success);
 }


  در متد ValidateAsync بررس می‌کنیم که طول کلمه عبور معتبر و مجاز است یا خیر. سپس با استفاده از یک RegEx وجود کاراکترهای ویژه و اعداد را بررسی می‌کنیم. دقت کنید که regex استفاده شده تست نشده و تنها بعنوان یک مثال باید در نظر گرفته شود.
  • قدم بعدی تعریف این اعتبارسنج سفارشی در کلاس UserManager است. باید مقدار خاصیت PasswordValidator را به این کلاس تنظیم کنیم. به کلاس ApplicationUserManager که پیشتر ساختید بروید و مقدار خاصیت PasswordValidator را به CustomPasswordValidator تغییر دهید.
public class ApplicationUserManager : UserManager<ApplicationUser>  
{  
    public ApplicationUserManager() : base(new UserStore<ApplicationUser(new ApplicationDbContext()))
    {
        PasswordValidator = new CustomPasswordValidator(10);
    }
 }

  هیچ تغییر دیگری در کلاس AccountController لازم نیست. حال سعی کنید کاربر جدید دیگری بسازید، اما اینبار کلمه عبوری وارد کنید که خطای اعتبارسنجی تولید کند. پیغام خطایی مشابه تصویر زیر باید دریافت کنید.


قانون 3: امکان استفاده از 5 کلمه عبور اخیر ثبت شده وجود ندارد

هنگامی که کاربران سیستم، کلمه عبور خود را بازنشانی (reset) می‌کنند یا تغییر می‌دهند، می‌توانیم بررسی کنیم که آیا مجددا از یک کلمه عبور پیشین استفاده کرده اند یا خیر. این بررسی بصورت پیش فرض انجام نمی‌شود، چرا که سیستم Identity تاریخچه کلمه‌های عبور کاربران را ذخیره نمی‌کند. می‌توانیم در اپلیکیشن خود جدول جدیدی بسازیم و تاریخچه کلمات عبور کاربران را در آن ذخیره کنیم. هربار که کاربر سعی در بازنشانی یا تغییر کلمه عبور خود دارد، مقدار Hash شده را در جدول تاریخچه بررسی میکنیم.
فایل IdentityModels.cs را باز کنید. مانند لیست زیر، کلاس جدیدی بنام 'PreviousPassword' بسازید.
public class PreviousPassword  
{
  
 public PreviousPassword()
 {
    CreateDate = DateTimeOffset.Now;
 }
  
 [Key, Column(Order = 0)]
  
 public string PasswordHash { get; set; }
  
 public DateTimeOffset CreateDate { get; set; }
  
 [Key, Column(Order = 1)]
  
 public string UserId { get; set; }
  
 public virtual ApplicationUser User { get; set; }
  
 }
در این کلاس، فیلد 'Password' مقدار Hash شده کلمه عبور را نگاه میدارد و توسط فیلد 'UserId' رفرنس می‌شود. فیلد 'CreateDate' یک مقدار timestamp ذخیره می‌کند که تاریخ ثبت کلمه عبور را مشخص می‌نماید. توسط این فیلد می‌توانیم تاریخچه کلمات عبور را فیلتر کنیم و مثلا 5 رکورد آخر را بگیریم.

Entity Framework Code First جدول 'PreviousPasswords' را می‌سازد و با استفاده از فیلدهای 'UserId' و 'Password' کلید اصلی (composite primary key) را ایجاد می‌کند. برای اطلاعات بیشتر درباره قرارداهای EF Code First به  این لینک  مراجعه کنید.

  • خاصیت جدیدی به کلاس ApplicationUser اضافه کنید تا لیست آخرین کلمات عبور استفاده شده را نگهداری کند.
public class ApplicationUser : IdentityUser
{
    public ApplicationUser() : base()
    {
        PreviousUserPasswords = new List<PreviousPassword>();
     }

     public virtual IList<PreviousPassword> PreviousUserPasswords { get; set; }
}

 همانطور که پیشتر گفته شد، کلاس UserStore پیاده سازی API‌های لازم برای مدیریت کاربران را در بر می‌گیرد. هنگامی که کاربر برای نخستین بار در سایت ثبت می‌شود باید مقدار Hash کلمه عبورش را در جدول تاریخچه کلمات عبور ذخیره کنیم. از آنجا که UserStore بصورت پیش فرض متدی برای چنین عملیاتی معرفی نمی‌کند، باید یک override تعریف کنیم تا این مراحل را انجام دهیم. پس ابتدا باید کلاس سفارشی جدیدی بسازیم که از UserStore ارث بری کرده و آن را توسعه می‌دهد. سپس از این کلاس سفارشی در ApplicationUserManager بعنوان پیاده سازی پیش فرض UserStore استفاده می‌کنیم. پس کلاس جدیدی در پوشه IdentityExtensions ایجاد کنید.
public class ApplicationUserStore : UserStore<ApplicationUser>
{ 
    public ApplicationUserStore(DbContext context) : base(context)  { }
  
    public override async Task CreateAsync(ApplicationUser user)
    {
        await base.CreateAsync(user);
        await AddToPreviousPasswordsAsync(user, user.PasswordHash);
    }
  
     public Task AddToPreviousPasswordsAsync(ApplicationUser user, string password)
     {
        user.PreviousUserPasswords.Add(new PreviousPassword() { UserId = user.Id, PasswordHash = password });
  
        return UpdateAsync(user);
     }
 }

 متد 'AddToPreviousPasswordsAsync' کلمه عبور را در جدول 'PreviousPasswords' ذخیره می‌کند.

هرگاه کاربر سعی در بازنشانی یا تغییر کلمه عبورش دارد باید این متد را فراخوانی کنیم. API‌های لازم برای این کار در کلاس UserManager تعریف شده اند. باید این متدها را override کنیم و فراخوانی متد مذکور را پیاده کنیم. برای این کار کلاس ApplicationUserManager را باز کنید و متدهای ChangePassword و ResetPassword را بازنویسی کنید.
public class ApplicationUserManager : UserManager<ApplicationUser>
    {
        private const int PASSWORD_HISTORY_LIMIT = 5;

        public ApplicationUserManager() : base(new ApplicationUserStore(new ApplicationDbContext()))
        {
            PasswordValidator = new CustomPasswordValidator(10);
        }

        public override async Task<IdentityResult> ChangePasswordAsync(string userId, string currentPassword, string newPassword)
        {
            if (await IsPreviousPassword(userId, newPassword))
            {
                return await Task.FromResult(IdentityResult.Failed("Cannot reuse old password"));
            }

            var result = await base.ChangePasswordAsync(userId, currentPassword, newPassword);

            if (result.Succeeded)
            {
                var store = Store as ApplicationUserStore;
                await store.AddToPreviousPasswordsAsync(await FindByIdAsync(userId), PasswordHasher.HashPassword(newPassword));
            }

            return result;
        }

        public override async Task<IdentityResult> ResetPasswordAsync(string userId, string token, string newPassword)
        {
            if (await IsPreviousPassword(userId, newPassword))
            {
                return await Task.FromResult(IdentityResult.Failed("Cannot reuse old password"));
            }

            var result = await base.ResetPasswordAsync(userId, token, newPassword);

            if (result.Succeeded)
            {
                var store = Store as ApplicationUserStore;
                await store.AddToPreviousPasswordsAsync(await FindByIdAsync(userId), PasswordHasher.HashPassword(newPassword));
            }

            return result;
        }

        private async Task<bool> IsPreviousPassword(string userId, string newPassword)
        {
            var user = await FindByIdAsync(userId);

            if (user.PreviousUserPasswords.OrderByDescending(x => x.CreateDate).
                Select(x => x.PasswordHash).Take(PASSWORD_HISTORY_LIMIT)
                .Where(x => PasswordHasher.VerifyHashedPassword(x, newPassword) != PasswordVerificationResult.Failed).Any())
            {
                return true;
            }

            return false;
        }
    }
فیلد 'PASSWORD_HISTORY_LIMIT' برای دریافت X رکورد از جدول تاریخچه کلمه عبور استفاده می‌شود. همانطور که می‌بینید از متد سازنده کلاس ApplicationUserStore برای گرفتن متد جدیدمان استفاده کرده ایم. هرگاه کاربری سعی می‌کند کلمه عبورش را بازنشانی کند یا تغییر دهد، کلمه عبورش را با 5 کلمه عبور قبلی استفاده شده مقایسه می‌کنیم و بر این اساس مقدار true/false بر می‌گردانیم.

کاربر جدیدی بسازید و به صفحه Manage  بروید. حال سعی کنید کلمه عبور را تغییر دهید و از کلمه عبور فعلی برای مقدار جدید استفاده کنید تا خطای اعتبارسنجی تولید شود. پیغامی مانند تصویر زیر باید دریافت کنید.

سورس کد این مثال را می‌توانید از این لینک دریافت کنید. نام پروژه Identity-PasswordPolicy است، و زیر قسمت Samples/Identity قرار دارد.

مطالب
خودمیزبانی ماژول های Nancy
در ادامه بررسی پروژه Nancy، در این مطلب به میزبانی پروژه‌های Nancy بدون نیاز به Asp.net می‌پردازیم. به این معنی که برنامه اجرایی که شما می‌نویسید خود یک سرور ایجاد می‌کند و کاربر با وارد کردن آدرس دستگاه شما در مرورگر خود، صفحات و ماژول‌های طراحی شده توسط شما را مشاهده می‌کند.
از کاربردهای چنین سیستمی به سایت‌های قابل حمل، و یا ارائه خدمات یک نرم افزار بر روی صفحات html می‌توان اشاره کرد. مثل گوگل دسکتاپ و یا گزارشات برخی سرویس‌های ویندوزی و یا حتی تنظیم یک سخت افزار متصل به سیستم از روی شبکه. یک ایده جالب می‌تواند ارسال اس ام اس از طریق شبکه و با جی اس ام مودم باشد. که به عنوان مثال کاربران با ورود به یک صفحه و ثبت پیام بتوانند از طریق جی اس ام مودم متصل به سرور آن را ارسال کنند. با یک مثال ساده ادامه می‌دهیم.

برای شروع یک پروژه از نوع Console بسازید و در Package manager کتابخانه Nancy.Hosting.Self را نصب کنید.
حالا یک ماژول جدید به نام TestModule.cs به پروژه اضافه می‌کنیم.
public class TestModule:NancyModule
{
 public TestModule()
 {
 Get["/"] = x=> { return "It is a test for nancy self hosting."; };

 }
}

حالا وارد program.cs شده و در متدMain کد زیر را می‌نویسیم:
var selfHost = new NancyHost(new Uri("http://localhost:12345"));
selfHost.Start();
Console.ReadKey();
selfHost.Stop();

در خط اول پورتی که منتظر دریافت درخواست‌های کاربران است را برابر 12345 قرار می‌دهیم. بنابراین برای تست این کد باید در مرورگر آدرس

http://localhost:12345 را تایپ کنید. اگر بخواهیم کاربر عدد انتهایی را وارد نکند باید از پورت 80 استفاده کنیم که پیش فرض http است ولی اکثرا در سیستم برنامه نویس‌ها توسط IIS مشغول می‌باشد.
در خط بعد سرور را اجرا کرده ایم و برنامه را به حالت انتظار برای فشرده شدن کلیدی در کنسول برده ایم.
وقتی کلیدی در کنسول فشرده شود سرور به حالت توقف می‌رود و اجرای برنامه پایان می‌یابد.
Nancy امکانات دیگری هم دارد. به عنوان مثال می‌توان برای طراحی نمای ماژول‌ها از موتور‌های دید استفاده کرد (ViewEngines). موتورهایی مثل Razor و ... . در صورت علاقمندی دوستان، در این باره هم خواهم نگاشت. 
مطالب
تعامل و انتقال اطلاعات بین کامپوننت‌ها در Angular – بخش دوم
در قسمت قبل نحوه انتقال اطلاعات از کامپونت پدر به فرزند را از طریق متادیتای Input@ برسی کردیم. در اینجا نکات تکمیلی را مورد بحث قرار خواهیم داد.
همانطور که قبلا مشاهده کردید، نام متغیر تعریف شده در کامپوننت فرزند (FormIsReadOnly) به عنوان یک خصوصیت در هنگام استفاده از کامپوننت ظاهر شده و عمل انقیاد از طریق این خصوصیت FormIsReadOnly صورت می‌گیرد. در صورتیکه قصد دارید نام خصوصیت ظاهر شده در کامپوننت، با نام متغیر تعریف شده در کامپوننت فرزند متفاوت باشد، به شکل زیر عمل کنید. 
@Input('readOnly') FormIsReadOnly: boolean;
در این حالت عمل انقیاد از طریق خصوصیتی با نام readOnly صورت خواهد گرفت.

ردیابی تغییرات اعمال شده بر روی خصوصیت 

در برخی موارد لازم است بعد از انتساب مقداری از سمت کامپوننت پدر به کامپوننت فرزند و قبل از استفاده کامپوننت فرزند از آن مقدار، تغییرات یا اعتبار سنجی بر روی مقدار منتسب شده اعمال کنیم. مثلا فرض کنید کامپوننتی را به نام LeftSideMenu تعریف کرده‌اید که باز بودن یا بسته بودن آن توسط کامپوننت پدر تنظیم میشود. در اینجا لازم است همواره منتظر تغییر این خصوصیت از سمت کامپوننت پدر بود تا بلافاصله بعد از تنظیم این خصوصیت، کامپوننت فرزند نسبت به باز شدن یا بسته ماندن، عکس العمل نشان داده و بلافاصله تغییرات را اعمال کند (منو را باز کند یا ببندد). لازمه این کار ردیابی تغییرات اعمال شده از سمت کامپوننت پدر می‌باشد تا به محض تغییر، اصلاحات یا اعتبار سنجی‌های لازم بر روی آن اعمال شود. برای این کار دو راه حل وجود خواهد داشت. 
  1. ردیابی تغییرات صورت گرفته از طریق تنظیم setter به متغیر تعریف شده با متادیتای Input@
  2. پیاده سازی onChanges توسط کامپوننت فرزند جهت ردیابی تغییرات کامپوننت 


ردیابی تغییرات از طریق تنظیم setter

همانطور که گفته شد استفاده از کامپوننت فرزند به شکل زیر:
<app-customer-info FormIsReadOnly="true"></app-customer-info>
باعث خواهد شد مقدار انتساب یافته به FormIsReadOnly از جنس رشته‌ای باشد (یعنی "true"). در اینجا می‌خواهیم قبلا از اینکه مقدار، از طریق کامپوننت پدر به فرزند مقدار دهی شود، برسی کنیم در صورتیکه مقدار انتسابی از جنس boolean نبود، خطایی را صادر و برنامه نویس را برای این استفاده نادرست از کامپوننت هشیار کنیم: 
@Component({
    selector: 'app-customer-info',
    templateUrl: './customer-info.component.html',
    styleUrls: ['./customer-info.component.css']
})
export class CustomerInfoComponent implements OnInit {
    private _formIsReadOnly: boolean;

    @Input()
    set FormIsReadOnly(value: boolean) {
        if (typeof (value) != 'boolean')
            throw new Error(`${value} type is not boolean.`);
        this._formIsReadOnly = value;
    }

    get FormIsReadOnly(): boolean { return this._formIsReadOnly; }

    constructor() { }

    ngOnInit() {
    }
}
با تنظیم setter بر روی متغیر FormIsReadOnly، لازمه‌ی تمامی تغییرات بر روی این متغیر، اجرای آن setter خواهد بود. در اینجا برسی کردیم در صورتیکه نوع مقدار (typeof(value)) از جنس boolean نباشد، خطایی صادر شود. 


پیاده سازی onChanges توسط کامپوننت فرزند جهت ردیابی تغییرات کامپوننت 

یکی دیگر از راه‌های تشخیص تغییرات اعمال شده بر روی کامپوننت، پیاده سازی اینترفیس onChanges توسط کامپوننت و پیاده سازی متد تعریف شده در این اینترفیس به نام ngOnChanges می‌باشد. 
import { Component, OnInit, Input, OnChanges, SimpleChange } from '@angular/core';
import { ICustomerInfo } from '../../core/model/ICustomerInfo';

@Component({
    selector: 'app-customer-info',
    template: '<ul>< li *ngFor="let change of changeLog">{{change }}</li></ul>',
    styleUrls: ['./customer-info.component.css']
})
export class CustomerInfoComponent implements OnChanges {
    changeLog: string[] = [];

    @Input() FormIsReadOnly: boolean;

    ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
        let log: string[] = [];
        for (let propName in changes) {
            let changedProp = changes[propName];
            let to = JSON.stringify(changedProp.currentValue);
            if (changedProp.isFirstChange()) {
                log.push(`Initial value of ${propName} set to ${to}`);
            } else {
                let from = JSON.stringify(changedProp.previousValue);
                log.push(`${propName} changed from ${from} to ${to}`);
            }
        }
        this.changeLog.push(log.join(', '));
    }

    constructor() { }
}
این کامپوننت تمامی تغییرات اعمال شده بر روی FormIsReadOnly را ردیابی کرده و نمایش خواهد داد. نمونه خروجی به شکل زیر خواهد بود. 
•    Initial value of FormIsReadOnly set to true
•    FormIsReadOnly changed from true to "trued"
•    FormIsReadOnly changed from "trued" to "true"
•    FormIsReadOnly changed from "true" to "truef"
•    FormIsReadOnly changed from "truef" to "true"
•    FormIsReadOnly changed from "true" to "tru"
•    FormIsReadOnly changed from "tru" to "tr"
•    FormIsReadOnly changed from "tr" to "t"
•    FormIsReadOnly changed from "t" to ""
•    FormIsReadOnly changed from "" to "t"
•    FormIsReadOnly changed from "t" to "tr"
•    FormIsReadOnly changed from "tr" to "tru"
•    FormIsReadOnly changed from "tru" to "true"

در ادامه عنوان «به‌جریان انداختن رخدادها از کامپوننت فرزند و گرفتن آن‌ها را از طریق کامپوننت پدر» را مورد برسی قرار خواهیم داد.


ادامه دارد/

مطالب
استفاده از GZip توکار IISهای جدید و تنظیمات مرتبط با آن‌ها
یکی از نقش‌های IISهای جدید (از نگارش 7 به بعد) که در ویندوز سرورهای قابل نصب است، نقش Performance است و ذیل آن دو نقش فشرده سازی استاتیک و پویا قابل انتخاب است. اگر این نقش‌ها بر روی سرور نصب باشند، دیگر نیازی به استفاده از HTTP Moduleهای متداول فشرده سازی صفحات وب نیست. برای استفاده‌ی از آن تنها کافی است کمی web.config را ویرایش کرد و ... گفته شده‌است که کار می‌کند! اما پس از اعمال تنظیمات، اگر به هدرهای خروجی Response صفحه در ابزارهای web developer مرورگرها دقت کنید، خبری از encoding جدیدی به نام gzip نیست (Content-Encoding: gzip) و به نظر اعمال نمی‌شود. در ادامه بررسی خواهیم کرد که چرا اینگونه است.


فعال سازی GZip توکار IIS

تنظیمات پیش فرض فعال سازی ماژول توکار GZip وب سرورهای جدید شامل دو مرحله است:
الف) تنظیمات سرور جهت فعال سازی فشرده سازی
بر روی ویندوزهای سرور، پس از مراجعه به Administrative Tools -> Server Manager و گشودن Roles آن، ذیل قسمت Web Server که در اینجا IIS است، نیاز است نقش جدیدی به نام Performance اضافه شود و مطابق تصویر، هر دو گزینه‌ی فشرده سازی استاتیک و پویا انتخاب گردد.


بنابراین اولین قدم برای عیب یابی کار نکردن GZip توکار IIS، از این مرحله شروع می‌شود که آیا اصلا ماژول مربوطه نصب هست یا خیر؟

ب) تنظیمات برنامه جهت فعال سازی ماژول GZip
پس از اطمینان از نصب ماژول توکار فشرده سازی صفحات وب IIS در سمت تنظیمات سرور، اکنون باید چند سطر ذیل را به Web.Config برنامه اضافه کرد:
  <system.webServer>

    <httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files">
      <scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" staticCompressionLevel="9" />
      <dynamicTypes>
        <add mimeType="text/*" enabled="true" />
        <add mimeType="message/*" enabled="true" />
        <add mimeType="application/x-javascript" enabled="true" />
        <add mimeType="application/javascript" enabled="true" />
        <add mimeType="application/json" enabled="true" />
        <add mimeType="application/json; charset=utf-8" enabled="true" />
        <add mimeType="application/atom+xml" enabled="true" />
        <add mimeType="application/xaml+xml" enabled="true" />
        <add mimeType="*/*" enabled="false" />
      </dynamicTypes>
      <staticTypes>
        <add mimeType="text/*" enabled="true" />
        <add mimeType="message/*" enabled="true" />
        <add mimeType="application/x-javascript" enabled="true" />
        <add mimeType="application/javascript" enabled="true" />
        <add mimeType="application/json" enabled="true" />
        <add mimeType="application/json; charset=utf-8" enabled="true" />
        <add mimeType="application/atom+xml" enabled="true" />
        <add mimeType="application/xaml+xml" enabled="true" />
        <add mimeType="*/*" enabled="false" />
      </staticTypes>
    </httpCompression>
    <urlCompression doStaticCompression="true" doDynamicCompression="true" />

  </system.webServer>
در اینجا تنظیمات مخصوص نحوه‌ی فعال سازی فشرده سازی توکار صفحات پویا و فایل‌های استاتیک را مشاهده می‌کنید. در این تنظیمات محل قرارگیری فایل‌های موقتی فشرده شده‌ی توسط این ماژول و همچنین mime typeهای مدنظر جهت فشرده سازی، ذکر شده‌اند. با این تنظیمات، تنها mime typeهایی که به صورت صریح ذکر شده‌اند فشرده خواهند شد و از سایر mime types صرفنظر می‌شود.
این تنظیماتی است که در اکثر سایت‌ها نیز یافت می‌شود. ذکر آن‌ها اجباری است و پس از اعمال، اگر برنامه را اجرا کنید ... چیزی فشرده نمی‌شود! علت اصلی را باید در تنظیماتی یافت که مخصوص سرور است و در اینجا ذکر نشده‌اند.


تنظیمات مخصوص آستانه‌ی فشرده سازی صفحات

علت اصلی عدم مشاهده‌ی هدر gzip، در Response برنامه، به frequent hit threshold تنظیم شده‌ی در IIS بر می‌گردد. مقدار آن به 2 درخواست در طی 10 ثانیه تنظیم شده‌است. یعنی اگر به صفحه‌ای در طی 10 ثانیه دو درخواست نرسد، فشرده نخواهد شد. این تنظیم را می‌توان با مراجعه‌ی به configuration editor نود اصلی سرور وب در IIS manager، ویرایش کرد:



برای نمونه در تصویر فوق، این آستانه به یک درخواست در طی 10 ساعت تنظیم شده‌است. این عدد سبب خواهد شد تا تمامی درخواست‌های رسیده حتما فشرده سازی شوند.
این تنظیم معادل یک سطر ذیل در فایل web.config است. اما چون قسمت system.webServer/serverRuntime در تنظیمات سرور قفل شده‌است، هیچ تاثیری نخواهد داشت و حتما باید در سمت سرور و توسط IIS manager اعمال شود:
<system.webServer>
   <serverRuntime frequentHitThreshold="1" frequentHitTimePeriod="10:00:00" />
</system.webServer>
برای آزاد سازی این تنظیمات نیاز است دستور ذیل بر روی سرور اجرا شود. پس از آن کاربران برنامه‌های وب می‌توانند از تنظیمات وب کانفیگ خاص خود استفاده کنند:
 C:\Windows\System32\inetsrv\appcmd.exe unlock config /section:system.webServer/serverRuntime

یک نکته
اگر از سرورهای پس از 2008 استفاده می‌کنید، گزینه‌ی staticCompressionIgnoreHitFrequency نیز به تنظیمات serverRuntime اضافه شده‌است که با تنظیم آن به true، از این حد آستانه، برای فایل‌های استاتیک صرفنظر خواهد شد.


تنظیمات مخصوص اندازه‌ی فایل‌هایی که باید فشرده سازی شوند

تنها حد آستانه‌ی درخواست صفحات وب نیست که بر روی فشرده سازی یا عدم آن ثاثیرگذار است. در اینجا میزان CPU Usage سیستم و یا حتی اندازه‌ی Response خروجی نیز مهم هستند که نمونه‌ای از تنظیمات آن‌را در شکل ذیل مشاهده می‌کنید:


در اینجا با تنظیم minFileSizeForComp به 1024، اعلام شده‌است که حجم‌هایی کمتر از یک کیلوبایت، فشرده سازی نشوند (مقدار پیش فرض آن، بیش از این عدد است).
البته این عدد را به شکل زیر نیز می‌توان به تنظیمات httpCompression وب کانفیگ اضافه کرد:
 <httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files" minFileSizeForComp="1024">

پس از اعمال این تنظیمات نیاز است یکبار IIS را نیز ری استارت کرد.


نتیجه گیری

اگر پس از فعال سازی GZip وب سرور، خروجی برنامه فشرده سازی نشد (Content-Encoding: gzip)، علت اینجا است که هنوز 2 درخواست مورد نیاز، در طی 10 ثانیه به سمت سرور ارسال نشده‌اند و تنظیمات پیش فرض این ماژول، جهت حداقل مصرف CPU و فشار بر روی سرور است.
نظرات مطالب
اعمال تزریق وابستگی‌ها به مثال رسمی 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.
ممنون میشم یه مقدار بنده رو راهنمایی کنید تو این مبحث یا اگه پیاده سازی و مثال خاصی با پیاده سازی در این زمینه می‌شناسید به بنده معرفی کنید.
مطالب
نحوه استفاده از Text template ها در دات نت - قسمت اول
 یکی از امکانات کمتر شناخته شده در دات نت، امکان تولید اتوماتیک کد (Code Generator)، توسط فایلهایی به عنوان Text template می‌باشد. اگر چه فایلهای Text template با پسوند tt  از Visual Studio 2008 بطور آشکار به IDE اضافه گردیده‌اند، این امکان پیش از این نیز بصورت توکار در Visual Studio جهت تولید کد دات نت برای ابزارهایی مانند Dataset ، Report  و ... وجود داشته است. در حال حاضر Visual Studio بصورت توکار  از این امکان برای تولیدکلاسها و  کد‌های EntityFramework ( edmx ) ،dbml  ، Dataset  و ... استفاده می‌نماید. 
شما نیز با دانستن قواعد ساده کد نویسی برای Text template‌ها می‌توانید از این امکان برای تولید اتوماتیک کلاسها، HTML ، XML  و بطور کلی هر نوع فایل متنی استفاده نمایید. کاربردهای عملی Text template بیشتر برای تولید کد از روی دیتابیس و مثلا بر اساس فیلد‌های هر جدول و امثال آن می‌باشد. اما با کمی خلاقیت استفاده‌های بسیار زیاد و جالبی را می‌توانید از آن مشاهده نمایید.
کاربردهایی مانند : تولید خود کار فایل Config، تولید خودکار  Unit Test، تولید خودکار کلاسهای CRUD برای هر موجودیت در لایه Data، تولید خودکار SQL برای StoredProcedure ها، تولید خودکار Html و Js، تولید خودکار فرمهای ASPX و ... برای فرمهای ثابت و تکراری با فرآیند مشخص، تولید خودکار مستندات پروژه در قالب Word Document  , …  ،  تولید خودکار فایلهای Excel از اطلاعات خاص و تولید خودکار فرمهای xaml و .....

در این آموزش با قواعد اصلی نوشتن کد برای Text templateها آشنا می‌شویم و چند نمونه از کاربردهای آن را به عنوان مثال کاربردی مورد بررسی قرار خواهیم داد.
برای شروع قبل از توضیح در مورد قواعد کد نویسی Text template، یک فایل Text template ایجاد نمایید. برای ایجاد، از پنجره Add New Item  یک فایل Text template به پروژه اضافه نمایید:
 


توجه داشته باشید که نام فایل خروجی دقیقا هم نام با فایل tt مشخص شده خواهد بود. اما پسوند آن بسته به تنظیمات داخل آن متفاوت است. با اضافه کردن Text template داخل فایل، باید کد‌های زیر را مشاهده نمایید؛ در غیر این صورت آن را بنویسید:
 <#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".txt" #>
Language زبانی را مشخص می‌کند که می‌خواهید با آن داخل فایل Text template کد نویسی نمایید. تعیین آن برای کامپایل الزامی است.
Extension نیز پسوند فایل خروجی تولید شده را مشخص می‌نماید . 
اگر در ادامه متن Hello dotnettips را بنویسید و فایل را Save کنید، کنار فایل tt مذکور، یک فایل با همان نام و پسوند txt ایجاد خواهد شد که داخل آن نوشته است:
Hello dotnettips

برای ایجاد فایل خروجی همچنین میتوانید روی فایل tt  کلیک راست نموده و از منوی باز شده  Run Custom Tool  را انتخاب نمایید. توجه داشته باشید که در تنظیمات Custom Tool باید مقدار  TextTemplatingFileGenerator وجود داشته باشد:
 

خوب؛ برای ادامه، باید با قواعد کد نویسی در Text template آشنا شوید. جلسه بعدی قواعد کد نویسی T4  را بررسی خواهیم کرد.