نظرات مطالب
Blazor 5x - قسمت چهارم - مبانی Blazor - بخش 1 - Data Binding
ساده شدن «روش تعریف data binding دو طرفه در کامپوننت‌ها» در دات نت 7

در نکته‌ی فوق، روش تعریف data binding دو طرفه را در کامپوننت‌ها بررسی کردیم. برای مثال اگر بخواهیم کامپوننت سفارشی ما دارای ویژگی bind-Value@ باشد، باید حداقل توسط دو پارامتر زیر پشتیبانی شود:
[Parameter] public string Value { set; get; }
[Parameter] public EventCallback<string> ValueChanged { get; set; }
که در اینجا باید قسمت setter مربوط به خاصیت Value را هم بازنویسی کنیم و توسط آن، با فراخوانی ValueChanged.InvokeAsync، به استفاده کننده اطلاع دهیم که مقدار Value تغییر کرده‌است. این قسمت بازنویسی در دات نت 7 به صورت زیر قابل حذف شدن است (نام فرضی کامپوننت زیر، MyInput است):
<input @bind:get="Value" @bind:set="ValueChanged" />

@code {
    [Parameter]
    public TValue Value { get; set; }
    
    [Parameter]
    public EventCallback<TValue> ValueChanged { get; set; }
}
در دات نت 7، دو binding modifier جدید bind:get و bind:set جهت تعریف ساده‌تر two-way data-binding اضافه شده‌اند که باید به همراه هم اضافه شوند. توسط bind:get، مقدار در حال تغییر و توسط bind:set، یک callback را که پس از تغییر این مقدار فراخوانی می‌شود، مشخص می‌کنیم و ... همین! پس از این تعاریف دیگر نیازی به بازنویسی قسمت set پارامتر Value مانند قبل نیست و اکنون می‌توان به bind-Value@ دو طرفه به صورت زیر دسترسی یافت:
<MyInput @bind-Value="text" />

@code {
    string text = "Type something great!";
}
مطالب
Performance در AngularJS 1.x قدم ششم
موضوع این مقاله استفاده مستقیم از توابع و عملیات محاسباتی برای Binding در View می‌باشد که در پروژه‌های بزرگ که حجم المنت‌ها در صفحه زیاد است عملکردی منفی در Performance دارد که قابل چشم پوشی نیست. برای اینکه این مورد ملموس باشد بنده مثالی را آماده کرده‌ام که هدف آن بیشتر درک درست شما از این موضوع است.
کد زیر را مشاهده کنید:
<input type="text" ng-model="newItemTitle">
<button type="button" ng-click="add()">افزودن</button>
<ul>
     <li ng-repeat="item in items">{{item.title}}</li>
</ul>
<div ng-show="showMSG()">شما بیش از ۱۰ استان ثبت کرده اید</div>
و قسمت Controller:
$scope.newItemTitle='';           

$scope.add=function(){
     $scope.items.push({
          title:$scope.newItemTitle
     });
}

$scope.items=[
     {title:'اردبیل'},
     {title:'تهران'},
     {title:'اصفهان'},
     {title:'شیراز'},
     {title:'مشهد'},
];  

$scope.showMSG=function(){
     return $scope.items.length>10;
}
 همانطور که مشاهده میکنید این کد یک مثال ساده است که شامل لیست استانها و قسمتی برای افزودن استان جدید و در قسمت پایینتر در صورتی که بیش از ۱۰ استان ثبت شده باشد به کاربر پیغامی نمایش می‌دهیم.

