اشتراک‌ها
پروژه CircuitBreaker.Net

CircuitBreaker.Net is an implementation of the Circuit Breaker pattern for .NET. This pattern can improve the stability and resiliency of your application, especially in SOAP, microservices and distributed environments.  

پروژه CircuitBreaker.Net
اشتراک‌ها
مقایسۀ ماژول های داخلی و خارجی TypeScript

We have done Javascript development for quite a few years and we are quite proficient at it as a team. Still there is room for improvement. We really dislike the syntax of the revealing module pattern and we love strongly typed languages for the compile time checks it provides

مقایسۀ ماژول های داخلی و خارجی TypeScript
مطالب
نگاشت اشیاء در AutoMapper توسط Attribute ها #2 - تبدیل ویژگی‌ها به نگاشت
پس از معرفی ویژگی‌های لازم، در ادامه با نحوه‌ی تبدیل این ویژگی‌ها به معادل نگاشت آن‌ها در automapper خواهم پرداخت.
متد زیر هسته‌ی اصلی عملیات است و کلیه‌ی نگاشت‌های لازم را انجام می‌دهد. این متد وظیفه‌ی تبدیل نگاشت‌ها را دارد. نگاشت‌هایی که با Attributes مشخص شده‌اند:
 public static void Initialize(Assembly assembly)
 {
     //register global convertors.
     AutoMapper.Mapper.CreateMap<DateTime, string>().ConvertUsing<DateTimeToPersianDateTimeConverter>();

     var typesToMap = from t in assembly.GetTypes()
         let attr = t.GetCustomAttribute<MapFromAttribute>()
         where attr != null
         select new {SourceType = attr.SourceType, Destination = t, Attribute = attr};

     foreach (var map in typesToMap)
     {
         AutoMapper.Mapper.CreateMap(map.SourceType, map.Destination)             
             .DoMapForMemberAttribute() // for different property names in source and destination
             .DoIgnoreMapAttribute()// ignore specified properties
             .DoUseValueResolverAttribute()// set value resolvers
             .DoIgnoreAllNonExisting()// its have to be the latest.
             ;
     } //endeach
     AutoMapper.Mapper.AssertConfigurationIsValid();
 }
ورودی این متد اسملبی مربوط به ویوومدل می‌باشد (برای زمانیکه ویوومدل‌ها در اسمبلی دیگری باشند).
در سطر اول، اقدام به رجیستر کردن کلیه‌ی مبدل‌های سراسری می‌کنیم. در این سطر مبدل تاریخ به کوچی خورشیدی مورد استفاده قرار گرفته است. سپس در اسمبلی داده شده، کلیه نوع‌هایی که ویژگی MapFromAttribute را دارند، یافته و جدا می‌کنیم. در حلقه‌ی foreach ابتدا نگاشت نوع مبدأ و مقصد را انجام می‌دهیم. خروجی این متد از نوع IMappingExpression است. گر چه این اینترفیس برای تغییر بسته است، ولی قابل توسعه می‌باشد و عملیات را توسط متدهای الحاقی انجام می‌دهیم(اصل OCP).
اگر به نحوه‌ی نامگذاری متدهای الحاقی تعریف شده دقت کرده باشید، تنها کلمه‌ی Do به ابتدای نام ویژگی‌ها اضافه شده است
.

متد الحاقی DoMapFormMemberAttribute
public static IMappingExpression DoMapForMemberAttribute(this IMappingExpression expression)
{
    var ok =
        from p in expression.TypeMap.DestinationType.GetProperties()
        let attr = p.GetCustomAttribute<MapForMemberAttribute>()
        where attr != null
        select new {AttributeValue = attr, PropertyName = p.Name};

     foreach (var property in ok)
     {
         expression.ForMember(property.PropertyName, 
             opt => opt.MapFrom(property.AttributeValue.MemberToMap));
     }
    return expression;
}
هر IMappingExpression دارای امکاناتی برای نگهداری و انجام فعالیت بر روی یک نگاشت می‌باشد. در کوئری ابتدای متد، کلیه‌ی پروپرتی‌هایی را که دارای ویژگی MapForMemeberAttribute می‌باشند، یافته و جدا می‌کنیم. این پروپرتی‌ها از نظر معادل اسمی در نوع مبدأ و مقصد متفاوت هستند. سپس در حلقه، کار اتصال پروپرتی مبدأ و مقصد صورت می‌گیرد.

