‫۶ سال و ۷ ماه قبل، یکشنبه ۶ اسفند ۱۳۹۶، ساعت ۱۵:۰۵
یک نکته‌ی تکمیلی: تغییرات در فایل angular-cli.json نیاز به راه اندازی مجدد watchers را دارد

فرض کنید برنامه را توسط دستورات ng build --watch و یا ng serve -o تهیه و یا اجرا کرده‌اید. در این حال، برای مثال جهت افزودن مجموعه آیکن‌های قلم font-awesome به صورت زیر عمل کرده‌اید:
 npm install font-awesome --save
و سپس تعریف css آن‌را نیز به قسمت styles فایل angular-cli.json افزوده‌اید:
"styles": [
   "../node_modules/bootstrap/dist/css/bootstrap.css",
   "../node_modules/font-awesome/css/font-awesome.css",
   "styles.css"
],
اکنون اگر برنامه مجددا به صورت خودکار build شود، فونت‌ها و آیکن‌ها را مشاهده نخواهید کرد. علت اینجا است که دستورات یاد شده که در حالت watch اجرا می‌شوند، تنها به تغییرات پوشه‌ی src واکنش نشان می‌دهند و تغییرات فایل angular-cli.json از دید آن‌ها مخفی است.
به همین جهت باید یکبار آن‌ها را بسته و مجددا از ابتدا اجرا کنید تا اینبار قلم آیکن font-awesome اعمال شده و قابل نمایش شود.
یک نکته‌ی تکمیلی: روش دیگری برای بهبود کنترل نمایش و مخفی سازی قسمت‌های مختلف صفحه

کدهای یک دایرکتیو سفارشی نمایش و یا مخفی سازی قسمت‌های مختلف صفحه را بر اساس سطوح دسترسی کاربر جاری، در IsVisibleForAuthUserDirective مشاهده کردید. روش دیگر انجام اینکار، نوشتن یک دایرکتیو ساختاری شبیه به ngIf توکار خود Angular است. کاری که ngIf انجام می‌دهد، مخفی کردن یک المان در صفحه نیست؛ بلکه کل آن‌را از DOM حذف می‌کند.

نکته‌ی اصلی پیاده سازی یک دایرکتیو ساختاری

اگر به سازنده‌ی IsVisibleForAuthUserDirective دقت کنید، تزریق وابستگی ElementRef را داریم:
@Directive({
  selector: "[isVisibleForAuthUser]"
})
export class IsVisibleForAuthUserDirective implements OnInit, OnDestroy {
  constructor(private elem: ElementRef, private authService: AuthService) { }
به این ترتیب Angular به صورت خودکار امکان دسترسی به المان جاری را با تزریق آن در سازنده‌ی کلاس، میسر می‌کند.
برای ایجاد یک دایرکتیور ساختاری، نیاز است از تزریق TemplateRef و ViewContainerRef استفاده کرد:
@Directive({
selector: "[hasAuthUserViewPermission]"
})
export class HasAuthUserViewPermissionDirective implements OnInit, OnDestroy {

  constructor(
  private templateRef: TemplateRef<any>,
  private viewContainer: ViewContainerRef,
  private authService: AuthService
) { }
در این حالت Angular تنها زمانی این وابستگی‌ها را به سازنده‌ی کلاس تزریق می‌کند که پیش از نام این دایرکتیو، یک * قرار دهید (مانند ngIf توکار آن):
<div *hasAuthUserViewPermission="['Admin','User']">
    *hasAuthUserViewPermission="['Admin','User']"
</div>
پس از آن می‌توان از viewContainer، برای نمایش المان و یا قالب مدنظر:
 this.viewContainer.createEmbeddedView(this.templateRef);
و یا حذف کامل آن المان از DOM کمک گرفت:
 this.viewContainer.clear();

اگر این نکات را کنار هم قرار دهیم و کدهای IsVisibleForAuthUserDirective فوق را بر این اساس اصلاح کنیم، به قطعه کد زیر می‌رسیم:
import { Directive, Input, OnDestroy, OnInit, TemplateRef, ViewContainerRef } from "@angular/core";
import { Subscription } from "rxjs/Subscription";

import { AuthService } from "./../../core/services/auth.service";

@Directive({
  selector: "[hasAuthUserViewPermission]"
})
export class HasAuthUserViewPermissionDirective implements OnInit, OnDestroy {
  private isVisible = false;
  private requiredRoles: string[] | null = null;
  private subscription: Subscription | null = null;

  @Input()
  set hasAuthUserViewPermission(roles: string[] | null) {
    this.requiredRoles = roles;
  }

  // Note, if you don't place the * in front, you won't be able to inject the TemplateRef<any> or ViewContainerRef into your directive.
  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef,
    private authService: AuthService
  ) { }