سوال اول، مشکل کجاست؟
این کد کاملا صحیح است، اما بهینه نیست. مشکل اصلی در View است که مستقیما تابع showMSG را صدا میزند. صدا زدن مستقیم توابع از View در قسمت‌هایی که Bind شده‌اند، در Performance نتیجه منفی دارند. منظور از صدا زدن مستقیم توابع در قسمت‌های Bind روش‌های زیر است:
ng-show="showMSG()"
ng-if="showMSG()"
ng-hide="showMSG()==false"
ng-class="{'red',getResult()}"
ng-style="{'width':getWidth()}"
سوال دوم، چرا Performance را کاهش میدهد؟
AngularJS برای اینکه بتواند تکلیف ng-show هایی را که در div نوشته شده‌است، مشخص کند، مجبور است تابع showMSG را صدا بزند. تا این قسمت هیچ مشکلی نیست. اما وقتی که AngularJS می‌خواهد در زمان‌های بعدی هم متوجه تغییرات بشود مجبود هست دوباره تابع showMSG را صدا بزند و این کار تکرار می‌شود و در مواقعی که تغییرات انجام شده هیچ ارتباطی با این Binding ندارند باز هم اجرا می‌شود و این تداوم در اجرا که اکثرا لازم نیستند باعث کاهش Performance می‌شود. حالا فرض کنید در پروژه‌ای بزرگ در بیشتر قسمت‌های صفحه از این روش استفاده کرده اید و پروژه با داده‌های حجیم کار میکند.

سوال سوم، راهکار چیست؟ 
راهکار این موضوع خیلی ساده است؛ اما در بهبود کارآیی پروژه، خیلی تاثیر مثبتی دارد. به طور کلی سعی کنید در ‌Binding‌های View از متغییر استفاده کنید و هیچ تابعی و یا عملیات محاسباتی را در Binding قرار ندهید . منظور از عملیات محاسباتی در Binding روش زیر است:
<div ng-show="items.length>10">شما بیش از ۱۰ استان ثبت کرده اید</div>
حتی این روش هم مناسب نیست. روشی که می‌توان برای حل این مشکل در نظر گرفت به شرح زیر است:
$scope.maxItem=false';           

$scope.add=function(){
     $scope.items.push({
          title:$scope.newItemTitle
     });
     $scope.maxItem=$scope.items.length>10;
}

<div ng-show="maxItem">شما بیش از ۱۰ استان ثبت کرده اید</div>
این مشکل با متفیر maxItem و انتقال منطق به تابع add حل می‌شود.
نتیجه گیری کلی، در پروژه‌هایی که با داده‌های حجیم کار میکنند، هیچ وقت از توابع و عملیات محاسباتی در View استفاده نکنید. 
مطالب
آموزش QUnit #1
مقدمه:
تست و آزمایش کد برنامه‌ها و وب سایت‌هایمان، بهترین راه کاهش خطا و مشکلات آنها بعد از انتشار است. از جمله روش‌های موجود، تست واحد است که ویژوال استادیو نیز از آن برای پروژه‌های دات نت پشتیبانی می‌کند. با افزایش روز افزون کتابخانه‌های جاوا اسکریپتی و جی کوئری، نیاز به تست کد‌های جاواسکریپتی نیز بیشتر به نظر می‌رسد و بهتر است تست واحد و آزمایش شوند. اما برخلاف کدهای #C و ASP.NET تست کد‌های جاوا اسکریپت، مخصوصا زمانی که به دستکاری عناصر DOM می‌پردازیم و یا رویداد‌های درون صفحه وب را با استفاده از جی کوئری می‌نویسیم، حتی اگر در فایل جداگانه‌ای نوشته شود، این بدان معنی نیست که آماده تست واحد است و ممکن است امکان نوشتن تست وجود نداشته باشد.
بنابراین چه چیزی یک تست واحد است؟ در بهترین حالت توابعی که مقداری را برمی گردادنند، بهترین حالت برای تست واحد است. اما در بیشتر موارد شما نیاز دارید تا تاثیر کد را بر روی عناصر صفحه نیز مشاهد نمایید.
ساخت تست واحد
برای تست پذیری بهتر، توابع جاوا اسکریپت و هر کد دیگری، آن را می‌بایست طوری بنویسید که مقادیر تاثیر گذار در اجرای تابع به عنوان ورودی تابع در نظر گرفته شده باشند و همیشه نتیجه به عنوان خروجی تابع برگردانده شود؛ قطعه کد زیر را در نظر بگیرید:
 function prettyDate(time){
    var date = new Date(time || ""),
      diff = (((new Date()).getTime() - date.getTime()) / 1000),
      day_diff = Math.floor(diff / 86400);
 
    if ( isNaN(day_diff) || day_diff < 0 || day_diff >= 31 )
      return;
 
    return day_diff == 0 && (
        diff < 60 && "just now" ||
        diff < 120 && "1 minute ago" ||
        diff < 3600 && Math.floor( diff / 60 ) +
          " minutes ago" ||
        diff < 7200 && "1 hour ago" ||
        diff < 86400 && Math.floor( diff / 3600 ) +
          " hours ago") ||
      day_diff == 1 && "Yesterday" ||
      day_diff < 7 && day_diff + " days ago" ||
      day_diff < 31 && Math.ceil( day_diff / 7 ) +
        " weeks ago";
  }