متد الحاقی DoIgnoreMapAttribute  
public static IMappingExpression DoIgnoreAttribute(this IMappingExpression expression)
{
    foreach (var property in
        expression.TypeMap.DestinationType.GetProperties()
        .Where(x => x.GetCustomAttribute<IgnoreMapAttribute>() != null))
    {
        expression.ForMember(property.Name, opt => opt.Ignore());
    }
    return expression;
}
این متد کلیه‌ی پروپرتی‌هایی را که دارای ویژگی IgnoreMapAttribute باشند، از گردونه‌ی نگاشت automapper خارج می‌کند. به عنوان مثال پروپرتی Password در ویوومدل مربوط به تغییر گذرواژه را نظر بگیرید. این پروپرتی نباید مقدار معادلی در شیء EF داشته باشد. از طرفی هم باید در ویوو وجودداشته باشد. با استفاده از این ویژگی هیچ نگاشتی انجام نمی‌شود و می‌توان تضمین کرد که گذرواژه به ویوومدل و ویوو راه پیدا نمی‌کند.

متد الحاقی DoUseValueResolverAttribute 
public static IMappingExpression DoUseValueResolverAttribute(this IMappingExpression expression)
{
    var ok =
        from p in expression.TypeMap.DestinationType.GetProperties()
        let attr = p.GetCustomAttribute<UseValueResolverAttribute>()
        where attr != null
        select new {AttributeValue = attr, PropertyName = p.Name};

    foreach (var property in ok)
    {
        expression.ForMember(property.PropertyName,
            opt => opt.ResolveUsing(property.AttributeValue.ValueResolver));
    }
    return expression;
}
به شیوه‌ی قبل، ابتدا نوع هایی را که دارای ویژگی UseValueResolverAttribute باشند، یافته و جدا می‌کنیم. سپس در حلقه، کار نگاشت متناظر در automapper انجام می‌گیرد. لازم به ذکر است که متد opt.ResolveUsing یک شیء با کارآیی (can do) اینترفیس IValueResolver را به عنوان آرگومان می‌گیرد.

متد الحاقی DoIgnoreAllNonExisting  
public static IMappingExpression DoIgnoreAllNonExisting(this IMappingExpression expression)
{
    var attr = expression.TypeMap.DestinationType.GetCustomAttribute<MapFromAttribute>();
    
    if (attr?.IgnoreAllNonExistingProperty == false)//instead of if(attr == null || attr.IgnoreAllNonExistingProperty == false)
        return expression;
    
    foreach (var property in expression.TypeMap.GetUnmappedPropertyNames())
    {
        expression.ForMember(property, opt => opt.Ignore());
    }
    return expression;
}
این متد برحسب پرچم تعیین شده در هنگام بکارگیری ویژگی MapFromAttribute رفتار می‌کند. به این صورت که اگر موقع تعریف، مقدار IgnoreAllNonExistingProperty را صحیح اعلام کنیم، تمام پروپرتی‌های مقصد را که معادل اسمی در مبدأ نداشته باشند و همچنین هیچگونه تنظیمی جهت مشخص سازی تکلیف نگاشت آن‌ها صورت نگرفته باشد، از گردونه‌ی نگاشت Automapper خارج می‌کند.