  ngOnInit() {
    this.subscription = this.authService.authStatus$.subscribe(status => this.changeVisibility(status));
    this.changeVisibility(this.authService.isAuthUserLoggedIn());
  }

  ngOnDestroy(): void {
    if (this.subscription) {
      this.subscription.unsubscribe();
    }
  }

  private changeVisibility(status: boolean) {
    const isInRoles = !this.requiredRoles ? true : this.authService.isAuthUserInRoles(this.requiredRoles);
    if (isInRoles && status) {
      if (!this.isVisible) {
        this.viewContainer.createEmbeddedView(this.templateRef);
        this.isVisible = true;
      }
    } else {
      this.isVisible = false;
      this.viewContainer.clear();
    }
  }
}

سپس تعریف آن‌را به قسمت‌های declarations و exports مربوط به SharedModule اضافه می‌کنیم:
import { HasAuthUserViewPermissionDirective } from "./directives/has-auth-user-view-permission.directive";

@NgModule({
  declarations: [
    HasAuthUserViewPermissionDirective
  ],
  exports: [
    HasAuthUserViewPermissionDirective
  ]
})
export class SharedModule {}

اکنون ماژول Dashboard برای استفاده‌ی از این امکانات تنها کافی است SharedModule را دریافت کند (یا هر ماژول دیگری نیز به همین ترتیب است):
import { SharedModule } from "../shared/shared.module";
@NgModule({
  imports: [
    SharedModule
  ]
})
export class DashboardModule { }

پس از آن برای مخفی سازی یک المان از دید کاربران وارد نشده‌ی به سیستم، فقط کافی است دایرکتیو ساختاری hasAuthUserViewPermission را به المان اعمال کنیم:
<div *hasAuthUserViewPermission="">
    *hasAuthUserViewPermission=""
</div>
و یا اگر نیاز به اعمال نقش‌ها نیز وجود داشت می‌توان به صورت زیر عمل کرد:
<div *hasAuthUserViewPermission="['Admin','User']">
    *hasAuthUserViewPermission="['Admin','User']"
</div>

خلاصه‌ی این تغییرات به کدهای نهایی این سری اعمال شده‌اند.
‫۶ سال و ۷ ماه قبل، پنجشنبه ۳ اسفند ۱۳۹۶، ساعت ۱۴:۳۶
یک نکته‌ی تکمیلی: امکان تعریف خروجی از نوع ref readonly در C# 7.2

modifier جدیدی در C# 7.2 به نام ref readonly جهت تعریف نوع خروجی متدها نیز معرفی شده‌است. به این ترتیب یک متد می‌تواند بازگشت ارجاعی به اطلاعاتی موجود را بیان و همچنین فراخوان را از تغییر آن منع کند.
البته فراخوان می‌تواند تصمیم گیری کند که آیا یک کپی و یا یک ارجاع فقط خواندنی را از این متد ویژه دریافت کند. به این معنا که خروجی از نوع ref readonly، فراخوان را ملزم به تعریف یک متغیر محلی از نوع ref readonly نمی‌کند.

در مثال زیر، متد ReturnBiggestA یک خروجی کپی را باز می‌گرداند و متد ReturnBiggestARefReadonly دقیقا ارجاعی را به DataInfo اصلی بازگشت می‌دهد و با آن یکی است:
namespace CS72Tests
{
    public struct DataInfo
    {
        public double A;
    }

    public class RefReadonlyExamples
    {
        public DataInfo ReturnBiggestA(in DataInfo data1, in DataInfo data2)
        {
            return data1.A > data2.A ? data1 : data2;
        }

        public ref readonly DataInfo ReturnBiggestARefReadonly(in DataInfo data1, in DataInfo data2)
        {
            if (data1.A > data2.A)
            {
                return ref data1;
            }
            return ref data2;
        }