تابع pertyDate اختلاف زمان حال را نسبت به زمان ورودی، بصورت یک رشته برمی گرداند. اما در اینجا مقدار زمان حال، در خط سوم، در خود تابع ایجاد شده است و در صورتی که بخواهیم برای چندین مقدار آن را تست کنیم زمان حال متفاوتی در نظر گرفته می‌شود و حداکثر، زمان 31 روز قبل را نمایش داده و در بقیه تاریخ ها undefined را بر می‌گرداند. برای تست واحد، چند تغییر می‌دهیم.
بهینه سازی، مرحله اول:
پارامتری به عنوان مقدار زمان جاری برای تابع در نظر می‌گیریم و تابع را جدا کرده و در یک فایل جداگانه قرار می‌دهیم. فایل prettydate.js بصورت زیر خواهد شد.
function prettyDate(now, time){
  var date = new Date(time || ""),
    diff = (((new Date(now)).getTime() - date.getTime()) / 1000),
    day_diff = Math.floor(diff / 86400);
 
  if ( isNaN(day_diff) || day_diff < 0 || day_diff >= 31 )
    return;
 
  return day_diff == 0 && (
      diff < 60 && "just now" ||
      diff < 120 && "1 minute ago" ||
      diff < 3600 && Math.floor( diff / 60 ) +
        " minutes ago" ||
      diff < 7200 && "1 hour ago" ||
      diff < 86400 && Math.floor( diff / 3600 ) +
        " hours ago") ||
    day_diff == 1 && "Yesterday" ||
    day_diff < 7 && day_diff + " days ago" ||
    day_diff < 31 && Math.ceil( day_diff / 7 ) +
      " weeks ago";
}
حال یک تابع برای تست داریم، چند تست واحد واقعی می‌نویسیم
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Refactored date examples</title>
  <script src="prettydate.js"></script>
  <script>
  function test(then, expected) {
    results.total++;
    var result = prettyDate("2013/01/28 22:25:00", then);
    if (result !== expected) {
      results.bad++;
      console.log("Expected " + expected +
        ", but was " + result);
    }
  }
  var results = {
    total: 0,
    bad: 0
  };
  test("2013/01/28 22:24:30", "just now");
  test("2013/01/28 22:23:30", "1 minute ago");
  test("2013/01/28 21:23:30", "1 hour ago");
  test("2013/01/27 22:23:30", "Yesterday");
  test("2013/01/26 22:23:30", "2 days ago");
  test("2012/01/26 22:23:30", undefined);
  console.log("Of " + results.total + " tests, " +
    results.bad + " failed, " +
    (results.total - results.bad) + " passed.");
  </script>
</head>
<body>
 
</body>
</html>
در کد بالا یک تابع بدون استفاده از Qunit برای تست واحد نوشته ایم که با آن تابع prettyDate را تست می‌کند. تابع test مقدار زمان حال و رشته خروجی را گرفته  و آن را با تابع اصلی تست می‌کند در آخر تعداد تست ها، تست‌های شکست خورده و تست  های پاس شده گزارش داده می‌شود.
خروجی می‌تواند مانند زیر باشد:

Of 6 tests, 0 failed, 6 passed.
Expected 2 day ago, but was 2 days ago. 
f 6 tests, 1 failed, 5 passed.