توضیح تکمیلی:
پس از تنظیم کلیه‌ی نگاشت‌ها در automapper جهت اطمینان از صحت تنظیمات، فراخوانی متد AutoMapper.Mapper.AssertConfigurationIsValid الزامی است. یکی از عواملی که باعث شکست این متد می‌شود، وجود پروپرتی‌هایی در نوع مقصد است، بطوریکه معادل اسمی در نوع مبدأ نداشته باشند و یا تنظیمی جهت مشخص سازی نگاشت آن انجام نشده باشد (پروپرتی که قابل نگاشت نباشد). در حقیقت این شکست بسیار مفید است. به این صورت که اگر این شکست صورت نگیرد در حین نگاشت مقادیر، باید از null یا مقدار default بدون اطلاع برنامه نویس برای مقداردهی پروپرتی استفاده کند و این یک حالت نامعلوم شیء است. اگر می‌خواهید این پروپرتی‌ها مقدار پیشفرضی بگیرند و همچنین باعث شکست عملیات هم نشوند، باید بطور صریح این موضوع را اعلام کنید. این اعلام یا باید به همین روش صورت بگیرد یا باید از ویژگی IgnorMapAttribute استفاده شود. تنها تفاوت این دو، نحوه‌ی اعمال تنظیم می‌باشد. IgnorMapAttribute باید روی تک تک پروپرتی‌های مدنظر قرار گیرد، ولی در روش اول تنها کافیست که مقدار true تنظیم گردد. به‌نظر استفاده از IgnoreMapAttribute باعث طولانی شدن کدها می‌شود؛ اما توصیه می‌شود که از همین شیوه استفاده کنید.

تا اینجا کدهای مورد نیاز نوشته شدند. در ادامه به ارائه‌ی یک مثال برای نگاشت اشیاء در Automapper توسط Attributeها می‌پردازم.
مدل ساده‌ی زیر را در نظر بگیرید:
public class Student
{
    public virtual int Id { set; get; }
    public virtual string Name { set; get; }
    public virtual string Family { set; get; }
    public virtual string Email { set; get; }
    public virtual DateTime RegisterDateTime { set; get; }
    public virtual ICollection<Book> Books { set; get; }
}
public class Book
{
    public virtual int Id { set; get; }
    public virtual string Name { set; get; }
    public virtual DateTime BorrowDateTime { set; get; }
    public virtual DateTime ExpiredDateTime { set; get; }
    public virtual decimal Price { set; get; }
    [ForeignKey("StudentIdFk")]
    public virtual Student Student { set; get; }
    public virtual int StudentIdFk { set; get; }
}
با ویوومدل متناظر ذیل:
[MapFrom(typeof (Student), ignoreAllNonExistingProperty: true, alsoCopyMetadata: true)]
public class AdminStudentViewModel
{
    // [IgnoreMap]
    public int Id { set; get; }

    [MapForMember("Name")]
    public string FirstName { set; get; }

    [MapForMember("Family")]
    public string LastName { set; get; }

 [IgnoreMap]  public string Email { set; get; } [MapForMember("RegisterDateTime")] public string RegisterDateTimePersian { set; get; } [UseValueResolver(typeof (BookCountValueResolver))] public int BookCounts { set; get; } [UseValueResolver(typeof (BookPriceValueResolver))] public decimal TotalBookPrice { set; get; } };