        public void TestingRefReadonly()
        {
            var data1 = new DataInfo { A = 0 };
            var data2 = new DataInfo { A = 100 };

            var biggest = ReturnBiggestA(data1, data2);
            biggest.A = 42;


            var biggest2 = ReturnBiggestARefReadonly(data1, data2);
            biggest2.A = 99;


            ref readonly var biggest3 = ref ReturnBiggestARefReadonly(data1, data2);
            biggest3.A = 99; // ERROR: The left-hand side of an assignment must be a variable, property or indexer
        }
    }
}
- در این فراخوانی‌ها، biggest یک کپی از data2 را باز می‌گرداند. به همین جهت می‌توان A آن‌را تغییر داد.
- در اولین فراخوانی ReturnBiggestARefReadonly، با تعریف خروجی به صورت var biggest2، یک کپی از data2 را دریافت کرده‌ایم. به همین جهت A آن قابل تغییر است.
- اما در دومین فراخوانی ReturnBiggestARefReadonly، چون خروجی آن‌را از نوع ref readonly var دریافت کرده‌ایم، این خروجی به data2 اصلی اشاره می‌کند و همچنین فقط خواندنی است. بنابراین سطر بعدی آن که A را تغییر می‌دهد، مجاز نیست.

پ.ن
در ابتدا قصد داشتند ref readonly را برای تعریف پارامترهای value type نیز بکار برند، اما این تصمیم با معرفی پارامترهای از نوع in جایگزین شد. به همین جهت ممکن است مقالات قدیمی‌تر C# 7.2 را با تعریف متدهایی مانند ذیل نیز مشاهده کنید که در نگارش آخر C# 7.2، تمام این‌ها به in تغییر کرده‌اند:
public static void Add(ref readonly int x, ref readonly int y, ref int z)
{
   z = x + y + z;
}
‫۶ سال و ۷ ماه قبل، چهارشنبه ۲ اسفند ۱۳۹۶، ساعت ۱۵:۲۳
«بهبود عملکرد» با «بهبود کارآیی» یکی نیست. پیاده سازی سیستم change tracking در حالت کلی، بدون پیاده سازی مباحث AOP غیرممکن است. بهتر است دوره‌ی مرتبطی را در سایت در این مورد مرور کنید تا کلیات بحث تشکیل Proxyها بهتر مشخص شوند (و ... تشکیل پروکسی با روش‌های مختلفی و با الگوریتم‌های متفاوتی ممکن است و مهم نیست که dynamic proxy چندسکویی باشد یا خیر؛ این مورد نام یک الگوی طراحی شیء گرا است و نه یک کتابخانه‌ی خاص). هدف من از عنوان این مسایل، اشاره به کلیات زیرساخت پیاده سازی این مباحث هست.
برای نمونه زمانیکه مقدار خاصیت شیء واکشی شده‌ای از Context را تغییر می‌دهید و سپس SaveChanges را فراخوانی می‌کنید، در این بین یک پروکسی وجود دارد (یک لایه‌ی نامرئی و حائل بین شیء اصلی و تغییراتی که قرار است به آن اعمال شوند) که به تغییرات گوش فرا می‌دهد و در نهایت صرفا یک کوئری به روز رسانی آن فیلد خاص را تولید می‌کند و نه تمام فیلدهای دیگر را. این نوع مفاهیم کلی در اینجا مدنظر هستند. یک نمونه پیاده سازی کلی این مفهوم را در اینجا می‌توانید مشاهده کنید.
همچنین EF Core 2.1 به همراه بسته‌ی Microsoft.EntityFrameworkCore.Proxies است که پیاده سازی Lazy loading را میسر کرده‌است و از Castel.Core هم استفاده می‌کند (یا همان Castle DynamicProxy که در دوره «Aspect oriented programming» مورد بررسی قرار گرفته‌است).
‫۶ سال و ۷ ماه قبل، سه‌شنبه ۱ اسفند ۱۳۹۶، ساعت ۱۲:۰۴
یک نکته‌ی تکمیلی: پشتیبانی توکار از به روز رسانی بسته‌های Angular
به همراه Angular CLI نگارش 1.7، دستور جدیدی به نام ng update اضافه شده‌است که تمام وابستگی‌های مرتبط به Angular را به صورت خودکار به روز رسانی می‌کند.
‫۶ سال و ۷ ماه قبل، دوشنبه ۳۰ بهمن ۱۳۹۶، ساعت ۱۲:۳۹
یک نکته‌ی تکمیلی: بهبود جزئی آرگومان‌های نامدار در C# 7.2

تا پیش از C# 7.2، آرگومان‌های نامدار، تنها پس از ذکر آرگومان‌های بدون نام، مجاز بودند. برای مثال اگر امضای متدی به صورت زیر باشد:
public static void Write(int age, string name, string homeTown)
فراخوانی آن به صورت زیر تا C# 7.2 مجاز نبود:
Write(age: 20, "User 1", homeTown: "Tehran");
و باخطای کامپایلر زیر، کامپایل نمی‌شد:
Named argument specifications must appear after all fixed arguments have been specified.
این محدودیت در C# 7.2 برطرف شده‌است؛ به این شرط که موقعیت پارامترها تغییری نکنند و پارامترها دقیقا در همانجایی که قرار است باشند، معرفی شوند.
در این حالت تمام فراخوانی‌های ذیل در C# 7.2 مجاز هستند:
Write(age: 20, name: "User 1", "T1");
Write(age: 21, "User 2", homeTown: "T2");
Write(age: 22, "User 3", "T3");
Write(23, name: "User 4", "T4");