مطالب
راه اندازی وب سایت سریع و سبک با Nancy
Nancy یک فریم ورک سبک برای ساخت سرویس‌های مبتنی بر HTTP بر روی .Net و Mono و در واقع پیاده سازی Sinatra در   Ruby برای .net است. با استفاده از این کتابخانه شما می‌توانید به سادگی درخواست‌های مختلف کاربران را از طریق وب پاسخ دهید. از ویژگی‌های این کتابخانه امکان اجرای آن بدون نیاز به وجود وب سرور و به صورت Standalone می‌باشد.
بهتر است وارد عمل شویم و ببینیم این سیستم چگونه عمل می‌کند. برای شروع ما خود Asp .net را به عنوان میزبان در نظر می‌گیریم.
- یک پروژه خالی Asp .net ایجاد کنید. (Asp .net Empty Web Application)
- وارد خط فرمان Package Manager شوید و عبارت زیر را وارد کنید:
Install-Package Nancy.Hosting.Aspnet
- پروژه‌های Nancy از تعدای ماژول تشکیل شده اند که می‌توانیم برای هر ماژول آدرس وب جداگانه ای در نظر بگیریم.
یک کلاس به پروژه اضافه کنید و نام آن را TestModule بگذارید.
در متن کلاس عبارات زیر را تایپ کنید:
public class TestModule : NancyModule 
{
    public TestModue() 
    {
        Get["/"] = x => "Welcome to my site!";
        Get["/Hello/"] = x=> "Hello Nancy!";
        Get["/Bye/{name}"] = x=> "Good bye " + x.name;
    }
}

حال کلید F5 را زده و برنامه را اجرا کنید.
حالا در مرورگر خودتان عبارت http://localhost:12345/Hello را تایپ کنید. توجه کنید که به جای 12345 باید شماره پورتی که وب سرور دات نت اجراست تایپ کنید.
همانطور که متوجه شدید ما در خطوط بالا تعیین کرده ایم که برای درخواست‌های از نوع Get که مسیر ریشه سایت را درخواست می‌کنند عبارت Welcome to my site! ارسال شود. همچنین برای درخواست هایی که مسیر /Hello را درخواست می‌کنند عبارت Hello Nancy نمایان می‌شود.
نکته جالب برای درخواست‌های /Bye است. در اینجا ما یک پارامتر به نام name تعریف کرده ایم و گفته ایم که درخواست‌های Bye که در ادامه آنها عبارتی وجود دارد به صورت Good bye به همراه عبارت بازگردانده شوند.
همین عملیات برای درخواست‌های Put و Head و سایر انواع درخواست وجود دارد.
برای اینکه درخواست‌ها از مسیر خاصی فراخوانی شوند کافی است در هنگام اعلان سازنده کلاس ماژول، مسیر را تعیین کنید.
public class TestModule : NancyModule
{
    public TestModule() : base("/test")
    {
        Get["/user"] = x=> "It is test for user";
    }
}

این درخواست‌ها از مسیری شبیه این مسیر فراخوانی خواهند شد:
http://localhost:12345/test/user
همانطور که برای پردازش درخواست از عبارات کوتاه استفاده کردیم می‌توانیم از توابع هم استفاده کنیم.
Get["/hello/{username}"] = x=> {
     // some code
     // ...
     // ...
     return "Hello, " + username;
};

همچنین امکان بازگرداندن کدهای وضعیت به طور مستقیم وجود دارد.
Get["/user"] = x=> return 200;