در تنظیم ویژگی MapFromAttribute ابتدا نوع مبدأ (Student) را مشخص کردیم و بعد صراحتاً گفتیم که از نگاشت پروپرتی‌های بلاتکلیف صرف نظر کند و همچنین پرچم انتقال Data Annotation‌های EF به ویوومدل را هم برافراشتیم. توسط MapForMember پروپرتی FirstName را به پروپرتی Name در مبدأ تنظیم کردیم و LastName را به Family. همچنین Email را بصورت صریح از نگاشت شدن منع کردیم. پروپرتی BookCounts تعداد کتاب‌ها را محاسبه می‌کند و TotalBookPrice قیمت کلیه‌ی کتاب‌ها را. برای این موارد از تأمین کننده‌ی داده (Value Resolver) استفاده کردیم. این تأمین کننده‌ها می‌توانند اینچنین پیاده سازی شوند:
public class BookCountValueResolver : ValueResolver<Student, int>
{
    protected override int ResolveCore(Student source) => source.Books.Count;
};
public class BookPriceValueResolver : ValueResolver<Student, decimal>
{
    protected override decimal ResolveCore(Student source) => source.Books.Sum(b => b.Price);
};
نحوه‌ی پیکربندی و مشاهده‌ی نتایج را در یک برنامه‌ی تحت کنسول پیاده سازی کردم. متد Main آن می‌تواند اینچنین باشد:
static void Main(string[] args)
{
    var assemblyToLoad = Assembly.GetAssembly(typeof (AdminStudentViewModel));//get assembly
    global::AttributesForAutomapper.Configuration.Initialize(assemblyToLoad);//init automaper
    IList<Student> lst;
    using (var context = new MySampleContext())
    {
        lst = context.Students.Include(x => x.Books).ToList();
    }
    foreach (var student in lst)
        {
            WriteLine( $"[{student.Id}]*\n{student.Name} {student.Family}.\nmailto:{student.Email}.\nRegistered at'{student.RegisterDateTime}'");
            foreach (var book in student.Books)
                WriteLine($"\tBook name:{book.Name}, Book price:{book.Price}");
        }
    
    var lstViewModel = AutoMapper.Mapper.Map<IList<Student>, IList<AdminStudentViewModel>>(lst);
    foreach (var adminStudentViewModel in lstViewModel)
    {
        WriteLine(
            $"[{adminStudentViewModel.Id}]*\n\t{adminStudentViewModel.FirstName} {adminStudentViewModel.LastName}.\n\t" +
            $"mailto:{adminStudentViewModel.Email}.\n\tRegistered at'{adminStudentViewModel.RegisterDateTimePersian}'\n\t" +
            $"Book Counts: {adminStudentViewModel.BookCounts} with total price of {adminStudentViewModel.TotalBookPrice}");
    }
    WriteLine("Press any key to exit...");
    ReadKey();
}
ابتدا اسمبلی مربوط به ویوومدل‌ها را مشخص می‌کنیم. سپس این اسمبلی را جهت تبدیل ویژگی‌ها به نگاشت‌های معتبر automapper به متد Initialize ارسال می‌کنیم. تنها بکار بردن همین دوسطر برای اعمال تنظیم‌ها مورد نیاز می‌باشد. بعد از اجرای موفق متد Initialize، نگاشت‌های اشیاء آماده هستند.
نمونه‌ی خروجی:
[1]*
Morteza Raeisi.
mailto:MrRaeisi@outlook.com.
Registered at'23/08/1392 19:11:43'    // I'm using Windows 10 with Persian calendar as default, On other OS or calendar settings, this value is different.
        Book name:AutoMapper Attr, Book price:1000.00
        Book name:Second Book, Book price:2500.00
        Book name:Hungry Book, Book price:2500.00
...
[1]*
Morteza Raeisi. //MapForMemebers
mailto:.  // IgnoreMap
Registered at'1392/08/23 19:11' // Convert using
Book Counts: 3 with total price of 6000.00  // Value resolvers
...
دریافت کدها + مثال
مطالب
بررسی معادل‌های LINQ to Objects در TypeScript
اگر برنامه نویس NET. باشید، پس از مدتی کار با LINQ، در سایر زبان‌های دیگر نیز به دنبال این قابلیت فوق العاده‌ی functional یا تابعی خواهید گشت. در این مطلب، خلاصه‌ای از متدهای توکار جاوا اسکریپت را که می‌توانند معادل‌هایی برای متدهای LINQ to Objects دات نت باشند، بررسی خواهیم کرد.


تدارک ساختار ابتدایی این مطلب

در اینجا اینترفیسی را که بیانگر ساختار شیء شخص است، به صورت ذیل ایجاد می‌کنیم:
export interface Person {
  name: string;
  age: number;
}
سپس آرایه‌ای را بر اساس این شیء تدارک خواهیم دید:
export class LinqTestsComponent {

  people: Person[] = [
    { name: "User 4", age: 27 },
    { name: "User 5", age: 42 },
    { name: "User 6", age: 8 },
    { name: "User 1", age: 20 },
    { name: "User 2", age: 35 },
    { name: "User 3", age: 78 }
  ];

}
در ادامه تمام اعمال مدنظر را بر روی این آرایه انجام می‌دهیم.
همچنین سه متد ذیل را نیز برای لاگ کردن عنوان آزمایش، نمایش محتوای آرایه‌ی اصلی و نمایش نتیجه‌ی آزمایش، به این کلاس اضافه می‌کنیم:
  logTitle(title: string) {
    console.log(`%c${title}`, "background: #222; color: #bada55");
  }

  logOriginalArray() {
    console.log(`original this.people:${JSON.stringify(this.people)}`);
  }

  logResult(message: string, result: any) {
    console.log(`${message}:${JSON.stringify(result)}`);
  }