و یا
Get["/user] = x=> return HttpStatusCode.OK;

همانطور که گفته شد امکان میزبانی پروژه‌های Nancy از داخل برنامه‌های دات نتی هم وجود دارد. در مقاله بعدی به این موضوع خواهیم پرداخت.
 
نظرات مطالب
نحوه‌ی نگاشت فیلدهای فرمول در Fluent NHibernate
از پاسخگویی شما بسیار ممنونم. من هر روز از شما مطلب جدیدی یاد میگیرم.
من قصد کشدار کردن بحث رو ندارم و اینم آخرین ارسال من در مورد این بحث است.فکر می کنم نتونستم منظورم رو واضح برسونم. فرض کنیم کلاس زیر وجود داره:
public class Project
{
public virtual int Id { get; set; }
public virtual long ProjectCode { get; set; }
public virtual string Name { get; set; }
public virtual int CreateDate { get; set; }

public virtual string SepratedDate
{
get { return myFunc(CreateDate); }
private set { ; }
}
}

من میخواهم در متد زیر لیستی از کلاس بالا رو به DataSet تبدیل کنم:

public DataSet dsGetAll(bool includeArchived)
{
using (var repository = new Repository())
{
var projects = repository.Find(x => x.IsArchive == includeArchived
);

var ds = new CollectionToDataSet>(projects.ToList());

return ds.CreateDataSet();
}
}

ولی خطا می ده که SepratedDate در جدول وجود نداره!!!
{"Invalid column name 'SepratedDate'."}
could not execute query
[ select project0_.Id as Id15_, project0_.ProjectCode as ProjectC2_15_, project0_.Name as Name15_, project0_.IsArchive as IsArchive15_, project0_.CreateDate as CreateDate15_, project0_.SepratedDate as Seprated6_15_ from tblProject project0_ where case when project0_.IsArchive=1 then 'true' else 'false' end=case when @p0='true' then 'true' else 'false' end
نظرات مطالب
عبارت using و نحوه استفاده صحیح از آن
با سلام خدمت شما جناب نصیری
با توجه به این مطلب که زیاد هم قدیمی نیست به نظر شما استفاده از using پیشنهاد میشود یا استفاده از try catch ؟
چون بنده در برنامه‌های کاربردی تحت وب بیشتر از using استفاده میکنم و از Elmah  هم برای ثبت خطاهای سیستم استفاده میکنم .
نظرات مطالب
EF Code First #6
در متد Seed، متد زیر را فراخوانی کنید:
private static void createUniqueIndex(MyContext context, string tableName, string fieldName)
{
  try
  {
     context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX [IX_Unique_" + tableName + "_" + fieldName + "] ON [" + tableName + "]([" + fieldName + "] ASC);");
  }
  catch { }
}

نظرات مطالب
EF Code First #12
به این ترتیب طراحی شده. نظر یکی از اعضای تیم EF در این مورد: (^). اگر می‌خواهید رکورد تکراری ثبت نشود از متد AddOrUpdate استفاده کنید.
استفاده‌ای که من از متد seed می‌کنم در عمل، تعریف قیودی مانند unique است با sql نویسی (داخل try/catch البته).
مطالب
بررسی معادل‌های 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، حذف عناصر باقیمانده‌ی در آرایه‌است و خروجی آن دقیقا لیست موارد حذف شده‌است. به همین جهت است که در نتیجه‌ی فوق، اکنون آرایه‌ی اصلی تنها دارای دو عضو باقیمانده است (و دیگر با آرایه‌ی اصلی و ابتدایی یکی نیست).
مطالب
MongoDb در سی شارپ (بخش دوم)
در قسمت پیشین عملیات درج و واکشی را در مونگو مورد بررسی قرار دادیم. در این مقاله به عملیات CRUD، پایان داده و عملیات ایندکس گذاری را نیز مورد بررسی قرار می‌دهیم.
در مقاله قبلی از بیلدر برای فیلترگذاری و مرتب سازی نتایج استفاده کردیم و در این مقاله هم برای به روزرسانی و ایندکس گذاری استفاده میکنیم.

به روزرسانی
کد زیر، تاریخ آخرین ورود کتب به انبار را به روزرسانی میکند؛ بدین صورت که اگر تاریخ آخرین به روزرسانی انبار کمتر یا مساوی ده روز است، آن‌را برابر 5 روز قبل قرار دهد.
var update = Builders<Book>.Update.Set("LastStock", new DateTime().AddDays((-5)));
var filter = Builders<Book>.Filter.Lte("LastStock", new DateTime().AddDays(-10));
collection.UpdateManyAsync(filter, update);
در خط اول ابتدا یک فرمان به روزرسانی را می‌سازیم و مقدار جدید را انتساب می‌دهیم. در خط بعدی، از گزینه فیلتر برای تعیین شرط استفاده میکنیم که در قسمت قبلی به آن پرداخته‌ایم. حال هم شرط را داریم و عمل به روزرسانی و از کالکشن میخواهیم که با استفاده از این دو شیء عملیات به روزرسانی را روی اسناد آغاز کند. توجه داشته باشید که دستور UpdateOne که به دو حالت همزمان و غیرهمزمان وجود دارد، وقتی به اولین مورد که با شرط همخوانی داشته باشد برسد و عملیات به روزرسانی را روی آن سند انجام دهد، دیگر باقی عملیات را ادامه نمی‌دهد. پس اگر میخواهید که کلیه اسناد در این فرمان شرکت کنند حتما از متد UpdateMany که به دو شکل همزمان و غیرهمزمان وجود دارد، استفاده کنید.

در بسیاری از موارد نیاز است که به روزرسانی یک فیلد، بسته به مقدار فعلی‌اش صورت بگیرد. برای مثال در کد زیر ما قصد داریم کتبی را که قیمت آن‌ها بالای دویست هزار ریال است، سی هزار ریال از قیمت آن‌ها کم کنیم:
var update = Builders<Book>.Update.Inc("Price", -30000);
var filter = Builders<Book>.Filter.Gt("Price", 200000);
collection.UpdateMany(filter, update);
اگر به متدهای خصوصیت Update نگاهی بیندازید، میتوانید خصوصیات مختلف و ترکیبات مختلفی از آن را نیز ببینید.

حذف
جهت حذف یک سند از سیستم با توجه به مواردی که تا به الان داشتیم باید این حدس را زده باشید که برای حذف، باید از متد Delete استفاده کنیم و یک فیلتر را باید به عنوان شرط گذاری به عنوان پارامتر آن، در نظر بگیریم. کد زیر کتاب‌هایی را که به زبان انگلیسی هستند، از کالکشن حذف میکند:
var filter = Builders<Book>.Filter.Eq("Language.Name", "English");
collection.DeleteMany(filter);
دستور Delete هم همانند آپدیت میتواند اولین سند برابر با شرط و همچنین تمامی اسناد برابر شرط را حذف کند. نکته‌ای که در این کد به نسبت کدهای دیگر وجود دارد این است که اگر سند توکار (Embed) در سند شما وجود دارد، باید ابتدا نام خصوصیت اول و بعد از علامت «.» نام خصوصیت درونی نوشته شود و اگر باز سند توکار دیگری نیز بود، این رویه ادامه پیدا کند. در اینجا برای دسترسی به خاصیت نام زبان ابتدا نام خصوصیت Language آورده شده است و سپس نام خصوصیت Name آن ذکر شده است:
Language.Name
در صورتی که قصد شما حذف یک کالکشن به طور کلی باشد، از خصوصیت DropCollection در شیء دیتابیس استفاده میکنیم:
var client = new MongoClient();
var db = client.GetDatabase("publisher");
db.DropCollection("books");
برای حذف دیتابیس هم از دستور DropDatebase در شیء کلاینت استفاده میکنیم:
var client = new MongoClient();
client.DropDatabase("publisher");

ایندکس گذاری Indexing
ایندکسها باعث میشوند که دسترسی سریعتری را به داده‌ها داشته باشیم و فیلدهایی را که در پرس و جوها مورد استفاده قرار میگیرند، سریعتر پیدا کنیم. به عنوان مثال فیلد Year فیلدی است که به احتمال زیاد در پرس و جوهایمان مرتبا استفاده خواهیم کرد تا به لیست کتب آن سال دسترسی سریعتری داشته باشیم. برای ایندکس گذاری هم از کلاس Builders استفاده میکنیم:
var index = Builders<Book>.IndexKeys.Ascending("Year");
collection.Indexes.CreateOneAsync(index);

ایندکس‌ها ترکیبی Compound Index

حتی میتوانید این ایندکس گذاری را بر روی دو فیلد، یعنی به حالت ترکیبی هم انجام دهید:
var index = Builders<Book>.IndexKeys.Ascending("Year").Descending("LastStock");
collection.Indexes.CreateOneAsync(index);
در اولین نگاه ممکن از است بپرسید که چرا این مرتب سازی بر روی اندیس‌ها مهم است؟ منظور از این صعودی و نزولی چیست؟ در اینجا باید ساختاری را که مونگو از آن برای یافتن آیتم‌ها استفاده میکند، بررسی کنیم.

مونگو برای جست و جو اسناد در سیستم خود، از ساختار Btree استفاده میکند و گره‌ها یا همان Node‌ها در واقع به ترتیب خاصی در این درخت باینری قرار میگیرند.
اگر شما تنها به دنبال یک آیتم باشید، مرتب سازی تاثیری بر روی این جست و جو نخواهد داشت؛ ولی اگر چندین آیتم یا گره را بخواهید برگردانید، مرتب سازی باعث میشود که این آیتم‌ها نزدیک یکدیگر باشند و سریعتر واکشی شوند. ولی اینکه این مرتب سازی صعودی یا نزولی باشد تاثیری بر روند آن ندارد. چرا که در هر دو حالت آیتم‌ها نزدیک به یکدیگر هستند.
ولی اگر ایندکس شما ترکیبی باشد، این مرتب سازی تاثیر خودش را نشان خواهد داد. ابتدا درخت را بر اساس سال به صورت صعودی مرتب میکند و بعدا زیرمجموعه هر سال را بر اساس تاریخ آخرین به روزرسانی انبار  به صورت نزولی مرتب میکند.

ایندکس یا کلیدهای چندگانه Multiple Index
این نوع ایندکس‌ها بر روی آرایه‌های یک فیلد به صورت خودکار ایجاد میگردند و لازم نیست تا خودتان دستی آن‌ها را ایجاد کنید. موقعیکه فیلدی در کلاستان از نوع یک آرایه دارید، مقادیر این آرایه در درخت نیز لیست میشوند و به طور خودکار توسط این نوع اندیکس، ایندکس گذاری می‌شوند.

ایندکس جغرافیایی GeoSpatial Index
برای داده‌های جرافیای می‌توان این نوع ایندکس را پیاده سازی کرد. موقعیکه شما با مختصات جغرافیایی کار میکنید، این ایندکس‌ها میتوانند به شما در بازیابی سریعتر مکان‌ها و مختصات کمک کنند. از آنجا که مطالب این نوع ایندکس‌ها تخصصی‌تر است، آن را به زمان دیگری موکول می‌کنیم.
var index2 = Builders<Book>.IndexKeys.Geo2D("Location");
var index3 = Builders<Book>.IndexKeys.Geo2DSphere("Location");
var index4 = Builders<Book>.IndexKeys.GeoHaystack("Location");

ایندکس متنی Text Index

این نوع ایندکس‌ها بر روی فیلدهای نوع رشته String قرار داده میشوند تا جست و جوی عبارت‌ها بین آن‌ها ساده‌تر گردد. زمانیکه ما در مورد جست و جو در بین مطالب یک سایت صحبت میکنیم، سریعا عبارات Full Text Search یا Elastic و لوسین را به یاد می‌آوریم. در مونگو هم برای این نوع جست و جو‌ها ایندکس Text را داریم که از این پس بتوانیم جست و جوی سریعتری بر روی آن داشته باشیم.
var index5 = Builders<Book>.IndexKeys.Text("Content");


قابلیت‌های ایندکس گذاری این نوع دیتابیس حتی به مواردی چون Sharding ، SparseIndex ,TTL Indexes و ... هم میرسد که از حوصله مقاله فعلی خارج است و باید به زمان دیگری و در یک مقاله تخصصی‌تر آنها را بررسی کرد.
 
در قسمت بعدی، ذخیره و بازیابی فایل‌ها در سطح دیتابیس را مورد بررسی قرار می‌دهیم.