معادل متد Where در TypeScript

متد filter که جزو متدهای توکار ES5 است، می‌تواند معادلی برای متد Where، جهت فیلتر کردن عناصر بر اساس یک خاصیت، یا چندین خاصیت باشد:
  equivalentToWhere() {
    const youngerThan40 = this.people.filter(person => person.age < 40); // ECMAScript 5
    this.logResult("People younger than 40", youngerThan40);

    // Filtering on Multiple Criteria
    const youngsters = this.people.filter(
      person => person.age < 40 && person.name.toLocaleLowerCase().indexOf("user") !== -1);
    this.logResult("Users younger than 40", youngsters);
  }
با این خروجی
People younger than 40:[
{"name":"User 4","age":27},
{"name":"User 6","age":8},
{"name":"User 1","age":20},
{"name":"User 2","age":35}
]

Users younger than 40:[
{"name":"User 4","age":27},
{"name":"User 6","age":8},
{"name":"User 1","age":20},
{"name":"User 2","age":35}
]
در اینجا نحوه‌ی استفاده‌ی از arrow functions ES6 را نیز جهت ساده سازی تعریف callback متد filter مشاهده می‌کنید که نمایش آن بسیار شبیه به عبارات LINQ در دات نت است.


معادل متد Any در TypeScript

متد some که جزو متدهای توکار ES5 است، می‌تواند معادلی برای متد Any باشد. اگر یکی از عناصر آرایه، بر اساس شرط تعیین شده یافت شود، این متد مقدار true را باز می‌گرداند:
  equivalentToAny() {

    const anyUnder40 = this.people.some(person => person.age < 40); // ECMAScript 5
    this.logResult("Are any people under 40?", anyUnder40); // true

    // Filtering on Criteria Matching any Object Properties
    const filterBy = "user";
    const anyUsers = this.people.filter(person =>
      Object.keys(person).some(property => {
        let value = (<any>person)[property];
        if (typeof value === "string") {
          value = value.toLocaleLowerCase();
        }
        return value.toString().indexOf(filterBy) !== -1;
      })
    );
    this.logResult("anyUsers", anyUsers);
  }
با این خروجی:
Are any people under 40?:true
anyUsers:[
{"name":"User 4","age":27},
{"name":"User 5","age":42},
{"name":"User 6","age":8},
{"name":"User 1","age":20},
{"name":"User 2","age":35},
{"name":"User 3","age":78}
]
در مثال اول، بررسی شده‌است که آیا شخصی با سن کمتر از 40 در این لیست وجود دارد؟
در مثال دوم، جستجویی بر روی تمام خواص شیء شخص انجام شده‌است. در اینجا توسط متد Object.keys، لیست خواص شیء یافت شده‌است. سپس بر روی این لیست توسط متد some، بررسی شده‌است که آیا خاصیت رشته‌ای وجود دارد که مساوی عبارت filterBy باشد؟ حاصل این بررسی به عنوان شرط متد filter جهت بازگشت آرایه‌ی متناظری از اشخاص یافت شده، استفاده شده‌است.


معادل متد ‍Contains در TypeScript

متد includes که جزو متدهای توکار ES7 است، می‌تواند معادلی برای متد Contains باشد و کار آن بررسی وجود عنصری در یک لیست است:
  equivalentToContains() {

    const searchElement: Person = { name: "User 4", age: 27 };
    const containsUser4 = this.people.includes(searchElement); // ECMAScript 2016 = ECMAScript 7
    this.logResult("Contains searchElement", containsUser4); // false -> only compares by reference and not by value.

    const indexOfUser4 = this.people.indexOf(searchElement); // ECMAScript 5
    this.logResult("indexOfUser4", indexOfUser4); // -1 -> only compares by reference and not by value.

    const stringifiedObj = JSON.stringify(searchElement);
    const includesUser4 = this.people.some(person => JSON.stringify(person) === stringifiedObj);
    this.logResult("includesUser4", includesUser4); // true -> compares by by value.
  }
در اینجا باید دقت داشت که اگر آرایه‌ی مدنظر رشته‌ای و یا عددی باشد، متد includes نتیجه‌ی مطلوبی را بازگشت می‌دهد. اما چون در اینجا وجود یک شیء را در لیست اشخاص بررسی می‌کنیم، این مقایسه بر اساس ارجاع عناصر خواهد بود و نتیجه‌ی نهایی یافت شدن آن، منفی است (شیء searchElement هیچ ارجاعی را در آرایه‌ی اشخاص ندارد، هرچند ظاهر آن شبیه به یکی از عناصر آن است). حتی متد indexOf نیز به همین صورت عمل می‌کند.
یکی از روش‌های مقایسه‌ی بر اساس تمام مقادیر خواص یک شیء، استفاده از متد JSON.stringify است که اگر آن‌را با متد some ترکیب کنیم، می‌توان به نتیجه‌ی مطلوبی رسید:
Contains searchElement:false
indexOfUser4:-1
includesUser4:true


معادل متد ‍All در TypeScript

متد every که جزو متدهای توکار ES5 است، می‌تواند معادلی برای متد All باشد و کار آن بررسی صحت شرط اعمالی، بر روی تک تک عناصر لیست است. اگر این بررسی با موفقیت صورت گرفت، مقدار true را بازگشت می‌دهد:
  equivalentToAll() {
    const allUnder30 = this.people.every(person => person.age < 30); // ECMAScript 5
    this.logResult("Are all people under 30?", allUnder30); // false
  }
با این خروجی:
 Are all people under 30?:false


معادل متدهای First و FirstOrDefault در TypeScript

می‌توان از متدهای filter و یا find بومی ES5 و ES 6 برای شبیه سازی متدهای First  (یافتن اولین عنصر درخواستی در یک لیست) و  FirstOrDefault استفاده کرد:
  equivalentToFirstOrDefault() {
    const vahidOrDefault = this.people.filter(item => item.name === "Vahid")[0] || null; // ECMAScript 5
    this.logResult("vahidOrDefault", vahidOrDefault);

    const user1OrDefault = this.people.find(item => item.name === "User 1") || null; // ECMAScript 2015 = ECMAScript 6
    this.logResult("user1OrDefault", user1OrDefault);
  }
متد filter، در صورت برآورده نشدن شرط آن، یک آرایه‌ی خالی را بازگشت می‌دهد که مقدار [0] آن، undefined است. بنابراین ترکیب آن با null ||، سبب بازگشت نال، در صورت خالی بودن آرایه می‌شود؛ یا همان حالت OrDefault (یا بازگشت مقدار پیش فرض، یا نال در اینجا). متد find نیز در صورت نیافتن عنصر درخواستی، مقدار undefined را بازگشت می‌دهد.


معادل متد FindIndex در TypeScript

متد findIndex که جزو متدهای توکار ES6 است، می‌تواند معادلی برای متد FindIndex در جهت یافتن ایندکس عنصری در یک لیست، بر اساس شرط درخواستی، باشد.
  equivalentToFindIndex() {
    const index = this.people.findIndex(person => person.age === 8); // ECMAScript 2015 = ECMAScript 6
    this.logResult("index of the user with age 8", index)
  }
با این خروجی:
 index of the user with age 8:2


معادل متد Select در TypeScript

متد map که جزو متدهای توکار ES5 است، می‌تواند معادلی برای متد Select، برای تغییر شکل نهایی خروجی یک لیست باشد:
  equivalentToSelect() {
    const names = this.people.map(person => person.name); // ECMAScript 5
    this.logResult("Selected the names of people", names);
  }
برای مثال در اینجا در لیست اشخاص، تنها خاصیت name آن‌ها، انتخاب و بازگشت داده شده‌است:
 Selected the names of people:["User 4","User 5","User 6","User 1","User 2","User 3"]


معادل متد Aggregate در TypeScript

متد reduce که جزو متدهای توکار ES5 است، می‌تواند شبیه به متد Aggregate عمل کند و لیستی از عناصر را به یک مقدار کاهش دهد:
  equivalentToAggregate() {
    // ECMAScript 5
    const aggregate = this.people.reduce((person1, person2) => {
      return { name: "", age: person1.age + person2.age };
    });
    this.logResult("Aggregate age", aggregate.age); // { age: 210 }
  }
با این خروجی:
 Aggregate age:210


معادل متد ForEach در TypeScript

متد forEach که جزو متدهای توکار ES5 است، می‌تواند معادلی برای متد ForEach باشد که روشی functional برای پیمودن عناصر یک لیست است:
  equivalentToForEach() {
    // ECMAScript 5
    this.people.forEach(person => {
      this.logResult("person", person);
    });
  }
با این خروجی:
 person:{"name":"User 4","age":27}
 person:{"name":"User 5","age":42}
 person:{"name":"User 6","age":8}
 person:{"name":"User 1","age":20}
 person:{"name":"User 2","age":35}
 person:{"name":"User 3","age":78}


معادل متد OrderBy در TypeScript

متد sort که جزو متدهای توکار ES5 است، می‌تواند معادلی برای متد OrderBy باشد که جهت مرتب سازی عناصر یک لیست از آن استفاده می‌شود:
    // ECMAScript 5
    let orderedByAgeAscending = this.people.sort((person1, person2) => {
      const a = person1.age;
      const b = person2.age;
      return a > b ? 1 : -1;
    });
    this.logResult("Ordered by age ascending", orderedByAgeAscending);
متد sort یک callback را می‌پذیرد و هر بار دو آیتم در حال مرتب سازی را به آن ارسال می‌کند. در این حالت اگر خروجی این callback:
 - مساوی صفر باشد، تغییری را به وجود نمی‌آورد.
 - کمتر از صفر باشد، اولین عنصر را قبل از دومین عنصر قرار می‌دهد.
 - بیشتر از صفر باشد، دومین عنصر را پس از اولین عنصر قرار می‌دهد.

منطق مقایسه‌ی فوق را به صورت ذیل نیز می‌توان خلاصه کرد:
    orderedByAgeAscending = this.people.sort((person1, person2) => person1.age - person2.age);
    this.logResult("Ordered by age ascending", orderedByAgeAscending);
با این خروجی:
Ordered by age ascending:[
{"name":"User 6","age":8},
{"name":"User 1","age":20},
{"name":"User 4","age":27},
{"name":"User 2","age":35},
{"name":"User 5","age":42},
{"name":"User 3","age":78}
]
و یا اگر بخواهیم این لیست را بر اساس نام اشخاص مرتب سازی کنیم، به منطق ذیل خواهیم رسید:
    const orderedByName = this.people.sort((person1, person2) => {
      // name1.localeCompare(name2) // is case insensitive
      // or use toUpperCase() to ignore character casing
      const name1 = person1.name.toUpperCase();
      const name2 = person2.name.toUpperCase();
      return name1 > name2 ? 1 : -1;
    })
    this.logResult("Ordered by name", orderedByName);
    this.logOriginalArray();
با این خروجی:
Ordered by name:[
{"name":"User 1","age":20},
{"name":"User 2","age":35},
{"name":"User 3","age":78},
{"name":"User 4","age":27},
{"name":"User 5","age":42},
{"name":"User 6","age":8}
]

original this.people:[
{"name":"User 1","age":20},
{"name":"User 2","age":35},
{"name":"User 3","age":78},
{"name":"User 4","age":27},
{"name":"User 5","age":42},
{"name":"User 6","age":8}
]
نکته‌ی مهم: همانطور که ملاحظه می‌کنید، متد sort نه فقط یک خروجی مرتب شده را بازگشت داده‌است، بلکه اصل آرایه را نیز درجا مرتب سازی کرده‌است و ترتیب عناصر این آرایه، دیگر با آرایه‌ی قبلی و اصلی یکی نیست.


امکان ترکیب زنجیروار متدهای کار بر روی لیست‌ها در TypeScript

همانند LINQ، در اینجا نیز می‌توان متدهای فوق را به صورت زنجیروار بر روی یک لیست فراخوانی و اجرا کرد:
  chainFunctionCalls() {
    const namesOfPeopleOver30OrderedDesc =
      this.people
        .filter(person => person.age > 30)
        .map(person => person.name)
        .sort((name1, name2) => {
          // name1.localeCompare(name2) // is case insensitive
          // or use toUpperCase() to ignore character casing
          name1 = name1.toUpperCase();
          name2 = name2.toUpperCase();
          return name2 > name1 ? 1 : -1;
        });
    this.logResult("the names of all people over 30 ordered by name descending", namesOfPeopleOver30OrderedDesc);
  }
با این خروجی:
 the names of all people over 30 ordered by name descending:["User 5","User 3","User 2"]
در اینجا ابتدا اشخاص بالای 30 سال فیلتر شده‌اند. سپس فقط خاصیت رشته‌ای نام آن‌ها انتخاب شده‌است و در آخر این نام‌ها به صورت نزولی مرتب شده‌اند.


معادل متد Skip در TypeScript

متد splice که جزو متدهای توکار ES5 است، می‌تواند شبیه به متد Skip عمل کند. این متد آرایه‌ای را بازگشت می‌دهد که حاوی عناصری است که پس از تعداد ذکر شده، در آرایه‌ی اصلی وجود دارند:
  equivalentToSkip() {
    const skip2 = this.people.splice(2); // ECMAScript 5
    this.logResult("skip2 -> the deleted elements", skip2);
    this.logOriginalArray();
  }
با این خروجی:
skip2 -> the deleted elements:[
{"name":"User 3","age":78},
{"name":"User 4","age":27},
{"name":"User 5","age":42},
{"name":"User 6","age":8}
]

original this.people:[
{"name":"User 1","age":20},
{"name":"User 2","age":35}
]
کار واقعی متد splice، حذف عناصر باقیمانده‌ی در آرایه‌است و خروجی آن دقیقا لیست موارد حذف شده‌است. به همین جهت است که در نتیجه‌ی فوق، اکنون آرایه‌ی اصلی تنها دارای دو عضو باقیمانده است (و دیگر با آرایه‌ی اصلی و ابتدایی یکی نیست).
اشتراک‌ها
کنفرانس مجازی blazor day

The blazor day is the online event around Blazor technologies. Originally, this event was organized by three MVP friends, Adrien, Christophe, and Denis. Their objective of this event is to share their passion for .NET and more particularly Blazor. Joined by the famous Charline to upgrade the event to the next level. Blazor is part of the ASP.NET Core product which is offered in Open Source by Microsoft.... 

کنفرانس مجازی blazor day
اشتراک‌ها
بررسی وضعیت اکوسیستم توسعه دهنده‌ها در سال 2023

The State of Developer Ecosystem 2023
This report is the culmination of insights gathered from 26,348 developers from all around the globe. The world of developers is vast and diverse, making it an endlessly fascinating realm for exploration and learning. Through yearly research initiatives like this one, our goal is to explore this captivating world, uncover valuable insights about developers and their craft, and then share these facts with the community. 

بررسی وضعیت اکوسیستم توسعه دهنده‌ها در سال 2023
اشتراک‌ها
پیشنهاد اضافه شدن type به JavaScript

Today we’re excited to announce our support and collaboration on a new Stage 0 proposal to bring optional and erasable type syntax to JavaScript. Because this new syntax wouldn’t change how surrounding code runs, it would effectively act as comments. We think this has the potential to make TypeScript easier and faster to use for development at every scale. We’d like to talk about why we’re pursuing this, and how this proposal works at a high level.

 

پیشنهاد اضافه شدن type به JavaScript
اشتراک‌ها
سری آموزش مقدماتی Git و GitHub

Git and GitHub Tutorials for Beginners!

#1 - What is Git and GitHub?
#2 - Creating Repositories and Commits
#3 - What are Git Branches?
#4 - Creating Forks and Pull Requests
#5 - Understanding GitHub Issues
#6 - Installing Git and Cloning our Repository
#7 - Staging Changes and Making you First Commit!
 

سری آموزش مقدماتی Git و GitHub
اشتراک‌ها
کتابخانه emergence.js

Emergence.js is a lightweight, high-performance JS plugin for detecting and manipulating elements in the browser. Demo

  • Dependancy-free
  • IE8+ and all modern browsers
  • 1KB minified and gzipped
npm install emergence.js
bower install emergence.js
کتابخانه emergence.js