مطالب
پیاده سازی ServiceLocator با استفاده از Microsoft Unity
در این پست قصد دارم روش استفاه از ServiceLoctor رو به وسیله یک مثال ساده بهتون نمایش بدم. Microsoft Unity روش توصیه شده Microsoft برای پیاده سازی Dependecy Injecttion و ServiceLocator Pattern است. یک ServiceLocator در واقع وظیفه تهیه Instance‌های مختلف از کلاس‌ها رو برای پیاده سازی Dependency Injection بر عهده داره.
برای شروع یک پروژه از نوع Console Application ایجاد کنید و یک ارجاع به Assembly‌های زیر رو در برنامه قرار بدید.
  • Microsoft.Practices.ServiceLocation 
  • Microsoft.Practices.Unity 
  • Microsoft.Practices.EnterpriseLibrary.Common 

اگر Assembly‌های بالا رو در اختیار ندارید می‌تونید اون‌ها رو از اینجا دانلود کنید. Microsoft Enterprise Library   یک کتابخانه تهیه شده توسط شرکت Microsoft است که شامل موارد زیر است و بعد از نصب می‌تونید در قسمت‌های مختلف برنامه از اون‌ها استفاده کنید.

  • Enterprise Library Caching Application Block : یک CacheManager قدرتمند در اختیار ما قرار می‌ده که می‌تونید از اون برای کش کردن داده‌ها استفاده کنید.

  • Enterprise Library Exception Handling Application Block : یک کتابخانه مناسب  و راحت برای پیاده سازی یک Exception Handler در برنامه‌ها است.

  • Enterprise Library Loggin Application Block  : برای تهیه یک Log Manager در برنامه استفاده می‌شود.

  • Enterprise Library Validation Application Block  : برای اجرای Validation برای Entity‌ها با استفاده از Attribute می‌تونید از این قسمت استفاده کنید.

  • Enterprise Library  DataAccess Application Block :  یک کتابخانه قدرتمند برای ایجاد یک DataAccess Layer است با Performance بسیار بالا.
  • Enterprise Library Shared Library: برای استفاده از تمام موارد بالا در پروژه باید این Dll رو هم به پروژه Reference بدید. چون برای همشون مشترک است.

برای اجرای مثال ابتدا کلاس زیر رو به عنوان مدل وارد کنید.

public class Book
    {
        public string Title { get; set; }

        public string ISBN { get; set; }
    }

حالا باید Repository مربوطه رو تهیه کنید. ایتدا یک Interface به صورت زیر ایجاد کنید.
 public interface IBookRepository
    {
        List<Book> GetBooks();
    }
سپس کلاسی ایجاد کنید که این Interface رو پیاده سازی کنه.
public class BookRepository : IBookRepository
    {
        public List<Book> GetBooks()
        {
            List<Book> listOfBooks = new List<Book>();

            listOfBooks.AddRange( new Book[] 
            {
                new Book(){Title="Book1" , ISBN="123"},
                new Book(){Title="Book2" , ISBN="456"},
                new Book(){Title="Book3" , ISBN="789"},
                new Book(){Title="Book4" , ISBN="321"},
                new Book(){Title="Book5" , ISBN="654"},
            } );

            return listOfBooks;
        }
    }
کلاس BookRepository یک لیست از Book رو ایجاد میکنه و اونو برگشت می‌ده.
در مرحله بعد باید Service مربوطه برای استفاده از این Repository ایجاد کنید. ولی باید Repository رو به Constructor این کلاس Service پاس بدید. اما برای انجام این کار باید از ServiceLocator استفاده کنیم.
public class BookService
    {
        public BookService()
            : this( ServiceLocator.Current.GetInstance<IBookRepository>() )
        {
        }

        public BookService( IBookRepository bookRepository )
        {
            this.BookRepository = bookRepository;
        }

        public IBookRepository BookRepository
        {
            get;
            private set;
        }

        public void PrintAllBooks()
        {
            Console.WriteLine( "List Of All Books" );

            BookRepository.GetBooks().ForEach( ( Book item ) =>
            {
                Console.WriteLine( item.Title );
            } );
        }
    }
همان طور که می‌بینید این کلاس دو تا Constructor داره که در حالت اول باید یک IBookRepository رو به کلاس پاس داد و در حالت دوم ServiceLocator این کلاس رو برای استفاده دز اختیار سرویس قرار میده.
متد Print هم تمام کتاب‌های مربوطه رو برامون چاپ می‌کنه.
در مرحله آخر باید ServiceLocator رو تنظیم کنید. برای این کار کد‌های زیر رو در کلاس Program قرار بدید.
 class Program
    {
        static void Main( string[] args )
        {
            IUnityContainer unityContainer = new UnityContainer();

            unityContainer.RegisterType<IBookRepository, BookRepository>();

            ServiceLocator.SetLocatorProvider( () => new UnityServiceLocator( unityContainer ) );

            BookService service = new BookService();

            service.PrintAllBooks();

            Console.ReadLine();
        }
    }
در این کلاس ابتدا یک UnityContainer ایجاد کردم و اینترفیس IBookRepository رو به کلاس BookRepository؛ Register کردم تا هر جا که به IBookRepository نیاز داشتم یک Instance از کلاس BookRepository ایجاد بشه. در خط بعدی ServiceLocator برنامه رو ست کردم و برای این کار از کلاس UnityServiceLocator استفاده کردم .
بعد از اجرای برنامه خروجی زیر قابل مشاهده است.



مسیرراه‌ها
ASP.NET MVC
              نظرات اشتراک‌ها
              نگارش بعدی ASP.NET Core از Full .NET Framework پشتیبانی نمی‌کند
              منظور من اجرای ASP.NET Core بر روی Full Dot Net Framework نیست. با توجه به پشتیبانی اضافه کردن Assembly‌های کامپایل شده برای Full Dot Net Framework بدون کامپایل مجدد در Dot Net Core 2 که شامل نزدیک به 70% از Nuget Package‌های موجود می‌شود ( حتی مواردی پیچیده چون Web API OData و NQuery )، می‌توان پروژه را روی Dot Net Core 2 پیش برد و در صورت لزوم Dll‌های Dot Net Full را ارجاع زد. نتیجه حتی از اجرای ASP.NET Core بر روی Full Dot Net Framework نیز بهتر است، بابت مزیت‌های ذاتی طراحی Dot Net Core
              فقط کسانی که نیاز به استفاده از WCF یا .NET Remoting یا Com Interop دارند، تحت تاثیر این تصمیم قرار می‌گیرند.
              Can reference existing .NET Framework libraries. The best thing is no recompile required, so this includes existing NuGet packages. Of course, this will only work if the consumed libraries use APIs that exist in .NET Standard. However, our extensive API surface results in 70% of all NuGet packages to be API compatible with .NET Standard 2.0. 
              مطالب
              کامپوننت‌های راهبری سایت در بوت استرپ 4
              پس از سیستم طرحبندی بوت استرپ، مهم‌ترین کامپوننت‌های آن، کامپوننت‌های راهبری سایت مانند Navs ،Tabs ،Pills و Navbars هستند. Navbars در بوت استرپ 4 نسبت به نگارش سوم آن بازنویسی کامل شده‌اند و شامل بهبودهای قابل ملاحظه‌ای هستند.


              کامپوننت‌های Nav در بوت استرپ 4

              کامپوننت‌های گروه Nav‌، در نگارش 4 آن به علت استفاده‌ی از Flexbox، تغییرات بسیاری داشته‌اند و در نتیجه‌ی آن، انعطاف پذیرتر و ساده‌تر شده‌اند.
              در ابتدا لیست ساده‌ی زیر را در نظر بگیرید. تنظیمات ابتدایی آن برای تبدیل به منوی راهبری بالای سایت به صورت زیر است:
              <body>
                  <div class="container">
                      <div class="row">
                          <section class="col-12">
                              <ul class="nav">
                                  <li class="nav-item"><a class="nav-link active" href="#">Home</a></li>
                                  <li class="nav-item"><a class="nav-link" href="#">Mission</a></li>
                                  <li class="nav-item"><a class="nav-link" href="#">Services</a></li>
                                  <li class="nav-item"><a class="nav-link" href="#">Staff</a></li>
                                  <li class="nav-item"><a class="nav-link disabled" href="#">Testimonials</a></li>
                              </ul>
              </div>
                  </div>
              </body>
              ابتدا کلاس nav به یک ul اضافه می‌شود. سپس به هر آیتم آن، کلاس nav-item را اضافه می‌کنیم. در آخر به هر لینک آن نیز کلاس nav-link نسبت داده می‌شود:


              در اینجا دو کلاس active و disabled نیز به لینک‌های منوی راهبری اضافه شده‌اند. البته این کلاس‌ها تا تکمیل نهایی nav‌، ظاهر آنچنان متفاوتی را ارائه نمی‌دهند.
              اولین شیوه‌نامه‌ای را که می‌توان به nav اضافه کرد، nav-pills است:
              <ul class="nav nav-pills">


              Pills شبیه به دکمه‌ها هستند و در این حالت لینک active، واضح‌تر به نظر می‌رسد.
              و یا می‌توان nav-tabs را به nav افزود:
              <ul class="nav nav-tabs">


              روش دیگر تعریف nav، استفاده از المان nav و سپس حذف ul و li و همچنین nav-item است:
              <nav class="nav nav-pills justify-content-center">
                  <a class="nav-link active" href="#">Home</a>
                  <a class="nav-link" href="#">Mission</a>
                  <a class="nav-link" href="#">Services</a>
                  <a class="nav-link" href="#">Staff</a>
                  <a class="nav-link disabled" href="#">Testimonials</a>
              </nav>
              در اینجا امکان کار با کلاس‌های Flexbox، مانند justify-content-center را نیز مشاهده می‌کنید. برای مثال برای هدایت منوی راهبری به سمت چپ و یا راست صفحه می‌توان بجای center، از end و یا start استفاده کرد. انجام یک چنین کارهایی در نگارش‌های قبلی بوت استرپ بدون Flexbox، مشکل بودند.
              کلاس دیگری را که در اینجا می‌توان استفاده کرد، flex-column است تا آیتم‌های nav، بجای نمایش در یک ردیف، در یک ستون ظاهر شوند:


              و یا می‌توان با استفاده از break-points، سبب شد تا اگر اندازه‌ی صفحه بیش از sm بود، آیتم‌های منوی راهبری، ردیفی و اگر کمتر از آن بود (حالت موبایل)، ستونی نمایش داده شوند:
              <nav class="nav nav-pills justify-content-center flex-column flex-sm-row">


              ایجاد navbars در بوت استرپ 4

              Navbar بوت استرپ 4، بازنویسی کامل شده و کار کردن با آن نسبت به نگارش سوم آن بسیار ساده‌تر شده‌است.
              <body>
                  <nav class="navbar bg-dark navbar-dark navbar-expand-sm">
                      <div class="container">
                          <div class="navbar-nav">
                              <a class="nav-item nav-link active" href="#">Home</a>
                              <a class="nav-item nav-link" href="#">Mission</a>
                              <a class="nav-item nav-link" href="#">Services</a>
                              <a class="nav-item nav-link" href="#">Staff</a>
                              <a class="nav-item nav-link disabled" href="#">Testimonials</a>
                          </div>
                      </div>
                  </nav>
              
                  <div class="container">
              با این خروجی:


              در اینجا، کار با افزودن کلاس navbar به المان nav شروع می‌شود. سپس هر لینک داخل آن، کلاس‌های nav-item nav-link را پیدا می‌کند. در اینجا اگر آیتمی قرار است به صفحه‌ی جاری اشاره کند، با کلاس active مشخص خواهد شد.
              سپس توسط  کلاس‌های bg-dark navbar-dark، رنگ‌های پس زمینه و رنگ متن مشخص شده‌اند. برای مثال می‌توان bg-light navbar-light را نیز آزمایش کرد:
              <nav class="navbar bg-light navbar-light navbar-expand-sm">


              و یا بجای این رنگ‌های پیش‌فرض، در بوت استرپ 4 می‌توان به سادگی رنگ navbar را توسط یک background-color دلخواه، سفارشی سازی کرد:
              <nav class="navbar navbar-dark navbar-expand-sm" style="background-color:red">


              کاری که در نگارش‌های پیشین بوت استرپ به سادگی میسر نبود.

              همچنین اگر دقت کرده باشید از کلاس navbar-expand-sm نیز استفاده شده‌است. حالت پیش‌فرض نمایش آیتم‌های navbar، ستونی است و برای حالت موبایل درنظر گرفته شده‌است. استفاده‌ی از navbar-expand-sm سبب می‌شود تا پس از عرض sm، آیتم‌های navbar همانند شکل‌های فوق، در طی یک ردیف نمایش داده شوند و در عرض کمتر از sm، به صورت یک ستون:


              به علاوه آیتم‌های navbar را داخل یک container قرار داده‌ایم:
              <div class="container">
                    <div class="navbar-nav">
              علت اینجا است که چون navbar تعریف شده خارج از container اصلی است، اگر چنین کاری را انجام ندهیم، آیتم‌های آن از سمت چپ صفحه بدون تراز بودن با container ذیل آن نمایش داده خواهند شد. تعریف یک container داخل navbar، این مشکل عدم تراز بودن عمودی را برطرف می‌کند.


              تعریف متون و لوگو در navbar بوت استرپ 4

              برای تعریف متن لوگوی سایت در navbar به صورت زیر عمل می‌شود:
              <body>
                  <nav class="navbar bg-dark navbar-dark navbar-expand-sm">
                      <div class="container">
                          <div class="navbar-brand">
                              Wisdom Pet Medicine
                          </div>
                          <div class="navbar-nav">
                              <a class="nav-item nav-link active" href="#">Home</a>
                              <a class="nav-item nav-link" href="#">Mission</a>
                              <a class="nav-item nav-link" href="#">Services</a>
                              <a class="nav-item nav-link" href="#">Staff</a>
                              <a class="nav-item nav-link disabled" href="#">Testimonials</a>
                          </div>
                      </div>
                  </nav>
              
                  <div class="container">
              در اینجا با استفاده از کلاس navbar-brand در یک div مجزا، سبب نمایش متن لوگوی سایت شده‌ایم:


              و یا می‌توان بجای div، از المان anchor نیز استفاده کرد تا به صورت لینک نمایش داده شود:
              <a class="navbar-brand" href="#"> Wisdom Pet Medicine </a>
              و بجای متن، تصاویر را نیز می‌توان قرار داد.

              برای تعریف متنی در navbar از کلاس navbar-text استفاده می‌شود:
              <body>
                  <nav class="navbar bg-dark navbar-dark navbar-expand-sm">
                      <div class="container">
                          <a class="navbar-brand d-none d-sm-inline-block" href="#"> Wisdom
                              Pet Medicine </a>
                          <div class="navbar-nav">
                              <a class="nav-item nav-link active" href="#">Home</a>
                              <a class="nav-item nav-link" href="#">Mission</a>
                              <a class="nav-item nav-link" href="#">Services</a>
                              <a class="nav-item nav-link" href="#">Staff</a>
                              <a class="nav-item nav-link disabled" href="#">Testimonials</a>
                          </div>
                          <span class="navbar-text d-none d-xl-inline-block">The best in
                              traditional and alternate
                              medicine</span>
                      </div>
                  </nav>
              
                  <div class="container">
              اما چون این متن طولانی است، بهتر است آن‌را در اندازه‌ی صفحه‌ی xl نمایش دهیم. به همین جهت با افزودن کلاس d-none، آن‌را در تمام اندازه‌ها مخفی می‌کنیم. سپس با افزودن d-xl-inline-block، آن‌را پس از عرض xl نمایان خواهیم کرد.
              همین تنظیم را به navbar-brand، در اندازه‌ی sm نیز اضافه کرده‌ایم تا لوگوی سایت در اندازه‌های موبایل ظاهر نشود.



              افزودن drop downs به navbar در بوت استرپ 4

              برای تبدیل یکی از آیتم‌های منوی راهبری، به منو، از dropdown استفاده می‌شود که نمونه‌ای از آن‌را در مثال زیر مشاهده می‌کنید:
              <body>
                  <nav class="navbar bg-dark navbar-dark navbar-expand-sm">
                      <div class="container">
                          <a class="navbar-brand d-none d-sm-inline-block" href="#">
                              <img src="images/wisdompetlogo.svg" style="width:40px;" alt="">
                              Wisdom Pet Medicine
                          </a>
                          <div class="navbar-nav ml-sm-auto">
                              <a class="nav-item nav-link active" href="#">Home</a>
                              <a class="nav-item nav-link" href="#">Mission</a>
              
                              <div class="dropdown">
                                  <a class="nav-item nav-link dropdown-toggle" data-toggle="dropdown"
                                      id="servicesDropdown" aria-haspopup="true"
                                      aria-expanded="false" href="#">Services</a>
                                  <div class="dropdown-menu" aria-labelledby="servicesDropdown">
                                      <a href="#" class="dropdown-item">Grooming</a>
                                      <a href="#" class="dropdown-item">General Health</a>
                                      <a href="#" class="dropdown-item">Nutrition</a>
                                      <a href="#" class="dropdown-item">Pest Control</a>
                                      <a href="#" class="dropdown-item">Vaccinations</a>
                                  </div>
                              </div>
              
                              <a class="nav-item nav-link" href="#">Staff</a>
                              <a class="nav-item nav-link disabled" href="#">Testimonials</a>
                          </div>
                          <span class="navbar-text d-none d-xl-inline-block">The best in
                              traditional and alternate
                              medicine</span>
                      </div>
                  </nav>
              
                  <div class="container">
              با این خروجی:


              - دراپ‌داون نیاز به یک container دارد که آن‌را با تعریف یک div با کلاس dropdown تعریف کرده‌ایم.
              - سپس به لینکی که قرار است آن‌را نمایش دهد، کلاس dropdown-toggle را اضافه می‌کنیم تا آیکن مثلثی رو به پایینی را نمایان کند. وجود این مثلث، بیانگر وجود منویی به همراه آن است.
              - اکنون با تنظیم data-toggle به dropdown، کدهای جاوا اسکریپتی بوت استرپ، این المان را به صورت یک dropdown پردازش می‌کنند و نیازی به افزودن اسکریپتی به صفحه برای فعالسازی آن نیست. ویژگی‌های aria-expanded و aria-haspopup نیز به مقدار دهی پیش‌فرض‌های کدهای جاوا اسکریپتی آن کمک می‌کنند.
              - خود منو توسط دربرگیرنده‌ای با کلاس dropdown-menu و آیتم‌هایی با کلاس dropdown-item تشکیل می‌شود.
              - در ادامه برای متصل کردن این دربرگیرنده به لینک نمایش دهنده‌ی منو، یک id را به لینک انتساب می‌دهیم (به نام servicesDropdown) و سپس aria-labelledby دربرگیرنده را به این id، مقدار دهی می‌کنیم.
              - در این مثال با استفاده از کلاس navbar-nav ml-sm-auto، سبب شده‌ایم تا منوی سایت، از لبه‌ی سمت راست صفحه پس از عرض sm، شروع شود.


              افزودن المان‌های فرم‌ها به منوی راهبری سایت

              برای اضافه کردن المان‌های فرم به منوی راهبری سایت، ابتدا نیاز است کلاس form-inline را بر روی container این فرم قرار داد و سپس به ورودی‌های این فرم، کلاس form-control را اضافه می‌کنیم. اگر نیاز بود، توسط کلاس‌های margin و padding مخصوص بوت استرپ 4 مانند mr-2 نیز می‌توان بین آن‌ها فاصله ایجاد کرد:
              <body>
                  <nav class="navbar navbar-dark bg-dark navbar-expand-sm">
                      <div class="container">
                          <div class="navbar-nav">
                              <a class="nav-item nav-link active" href="#">Home</a>
                              <a class="nav-item nav-link" href="#">Mission</a>
                              <a class="nav-item nav-link" href="#">Services</a>
                              <a class="nav-item nav-link" href="#">Staff</a>
                              <a class="nav-item nav-link" href="#">Testimonials</a>
                          </div>
                          <form class="form-inline">
                              <input type="text" placeholder="Search..." class="form-control mr-2">
                              <button class="btn btn-outline-light" type="submit">Go</button>
                          </form>
                      </div>
                  </nav>
              
                  <div class="container">
              با این خروجی:


              بوت استرپ در اندازه‌ی بزرگتر صفحه، فرم را به سمت راست و آیتم‌های منو را در سمت چپ نمایش می‌دهد.


              کنترل محل قرارگیری المان‌ها در منوی راهبری سایت

              توسط کلاس‌هایی مانند fixed-top (قرار گرفتن در بالای صفحه)، fixed-bottom (قرار گرفتن در پایین صفحه) و sticky-top، می‌توان محل قرارگیری منوی راهبری را تغییر داد. این کلاس‌ها را در مطلب «طرحبندی صفحات وب با بوت استرپ 4 - قسمت دوم» پیشتر بررسی کردیم.
              برای توضیح حالت sticky-top، فرض کنید بالای منو، تصویر بزرگی از لوگوی سایت را دارید و این منو زیر آن قرار گرفته‌است. زمانیکه صفحه به سمت پایین اسکرول می‌شود، این منو نیز پایین خواهد آمد تا جائیکه در لبه‌ی بالای صفحه قرار گیرد. پس از آن، این منو در همین ناحیه باقی مانده و شبیه به fixed-top عمل می‌کند.

              یک نکته: اگر fixed-bottom را مورد استفاده قرار دادید:
              <nav class="navbar navbar-dark bg-dark navbar-expand-sm fixed-bottom">
               ممکن است متن پایین صفحه زیر این منو قرار گیرد و قابل خوانده شدن نباشد. برای این منظور می‌توان از کلاس margin-bottom بر روی container استفاده کرد:
              <div class="container mb-5">



              اضافه کردن منوی همبرگری به منوی راهبری سایت

              در مورد کلاس navbar-expand-sm در این مطلب توضیح دادیم. هرچند قابلیت عمودی و افقی شدن خودکار آیتم‌های منوی راهبری بسیار جالب و کاربری است، اما در صفحات نمایشی کوچک، این نمایش عمودی می‌تواند ارتفاع قابل ملاحظه‌ای را به خود اختصاص دهد. به همین جهت می‌خواهیم نمایش آیتم‌های آن‌را وابسته به تصمیم کاربر کنیم.
              <body>
                  <nav class="navbar navbar-dark bg-dark navbar-expand-sm">
                      <div class="container">
                          <a href="#" class="navbar-brand">Wisdom Pet Medicine</a>
                          <button class="navbar-toggler" type="button" data-toggle="collapse"
                              data-target="#myToggle" aria-controls="myToggle" aria-expanded="false"
                              aria-label="Toggle navigation">
                              <span class="navbar-toggler-icon"></span>
                          </button>
                          <div class="collapse navbar-collapse" id="myToggle">
                              <div class="navbar-nav">
                                  <a class="nav-item nav-link active" href="#">Home</a>
                                  <a class="nav-item nav-link" href="#">Mission</a>
                                  <a class="nav-item nav-link" href="#">Services</a>
                                  <a class="nav-item nav-link" href="#">Staff</a>
                                  <a class="nav-item nav-link" href="#">Testimonials</a>
                              </div>
                          </div>
                      </div>
                  </nav>
              
                  <div class="container">
              - در اینجا نحوه‌ی پیاده سازی منوی همبرگری را در بوت استرپ 4 ملاحظه می‌کنید.
              - ابتدا نیاز است دکمه‌ی این منو اضافه شود که توسط کلاس navbar-toggler مشخص شده‌است. سپس با توجه به اینکه این کامپوننت توسط کدهای جاوا اسکریپتی بوت استرپ کار می‌کند، اطلاعات مورد نیاز آن‌را توسط ویژگی‌های data-toggle، data-target و aria مشخص می‌کنیم.
              - این دکمه نیاز دارد تا به یک div با کلاس collapse navbar-collapse متصل شود. این اتصال نیز از طریق id آن صورت می‌گیرد که در ویژگی data-target مقدار دهی شده‌است.
              - اگر این دکمه را پس از navbar-brand قرار دهیم، در سمت چپ صفحه و اگر پیش از آن قرار دهیم، در سمت راست صفحه ظاهر می‌شود.

              در حالت نمایش sm، آیتم‌های منو مخفی شده:


              با کلیک بر روی دکمه‌ی منوی همبرگری آن، گزینه‌های منو نمایش داده می‌شوند:


              و در حالت اندازه‌ی بزرگتر صفحه، محو می‌شود:




              کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: Bootstrap4_07.zip
              مطالب
              احراز هویت و اعتبارسنجی کاربران در برنامه‌های Angular - قسمت چهارم - به روز رسانی خودکار توکن‌ها
              در قسمت قبل، عملیات ورود به سیستم و خروج از آن‌را تکمیل کردیم. پس از ورود شخص به سیستم، هربار انقضای توکن دسترسی او، سبب خواهد شد تا وقفه‌ای در کار جاری کاربر، جهت لاگین مجدد صورت گیرد. برای این منظور، قسمتی از مطالب «اعتبارسنجی مبتنی بر JWT در ASP.NET Core 2.0 بدون استفاده از سیستم Identity»  و یا «پیاده سازی JSON Web Token با ASP.NET Web API 2.x» به تولید refresh_token در سمت سرور اختصاص دارد که از نتیجه‌ی آن در اینجا استفاده خواهیم کرد. عملیات به روز رسانی خودکار توکن دسترسی (access_token در اینجا) سبب خواهد شد تا کاربر پس از انقضای آن، نیازی به لاگین دستی مجدد نداشته باشد. این به روز رسانی در پشت صحنه و به صورت خودکار صورت می‌گیرد. refresh_token یک guid است که به سمت سرور ارسال می‌شود و برنامه‌ی سمت سرور، پس از تائید آن (بررسی صحت وجود آن در بانک اطلاعاتی و سپس واکشی اطلاعات کاربر متناظر با آن)، یک access_token جدید را صادر می‌کند.


              ایجاد یک تایمر برای مدیریت دریافت و به روز رسانی توکن دسترسی

              در مطلب «ایجاد تایمرها در برنامه‌های Angular» با روش کار با تایمرهای reactive آشنا شدیم. در اینجا قصد داریم از این امکانات جهت پیاده سازی به روز کننده‌ی خودکار access_token استفاده کنیم. در مطلب «احراز هویت و اعتبارسنجی کاربران در برنامه‌های Angular - قسمت دوم - سرویس اعتبارسنجی»، زمان انقضای توکن را به کمک کتابخانه‌ی jwt-decode، از آن استخراج کردیم:
                getAccessTokenExpirationDateUtc(): Date {
                  const decoded = this.getDecodedAccessToken();
                  if (decoded.exp === undefined) {
                    return null;
                  }
                  const date = new Date(0); // The 0 sets the date to the epoch
                  date.setUTCSeconds(decoded.exp);
                  return date;
                }
              اکنون از این زمان در جهت تعریف یک تایمر خود متوقف شونده استفاده می‌کنیم:
                private refreshTokenSubscription: Subscription;
              
                scheduleRefreshToken() {
                  if (!this.isLoggedIn()) {
                    return;
                  }
              
                  this.unscheduleRefreshToken();
              
                  const expiresAtUtc = this.getAccessTokenExpirationDateUtc().valueOf();
                  const nowUtc = new Date().valueOf();
                  const initialDelay = Math.max(1, expiresAtUtc - nowUtc);
                  console.log("Initial scheduleRefreshToken Delay(ms)", initialDelay);
                  const timerSource$ = Observable.timer(initialDelay);
                  this.refreshTokenSubscription = timerSource$.subscribe(() => {
                    this.refreshToken();      
                  });
                }
              
                unscheduleRefreshToken() {
                  if (this.refreshTokenSubscription) {
                    this.refreshTokenSubscription.unsubscribe();
                  }
                }
              کار متد scheduleRefreshToken، شروع تایمر درخواست توکن جدید است.
              - در ابتدا بررسی می‌کنیم که آیا کاربر لاگین کرده‌است یا خیر؟ آیا اصلا دارای توکنی هست یا خیر؟ اگر خیر، نیازی به شروع این تایمر نیست.
              -  سپس تایمر قبلی را در صورت وجود، خاتمه می‌دهیم.
              - در مرحله‌ی بعد، کار محاسبه‌ی میزان زمان تاخیر شروع تایمر Observable.timer را انجام می‌دهیم. پیشتر زمان انقضای توکن موجود را استخراج کرده‌ایم. اکنون این زمان را از زمان جاری سیستم برحسب UTC کسر می‌کنیم. مقدار حاصل، initialDelay این تایمر خواهد بود. یعنی این تایمر به مدت initialDelay صبر خواهد کرد و سپس تنها یکبار اجرا می‌شود. پس از اجرا، ابتدا متد refreshToken ذیل را فراخوانی می‌کند تا توکن جدیدی را دریافت کند.

              در متد unscheduleRefreshToken کار لغو تایمر جاری در صورت وجود انجام می‌شود.

              متد درخواست یک توکن جدید بر اساس refreshToken موجود نیز به صورت ذیل است:
                refreshToken() {
                  const headers = new HttpHeaders({ "Content-Type": "application/json" });
                  const model = { refreshToken: this.getRawAuthToken(AuthTokenType.RefreshToken) };
                  return this.http
                    .post(`${this.appConfig.apiEndpoint}/${this.appConfig.refreshTokenPath}`, model, { headers: headers })
                    .finally(() => {
                      this.scheduleRefreshToken();
                    })
                    .map(response => response || {})
                    .catch((error: HttpErrorResponse) => Observable.throw(error))
                    .subscribe(result => {
                      console.log("RefreshToken Result", result);
                      this.setLoginSession(result);
                    });
                }
              در اینجا هرزمانیکه تایمر اجرا شود، این متد فراخوانی شده و مقدار refreshToken فعلی را به سمت سرور ارسال می‌کند. سپس سرور این مقدار را بررسی کرده و در صورت تعیین اعتبار، یک access_token و refresh_token جدید را به سمت کلاینت ارسال می‌کند که نتیجه‌ی آن به متد setLoginSession جهت ذخیره سازی ارسال خواهد شد.
              در آخر چون این تایمر، خود متوقف شونده‌است (متد Observable.timer بدون پارامتر دوم آن فراخوانی شده‌است)، یکبار دیگر کار زمانبندی دریافت توکن جدید بعدی را در متد finally انجام می‌دهیم (متد scheduleRefreshToken را مجددا فراخوانی می‌کنیم).


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

              تا اینجا متدهای مورد نیاز شروع زمانبندی دریافت توکن جدید، خاتمه‌ی زمانبندی و دریافت و به روز رسانی توکن جدید را پیاده سازی کردیم. محل قرارگیری و فراخوانی این متدها در سرویس Auth، به صورت ذیل هستند:
              الف) در سازنده‌ی کلاس:
                constructor(
                  @Inject(APP_CONFIG) private appConfig: IAppConfig,
                  private browserStorageService: BrowserStorageService,
                  private http: HttpClient,
                  private router: Router
                ) {
                  this.updateStatusOnPageRefresh();
                  this.scheduleRefreshToken();
                }
              این مورد برای مدیریت حالتی که کاربر صفحه را refresh کرده‌است و یا پس از مدتی مجددا از ابتدا برنامه را بارگذاری کرده‌است، مفید است.

              ب) پس از لاگین موفقیت آمیز
              در متد لاگین، پس از دریافت یک response موفقیت آمیز و تنظیم و ذخیره سازی توکن‌های دریافتی، کار زمانبندی دریافت توکن دسترسی بعدی بر اساس refresh_token فعلی انجام می‌شود:
              this.setLoginSession(response);
              this.scheduleRefreshToken();

              ج) پس از خروج از سیستم
              در متد logout، پس از حذف توکن‌های کاربر از کش مرورگر، کار لغو تایمر زمانبندی دریافت توکن بعدی نیز صورت خواهد گرفت:
              this.deleteAuthTokens();
              this.unscheduleRefreshToken();

              در این حالت اگر برنامه را اجرا کنید، یک چنین خروجی را که بیانگر دریافت خودکار توکن‌های جدید است، پس از مدتی در کنسول developer مرورگر مشاهده خواهید کرد:


              ابتدا متد scheduleRefreshToken اجرا شده و تاخیر آغازین تایمر محاسبه شده‌است. پس از مدتی متد refreshToken توسط تایمر فراخوانی شده‌است. در آخر مجددا متد scheduleRefreshToken جهت شروع یک زمانبندی جدید، اجرا شده‌است.

              اعداد initialDelay محاسبه شده‌ای را هم که ملاحظه می‌کنید، نزدیک به همان 2 دقیقه‌ی تنظیمات سمت سرور در فایل appsettings.json هستند:
                "BearerTokens": {
                  "Key": "This is my shared key, not so secret, secret!",
                  "Issuer": "http://localhost/",
                  "Audience": "Any",
                  "AccessTokenExpirationMinutes": 2,
                  "RefreshTokenExpirationMinutes": 60
                }


              کدهای کامل این سری را از اینجا می‌توانید دریافت کنید.
              برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کرده‌اید. سپس از طریق خط فرمان به ریشه‌ی پروژه‌ی ASPNETCore2JwtAuthentication.AngularClient وارد شده و دستور npm install را صادر کنید تا وابستگی‌های آن دریافت و نصب شوند. در آخر با اجرای دستور ng serve -o، برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد (و یا همان اجرای فایل ng-serve.bat). همچنین باید به پوشه‌ی ASPNETCore2JwtAuthentication.WebApp نیز مراجعه کرده و فایل dotnet_run.bat را اجرا کنید، تا توکن سرور برنامه نیز فعال شود.
              مطالب
              ساخت یک اپلیکیشن ساده ToDo با ASP.NET Identity
              یک سناریوی فرضی را در نظر بگیرید. اگر بخواهیم IdentityDbContext و دیگر DbContext‌های اپلیکیشن را ادغام کنیم چه باید کرد؟ مثلا یک سیستم وبلاگ که برخی کاربران می‌توانند پست جدید ثبت کنند، برخی تنها می‌توانند کامنت بگذارند و تمامی کاربران هم اختیارات مشخص دیگری دارند. در چنین سیستمی شناسه کاربران (User ID) در بسیاری از مدل‌ها (موجودیت‌ها و مدل‌های اپلیکیشن) وجود خواهد داشت تا مشخص شود هر رکورد به کدام کاربر متعلق است. در این مقاله چنین سناریو هایی را بررسی می‌کنیم و best practice‌های مربوطه را مرور می‌کنیم.
              در این پست یک اپلیکیشن ساده ToDo خواهیم ساخت که امکان تخصیص to-do‌ها به کاربران را نیز فراهم می‌کند. در این مثال خواهیم دید که چگونه می‌توان مدل‌های مختص به سیستم عضویت (IdentityDbContext) را با مدل‌های دیگر اپلیکیشن مخلوط و استفاده کنیم.


              تعریف نیازمندی‌های اپلیکیشن

              • تنها کاربران احراز هویت شده قادر خواهند بود تا لیست ToDo‌های خود را ببینند، آیتم‌های جدید ثبت کنند یا داده‌های قبلی را ویرایش و حذف کنند.
              • کاربران نباید آیتم‌های ایجاد شده توسط دیگر کاربران را ببینند.
              • تنها کاربرانی که به نقش Admin تعلق دارند باید بتوانند تمام ToDo‌های ایجاد شده را ببینند.
              پس بگذارید ببینیم چگونه می‌شود اپلیکیشنی با ASP.NET Identity ساخت که پاسخگوی این نیازمندی‌ها باشد.
              ابتدا یک پروژه ASP.NET MVC جدید با مدل احراز هویت Individual User Accounts بسازید. در این اپلیکیشن کاربران قادر خواهند بود تا بصورت محلی در وب سایت ثبت نام کنند و یا با تامین کنندگان دیگری مانند گوگل و فیسبوک وارد سایت شوند. برای ساده نگاه داشتن این پست ما از حساب‌های کاربری محلی استفاده می‌کنیم.
              در مرحله بعد ASP.NET Identity را راه اندازی کنید تا بتوانیم نقش مدیر و یک کاربر جدید بسازیم. می‌توانید با اجرای اپلیکیشن راه اندازی اولیه را انجام دهید. از آنجا که سیستم ASP.NET Identity توسط Entity Framework مدیریت می‌شود می‌توانید از تنظیمات پیکربندی Code First برای راه اندازی دیتابیس خود استفاده کنید.
              در قدم بعدی راه انداز دیتابیس را در Global.asax تعریف کنید.
              Database.SetInitializer<MyDbContext>(new MyDbInitializer());


              ایجاد نقش مدیر و کاربر جدیدی که به این نقش تعلق دارد

              اگر به قطعه کد زیر دقت کنید، می‌بینید که در خط شماره 5 متغیری از نوع UserManager ساخته ایم که امکان اجرای عملیات گوناگونی روی کاربران را فراهم می‌کند. مانند ایجاد، ویرایش، حذف و اعتبارسنجی کاربران. این کلاس که متعلق به سیستم ASP.NET Identity است همتای SQLMembershipProvider در ASP.NET 2.0 است.
              در خط 6 یک RoleManager می‌سازیم که امکان کار با نقش‌ها را فراهم می‌کند. این کلاس همتای SQLRoleMembershipProvider در ASP.NET 2.0 است.
              در این مثال نام کلاس کاربران (موجودیت کاربر در IdentityDbContext) برابر با "MyUser" است، اما نام پیش فرض در قالب‌های پروژه VS 2013 برابر با "ApplicationUser" می‌باشد.
              public class MyDbInitializer : DropCreateDatabaseAlways<MyDbContext>
                   {
                        protected override void Seed(MyDbContext context)
                        {
                            var UserManager = new UserManager<MyUser>(new 
              
                                                              UserStore<MyUser>(context)); 
              
                            var RoleManager = new RoleManager<IdentityRole>(new 
                                                        RoleStore<IdentityRole>(context));
                 
                            string name = "Admin";
                            string password = "123456";
               
                 
                            //Create Role Admin if it does not exist
                            if (!RoleManager.RoleExists(name))
                            {
                                var roleresult = RoleManager.Create(new IdentityRole(name));
                            }
                 
                            //Create User=Admin with password=123456
                            var user = new MyUser();
                            user.UserName = name;
                            var adminresult = UserManager.Create(user, password);
                 
                            //Add User Admin to Role Admin
                            if (adminresult.Succeeded)
                            {
                                var result = UserManager.AddToRole(user.Id, name);
                            }
                            base.Seed(context);
                        }
                    }


              حال فایلی با نام Models/AppModels.cs بسازید و مدل EF Code First اپلیکیشن را تعریف کنید. از آنجا که از EF استفاده می‌کنیم، روابط کلید‌ها بین کاربران و ToDo‌ها بصورت خودکار برقرار می‌شود.
              public class MyUser : IdentityUser
                    {
                        public string HomeTown { get; set; }
                        public virtual ICollection<ToDo>
                                             ToDoes { get; set; }
                    }
                 
                    public class ToDo
                    {
                        public int Id { get; set; }
                        public string Description { get; set; }
                        public bool IsDone { get; set; }
                        public virtual MyUser User { get; set; }
                    }

              در قدم بعدی با استفاده از مکانیزم Scaffolding کنترلر جدیدی بهمراه تمام View‌ها و متدهای لازم برای عملیات CRUD بسازید. برای اطلاعات بیشتر درباره  نحوه استفاده از مکانیزم Scaffolding به این لینک مراجعه کنید.
              لطفا دقت کنید که از DbContext فعلی استفاده کنید. این کار مدیریت داده‌های Identity و اپلیکیشن شما را یکپارچه‌تر می‌کند. DbContext شما باید چیزی شبیه به کد زیر باشد.
                   public class MyDbContext : IdentityDbContext<MyUser>
                    {
                        public MyDbContext()
                            : base("DefaultConnection")
                        {
                         }
                  
                         protected override void OnModelCreating(DbModelBuilder modelBuilder)
                         {
                        public System.Data.Entity.DbSet<AspnetIdentitySample.Models.ToDo> 
                                   ToDoes { get; set; }
                    }

              تنها کاربران احراز هویت شده باید قادر به اجرای عملیات CRUD باشند

              برای این مورد از خاصیت Authorize استفاده خواهیم کرد که در MVC 4 هم وجود داشت. برای اطلاعات بیشتر لطفا به این لینک مراجعه کنید.
              [Authorize]
              public class ToDoController : Controller

              کنترلر ایجاد شده را ویرایش کنید تا کاربران را به ToDo‌ها اختصاص دهد. در این مثال تنها اکشن متدهای Create و List را بررسی خواهیم کرد. با دنبال کردن همین روش می‌توانید متدهای Edit و Delete را هم بسادگی تکمیل کنید.
              یک متد constructor جدید بنویسید که آبجکتی از نوع UserManager می‌پذیرد. با استفاده از این کلاس می‌توانید کاربران را در ASP.NET Identity مدیریت کنید.
               private MyDbContext db;
                        private UserManager<MyUser> manager;
                        public ToDoController()
                        {
                            db = new MyDbContext();
                            manager = new UserManager<MyUser>(new UserStore<MyUser>(db));
                        }

              اکشن متد Create را بروز رسانی کنید

              هنگامی که یک ToDo جدید ایجاد می‌کنید، کاربر جاری را در ASP.NET Identity پیدا می‌کنیم و او را به ToDo‌ها اختصاص می‌دهیم.
                  public async Task<ActionResult> Create
                        ([Bind(Include="Id,Description,IsDone")] ToDo todo)
                        {
                            var currentUser = await manager.FindByIdAsync
                                                               (User.Identity.GetUserId()); 
                            if (ModelState.IsValid)
                            {
                                todo.User = currentUser;
                                db.ToDoes.Add(todo);
                                await db.SaveChangesAsync();
                                return RedirectToAction("Index");
                            }
                 
                            return View(todo);
                        }

              اکشن متد List را بروز رسانی کنید

              در این متد تنها ToDo‌های کاربر جاری را باید بگیریم.
                        public ActionResult Index()
                        {
                            var currentUser = manager.FindById(User.Identity.GetUserId());
              
                             return View(db.ToDoes.ToList().Where(
                                                 todo => todo.User.Id == currentUser.Id));
                        }

              تنها مدیران سایت باید بتوانند تمام ToDo‌ها را ببینند

              بدین منظور ما یک اکشن متد جدید به کنترل مربوطه اضافه می‌کنیم که تمام ToDo‌ها را لیست می‌کند. اما دسترسی به این متد را تنها برای کاربرانی که در نقش مدیر وجود دارند میسر می‌کنیم.
                   [Authorize(Roles="Admin")]
                        public async Task<ActionResult> All()
                        {
                            return View(await db.ToDoes.ToListAsync());
                        }

              نمایش جزئیات کاربران از جدول ToDo ها

              از آنجا که ما کاربران را به ToDo هایشان مرتبط می‌کنیم، دسترسی به داده‌های کاربر ساده است. مثلا در متدی که مدیر سایت تمام آیتم‌ها را لیست می‌کند می‌توانیم به اطلاعات پروفایل تک تک کاربران دسترسی داشته باشیم و آنها را در نمای خود بگنجانیم. در این مثال تنها یک فیلد بنام HomeTown اضافه شده است، که آن را در کنار اطلاعات ToDo نمایش می‌دهیم.
               @model IEnumerable<AspnetIdentitySample.Models.ToDo>
                 
                @{
                  ViewBag.Title = "Index";
                }
                 
                <h2>List of ToDoes for all Users</h2>
                <p>
                    Notice that we can see the User info (UserName) and profile info such as HomeTown for the user as well.
                    This was possible because we associated the User object with a ToDo object and hence
                    we can get this rich behavior.
                12:  </p>
                 
                <table class="table">
                    <tr>
                        <th>
                            @Html.DisplayNameFor(model => model.Description)
                        </th>
                        <th>
                            @Html.DisplayNameFor(model => model.IsDone)
                        </th>
                        <th>@Html.DisplayNameFor(model => model.User.UserName)</th>
                        <th>@Html.DisplayNameFor(model => model.User.HomeTown)</th>
                    </tr>
                25:   
                26:      @foreach (var item in Model)
                27:      {
                28:          <tr>
                29:              <td>
                30:                  @Html.DisplayFor(modelItem => item.Description)
                31:              </td>
                32:              <td>
                                @Html.DisplayFor(modelItem => item.IsDone)
                            </td>
                            <td>
                                @Html.DisplayFor(modelItem => item.User.UserName)
                            </td>
                            <td>
                                @Html.DisplayFor(modelItem => item.User.HomeTown)
                            </td>
                        </tr>
                    }
                 
                </table>

              صفحه Layout را بروز رسانی کنید تا به ToDo‌ها لینک شود

              <li>@Html.ActionLink("ToDo", "Index", "ToDo")</li>
               <li>@Html.ActionLink("ToDo for User In Role Admin", "All", "ToDo")</li>

              حال اپلیکیشن را اجرا کنید. همانطور که مشاهده می‌کنید دو لینک جدید به منوی سایت اضافه شده اند.


              ساخت یک ToDo بعنوان کاربر عادی

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

              پس از ساختن یک ToDo می‌توانید لیست رکوردهای خود را مشاهده کنید. دقت داشته باشید که رکوردهایی که کاربران دیگر ثبت کرده اند برای شما نمایش داده نخواهند شد.


              مشاهده تمام ToDo‌ها بعنوان مدیر سایت

              روی لینک ToDoes for User in Role Admin کلیک کنید. در این مرحله باید مجددا به صفحه ورود هدایت شوید چرا که شما در نقش مدیر نیستید و دسترسی کافی برای مشاهده صفحه مورد نظر را ندارید. از سایت خارج شوید و توسط حساب کاربری مدیری که هنگام راه اندازی اولیه دیتابیس ساخته اید وارد سایت شوید.
              User = Admin
              Password = 123456
              پس از ورود به سایت بعنوان یک مدیر، می‌توانید ToDo‌های ثبت شده توسط تمام کاربران را مشاهده کنید.

              مطالب
              امکان تغییر رشته‌ی اتصالی به بانک اطلاعاتی در EF Core در زمان اجرای برنامه
              تغییر پویای رشته‌ی اتصالی به بانک اطلاعاتی در نگارش‌های پیشین EF، مشکل بودند که نمونه‌هایی از آن را پیشتر در مطالب زیر مشاهده کرده‌اید:
              - «تنظیم رشته اتصالی Entity Framework به بانک اطلاعاتی به وسیله کد»
              - «استفاده از چندین بانک اطلاعاتی به صورت همزمان در EF Code First»

              اما EF Core نه تنها این مشکل را پوشش را داده‌است، بلکه امکان تزریق وابستگی‌ها و استفاده‌ی از سرویس‌های مختلف را نیز در این حین، پیش بینی کرده‌است که در ادامه جزئیات آن‌را مرور می‌کنیم.


              نیاز به تغییر رشته‌ی اتصالی به بانک اطلاعاتی در زمان اجرا

              دلایل نیاز به امکان تغییر رشته‌ی اتصالی در زمان اجرا شامل موارد زیر هستند:
              - در برنامه‌هایی کمی پیچیده‌تر و سابقه دار، ممکن است عملیات تجاری یکسال را در بانک اطلاعاتی سال 98 و دیگری را در بانک اطلاعاتی سال 99 ثبت کنید. در این حالت کاربران باید بتوانند در زمان اجرا به هر بانک اطلاعاتی که پیشتر با آن کار کرده‌اند، متصل شده و از آن استفاده کنند.
              - یکی از روش‌های پیاده سازی برنامه‌های چند مستاجری، داشتن یک بانک اطلاعاتی مجزا، به ازای هر مستاجر است. در این حالت نیز تک برنامه‌ی ما باید بتواند بر اساس Id مشتری، بانک اطلاعاتی متناظری را در زمان اجرا انتخاب کند.
              - نیاز به داشتن چندین context در برنامه و کار با بانک‌های اطلاعاتی متفاوت در زمان اجرا؛ مانند کار با SQL Server، اوراکل و یا SQLite


              روش تغییر رشته‌ی اتصالی به بانک اطلاعاتی در EF Core در زمان اجرای برنامه

              اگر به روش ثبت متداول سرویس DbContext برنامه و پروایدر آن دقت کنیم:
              services.AddDbContext<ApplicationDbContext>(options =>
                    options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")
              ));
              یک action delegate قابل مشاهده‌است. کار این اکشن، تنظیم پروایدر و تمام نیازهای یک رشته‌ی اتصالی به بانک اطلاعاتی، جهت شروع به کار با Context برنامه است. نکته‌ی مهمی که در اینجا وجود دارد، فراخوانی هرباره‌ی این action، به ازای هر اتصال تشکیل شده‌است. یعنی کدهای داخل این action delegate کش نمی‌شوند و همین مساله امکان تغییر پویای آن‌ها را میسر می‌کند.

              یک نکته: چون این اطلاعات کش نمی‌شوند، اگر رشته‌ی اتصالی شما ثابت است (و نیازی به تغییر آن در زمان اجرای برنامه نیست)، محل تامین آن‌را به پیش از سطر services.AddDbContext انتقال دهید و فقط نتیجه‌ی محاسبه شده‌ی نهایی را استفاده کنید تا کارآیی برنامه افزایش یابد؛ در غیراینصورت فراخوانی Configuration.GetConnectionString مدام تکرار خواهد شد.


              دریافت یک قالب قابل تغییر از تنظیمات برنامه و تغییر آن با هدرهای درخواست رسیده‌ی به آن

              فرض کنید قالب رشته‌ی اتصالی برنامه در فایل appsettings.json به صورت زیر است:
              "ConnectionStrings": {
                  "ConnectionTemplate": "Data Source=.;Initial Catalog={db_Name};Integrated Security=True",
              }
              و db_Name آن قرار است برای مثال از یک query string، سشن، کوکی و یا فیلد خاصی در هدر HTTP رسیده تامین شود. برای مثال سال مالی انتخابی و یا شماره مستاجر انتخابی به صورت یک فیلد خاص HTTP به سمت برنامه ارسال می‌شوند.
              بنابراین اکنون نیاز است به ازای هر درخواست رسیده بتوان به سرویس IHttpContextAccessor و شیء HttpContext.Request جاری دسترسی یافت و سپس از هدرهای رسیده، برای مثال هدر ویژه‌ی tenantId و یا year را پردازش کرد؛ اما در تعریف services.AddDbContext فوق چگونه می‌توان اینکار را انجام داد؟
              خوشبختانه متد services.AddDbContext، دارای یک overload دیگر نیز هست که امکان دسترسی به تمام سرویس‌های جاری سیستم را میسر می‌کند:
              services.AddDbContext<ApplicationDbContext>((serviceProvider, dbContextBuilder) =>
              {
                 var connectionStringTemplate = Configuration.GetConnectionString("ConnectionTemplate");
                 var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
                 var dbName = httpContextAccessor.HttpContext.Request.Headers["tenantId"].First();
                 var connectionString = connectionStringTemplate.Replace("{db_Name}", dbName);
                 dbContextBuilder.UseSqlServer(connectionString);
              });
              همانطور که مشاهده می‌کنید، overload دوم متد services.AddDbContext، امکان ارسال serviceProvider را نیز به این action delegate دارد. پس از آن می‌توان توسط متد GetRequiredService آن به هر سرویس مدنظری که در سیستم ثبت شده‌است، دسترسی یافت و برای مثال در اینجا فیلد هدر سفارشی tenantId را از آن استخراج نمود و در قالب رشته‌ی اتصالی به بانک اطلاعاتی، در زمان اجرا به صورت پویایی جایگزین کرد.
              همچنین در صورت نیاز می‌توان UseSqlServer آن‌را نیز در این action delegate به هر پروایدر دیگری در زمان اجرا تغییر داد و از این لحاظ محدودیتی وجود ندارد.

              یک نکته: البته برنامه نباید هر tenantId ای را پردازش کند و این خودش می‌تواند تبدیل به یک نقیصه‌ی امنیتی شود. به همین جهت برای مثال می‌توان tenantId را در یک JWT قرار داد و در حین تعیین اعتبار آن و کاربر جاری، این مقدار را نیز بررسی کرد.
              مطالب
              معرفی قالب پروژه Web API مبتنی‌بر ASP.NET Core Web API و زیرساخت DNTFrameworkCore
              بعد از انتشار نسخه اولیه زیرساخت DNTFrameworkCore، در این مطلب قصد دارم قالب تهیه شده برپایه زیرساخت مذکور را معرفی کنم. در این قالب سیستم اعتبارسنجی کاربران مبتنی‌برJWT نیز تدارک دیده شده است.
              ‌‌‌

              نصب قالب پروژه از طریق نیوگت


              ابتدا برای نصب قالب تهیه شده از طریق نیوگت، دستور زیر را اجرا کنید:
              dotnet new --install DNTFrameworkCoreTemplateAPI::*‌‌
              ‌‌
              حال برای ایجاد اولین پروژه، دستور زیر را اجرا کنید:
              dotnet new dntcore-api
              ‌‌
              بعد از اجرای دستور بالا، ساختاری مشابه شکل زیر در اختیار شما می‌باشد:


              بررسی قسمت‌های مختلف قالب پروژه

              1- پروژه Domain دربرگیرنده Domain Model سیستم می‌باشد؛ تفاوتی ندارد Rich Domain Model یا Anemic Domain Model باشد. به عنوان مثال، به صورت پیش‌فرض موجودیت‎‌های مرتبط سیستم احراز هویت و کنترل دسترسی در این قالب طراحی شده‌اند.


              2- پروژه Infrastructure دربرگیرنده DbContext، مهاجرت‌ها و کلاس‌های تنظیمات نگاشت موجودیت‌ها به جداول بانک اطلاعاتی پروژه می‌باشد. به عنوان مثال، به صورت پیش‌فرض تنظیمات نگاشت موجودیت‌های کاربر و گروه‌کاربری و موجودیت‌های وابسته آنها در این قالب پیاده‌سازی شده‌اند. همچنین دو مهاجرت CreateInitialSchema و CreateIdentitySchema ایجاده شده‌اند.



              3- پروژه Application دربرگیرنده مدل‌ها/DTOها‎‌، اعتبارسنج‌های مدل‌ها، سرویس‌ها و همچنین Eventهای سفارشی و Handlerهای رویدادهای متناظر با موجودیت‌های سیستم، می‌باشد. همانطور که شکل زیر ملاحظه می‌کنید، برای موجودیت‌های کاربر و گروه‌کاربری طراحی و پیاده‌سازی پیش‌فرضی از قسمت‌های مذکور ارائه شده است.


              4- پروژه Resouces دربرگیرنده فایل‌های منبع resx می‌باشد و همچنین به صورت پیش‌فرض بحث انتقال منابع به یک اسمبلی جداگانه نیز اعمال شده است.


              5- از پروژه Common نیز می‌توان به عنوان دربرگیرنده کلاس‌های کمکی مورد استفاده در سایر قسمت‌ها، بهره برد.

              6- پروژه UnitTests دربرگیرده آزمون‌های واحد پروژه می‌باشد. به عنوان مثال، به صورت پیش‌فرض آزمون‌های واحد مرتبط با UserValidator و RoleValidator به صورت کامل در این قالب تدارک دیده شده است.

              7- پروژه IntegrationTests دربرگیرنده آزمون‌های جامعیت مرتبط با پروژه می‌باشد. به عنوان مثال، آزمون‌های جامعیت متناظر با سرویس‌های کاربر و گروه‌کاربری نیز در این قالب تدارک دیده شده است.

              نکته: بدلیل اینکه مکانیزم اعتبارسنجی خودکار ورودی‌ها به عنوان یک Aspect برروی این سرویس‌ها اعمال شده است و بدین ترتیب در فرآیند تست سرویس‌ها نیز دخالت دارند، به صورت ناخواسته به سمت آزمون جامعیت سوق پیدا کرده‌ایم. با این حال اگر برای لایه بالاتر/خارجی پروژه خود یا همان API در قالب، قصد تهیه آزمون جامعیت داشته باشید، می‌توانید این تنظیمات ValidationInterceptor را از فایل ApplicationRegistry در پروژه Application حذف کرده و آزمون‌های سرویس‌ها را در قالب آزمون واحد انجام دهید. با این حال باید توجه کنید که برای برخی از سناریوها که امکانات هیچ کدام از مهیاکننده‌های InMemory و SQLite درون حافظه، جوابگوی نیاز شما نباشد، نیاز خواهید داشت تا از بانک اطلاعاتی واقعی از جمله LocalDb استفاده کنید؛ در این صورت آزمون‌های شما نیز در ردیف آزمون‌های جامعیت قرار خواهند داشت. 


              8- پروژه API دربرگیرنده کنترلرها، هاب‌های SignalR،  زیرساخت Authentication مبتنی‌بر JWT و سایر تنظیمات آغازین پروژه، می‌باشد. CRUD API متناظر با موجودیت‌های کاربر و گروه‌کاربری نیز در این قالب تدارک دیده شده است.


              کدهای کامل این قسمت را می‌توانید از اینجا دریافت کنید. 

              مطالب
              روش بهینه‌ی بررسی خالی بودن مجموعه‌ها و آرایه‌ها در NET 5.0.
              پیشتر مطلب «Count یا Any » را در این سایت مطالعه کرده‌اید که در پایان آن این نتیجه گیری صورت گرفته‌است:
              «از این پس حین استفاده از انواع و اقسام لیست‌ها، آرایه‌ها، IEnumerable‌ها و امثال آن‌ها، جهت بررسی خالی بودن یا نبودن آن‌ها تنها از متد Any فراهم شده توسط LINQ استفاده نمائید.»

              اکنون پس از سال‌ها، قصد داریم صحت این مساله را با NET 5.0. بررسی کنیم که آیا هنوز هم متد Any، بهترین متد بررسی خالی بودن مجموعه‌ها و آرایه‌های NET 5.0. است یا خیر؟


              نحوه‌ی بررسی کارآیی روش‌های مختلف خالی بودن مجموعه‌ها و آرایه‌ها در C# 9.0

              در ابتدا یک لیست، یک Enumerable و یک آرایه را به صورت زیر مقدار دهی می‌کنیم و هر سه‌ی این‌ها می‌توانند نال هم باشند:
              private IList<int>? _idsList;
              private IEnumerable<int>? _idsEnumerable;
              private int[]? _idsArray;
              
              [GlobalSetup]
              public void Setup()
              {
                  _idsEnumerable = Enumerable.Range(0, 10000);
                  _idsList = _idsEnumerable.ToList();
                  _idsArray = _idsEnumerable.ToArray();
              }

              اکنون که C# 9.0 در اختیار ما است به همراه pattern matching و همچنین Null Conditional Operator و غیره، می‌توان روش‌های زیر را برای بررسی خالی بودن این مجموعه‌ها و آرایه‌ها بکار گرفت:
              1- استفاده از Null coalescing برای بررسی نال بودن مجموعه و سپس استفاده از متد Any برای بررسی خالی بودن آن:
              var list = _idsList ?? new List<int>();
              if (list.Any() is false) { }

              2- استفاده از pattern matching برای بررسی نال بودن مجموعه و سپس استفاده از متد Any برای بررسی خالی بودن آن:
              if (_idsList is null || _idsList.Any() is false) { }

              3- استفاده از روش سنتی مقایسه‌ی مستقیم با null و سپس استفاده از متد Any برای بررسی خالی بودن آن:
              if (_idsList == null || _idsList.Any() is false) { }

              4- استفاده از Null Conditional Operator برای بررسی نال بودن و سپس استفاده از متد Any برای بررسی خالی بودن آن:
              if (_idsList?.Any() is false) { }

              5- استفاده از pattern matching برای بررسی مقدار خاصیت Count یک لیست یا آرایه. البته Enumerable‌ها به همراه این خاصیت نیستند و یا باید آن‌ها را به لیست و یا آرایه تبدیل کرد و یا می‌توان متد ()Count آن‌ها را فراخوانی نمود:
              if (_idsList is { Count: > 0 } is false) { }

              6- استفاده از Null Conditional Operator برای بررسی نال بودن و سپس استفاده از مقدار خاصیت Count لیست، برای بررسی خالی بودن آن:
              if (_idsList?.Count == 0) { }

              7- استفاده از روش سنتی مقایسه‌ی مستقیم با null و سپس استفاده از مقدار خاصیت Count لیست، برای بررسی خالی بودن آن:
              if (_idsList == null || _idsList.Count == 0) { }


              کدهای کامل این بررسی به صورت زیر هستند: AnyCountBenchmark.zip

              ابتدا ارجاعی به BenchmarkDotNet به برنامه اضافه شده‌است:
              <Project Sdk="Microsoft.NET.Sdk">
                <PropertyGroup>
                  <OutputType>Exe</OutputType>
                  <TargetFramework>net5.0</TargetFramework>
                  <Nullable>enable</Nullable>
                  <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
                </PropertyGroup>
                <ItemGroup>
                  <PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
                </ItemGroup>
              </Project>

              و سپس کدهای زیر، بررسی کارآیی روش‌های مختلف تعیین خالی بودن مجموعه‌ها را انجام می‌دهند:
              using BenchmarkDotNet.Running;
              
              namespace AnyCountBenchmark
              {
                  public static class Program
                  {
                      static void Main(string[] args)
                      {
              #if DEBUG
                          System.Console.WriteLine("Please set the project's configuration to Release mode first.");
              #else
                          BenchmarkRunner.Run<Scenarios>();
              #endif
                      }
                  }
              }

              به همراه سناریوهای مختلف زیر:
              using System;
              using System.Collections.Generic;
              using System.Linq;
              using BenchmarkDotNet.Attributes;
              using BenchmarkDotNet.Jobs;
              using BenchmarkDotNet.Order;
              
              namespace AnyCountBenchmark
              {
                  [SimpleJob(RuntimeMoniker.NetCoreApp50)]
                  [Orderer(SummaryOrderPolicy.FastestToSlowest, MethodOrderPolicy.Declared)]
                  [RankColumn]
                  public class Scenarios
                  {
                      private IList<int>? _idsList;
                      private IEnumerable<int>? _idsEnumerable;
                      private int[]? _idsArray;
              
                      [GlobalSetup]
                      public void Setup()
                      {
                          _idsEnumerable = Enumerable.Range(0, 10000);
                          _idsList = _idsEnumerable.ToList();
                          _idsArray = _idsEnumerable.ToArray();
                      }
              
                      #region Any_With_Null_coalescing
                      [Benchmark]
                      public void List_Any_With_Null_coalescing()
                      {
                          var list = _idsList ?? new List<int>();
                          if (list.Any() is false) { }
                      }
              
                      [Benchmark]
                      public void Array_Any_With_Null_coalescing()
                      {
                          var array = _idsArray ?? Array.Empty<int>();
                          if (array.Any() is false) { }
                      }
              
                      [Benchmark]
                      public void Enumerable_Any_With_Null_coalescing()
                      {
                          var enumerable = _idsEnumerable ?? Enumerable.Empty<int>();
                          if (enumerable.Any() is false) { }
                      }
                      #endregion
              
                      #region Any_With_Is_Null_Check
                      [Benchmark]
                      public void List_Any_With_Is_Null_Check()
                      {
                          if (_idsList is null || _idsList.Any() is false) { }
                      }
              
                      [Benchmark]
                      public void Array_Any_With_Is_Null_Check()
                      {
                          if (_idsArray is null || _idsArray.Any() is false) { }
                      }
              
                      [Benchmark]
                      public void Enumerable_Any_With_Is_Null_Check()
                      {
                          if (_idsEnumerable is null || _idsEnumerable.Any() is false) { }
                      }
                      #endregion
              
                      #region Any_Any_With_Null_Equality_Check
                      [Benchmark]
                      public void List_Any_With_Null_Equality_Check()
                      {
                          if (_idsList == null || _idsList.Any() is false) { }
                      }
              
                      [Benchmark]
                      public void Array_Any_With_Null_Equality_Check()
                      {
                          if (_idsArray == null || _idsArray.Any() is false) { }
                      }
              
                      [Benchmark]
                      public void Enumerable_Any_With_Null_Equality_Check()
                      {
                          if (_idsEnumerable == null || _idsEnumerable.Any() is false) { }
                      }
                      #endregion
              
                      #region Any_With_Null_Conditional_Operator
                      [Benchmark]
                      public void List_Any_With_Null_Conditional_Operator()
                      {
                          if (_idsList?.Any() is false) { }
                      }
              
                      [Benchmark]
                      public void Array_Any_With_Null_Conditional_Operator()
                      {
                          if (_idsArray?.Any() is false) { }
                      }
              
                      [Benchmark]
                      public void Enumerable_Any_With_Null_Conditional_Operator()
                      {
                          if (_idsEnumerable?.Any() is false) { }
                      }
                      #endregion
              
                      #region Count_With_Pattern_Matching
                      [Benchmark]
                      public void List_Count_With_Pattern_Matching()
                      {
                          if (_idsList is { Count: > 0 } is false) { }
                      }
              
                      [Benchmark]
                      public void Array_Length_With_Pattern_Matching()
                      {
                          if (_idsArray is { Length: > 0 } is false) { }
                      }
              
                      [Benchmark]
                      public void Enumerable_Count_With_Pattern_Matching()
                      {
                          var list = _idsEnumerable?.ToList();
                          if (list is { Count: > 0 } is false) { }
                      }
                      #endregion
              
                      #region Count_With_Null_Conditional_Operator
                      [Benchmark]
                      public void List_Count_With_Null_Conditional_Operator()
                      {
                          if (_idsList?.Count == 0) { }
                      }
              
                      [Benchmark]
                      public void Array_Length_With_Null_Conditional_Operator()
                      {
                          if (_idsArray?.Length == 0) { }
                      }
              
                      [Benchmark]
                      public void Enumerable_Count_With_Null_Conditional_Operator()
                      {
                          if (_idsEnumerable?.Count() == 0) { }
                      }
                      #endregion
              
                      #region Count_With_Null_Equality_Check
                      [Benchmark]
                      public void List_Count_With_Null_Equality_Check()
                      {
                          if (_idsList == null || _idsList.Count == 0) { }
                      }
              
                      [Benchmark]
                      public void Array_Length_With_Null_Equality_Check()
                      {
                          if (_idsArray == null || _idsArray.Length == 0) { }
                      }
              
                      [Benchmark]
                      public void Enumerable_Count_With_Null_Equality_Check()
                      {
                          if (_idsEnumerable == null || _idsEnumerable.Count() == 0) { }
                      }
                      #endregion
                  }
              }
              یکبار اجرای آن، نتیجه‌ی زیر را به همراه داشت:


              نتایج حاصل:

              - بررسی خالی بودن آرایه‌ها، بسیار سریعتر از بررسی خالی بودن لیست‌ها و این مورد نیز سریعتر از Enumerable‌ها است.
              - اگر از آرایه‌ها و یا لیست‌ها استفاده می‌کنید، بررسی خاصیت Length و یا خاصیت Count آن‌ها، بسیار سریعتر از بکارگیری متد Any بر روی آن‌ها است.
              - اگر از Enumerableها استفاده می‌کنید، استفاده از متد Any بر روی آن‌ها، بسیار سریعتر از بکارگیری متد ()Count و یا تبدیل آن‌ها به لیست و سپس بررسی خاصیت Count آن‌ها است.
              - بررسی نال بودن با pattern matching یا همان is null، نسبت به روشی سنتی استفاده‌ی از null ==، سریعتر است. علت آن‌را در مطلب «روش ترجیح داده شده‌ی مقایسه مقادیر اشیاء با null از زمان C# 7.0 به بعد» می‌توانید مطالعه کنید.

              بنابراین برای بررسی خالی بودن آرایه‌ها و لیست‌ها، بهتر است از خاصیت Length و یا Count آن‌ها استفاده کرد و برای Enumerableها از متد ()Any.
              مطالب
              برنامه نویسی پیشرفته JavaScript - قسمت 5 - معرفی برخی عملگرها

              معرفی برخی عملگرها

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


              عملگر typeof

              از آنجائیکه جاوا اسکریپت دارای نوع داده‌ای ضعیف یا Loosely Typed می‌باشد، باید در بکارگیری متغیرها و یا آرگومانهای ورودی توابع، دقت لازم را داشته باشیم تا خطایی در اجرای کد یا محاسبات به وجود نیاید. بنابراین به راهکارهایی نیاز داریم تا بتوانیم نوع داده‌ای یک متغیر را تشخیص دهیم و قبل از بکارگیری آنها صحت و اعتبار داده‌های ورودی را بررسی کنیم. با استفاده از عملگر typeof می‌توانیم نوع داده‌ای یک متغیر را تشخیص دهیم که برای هر نوع داده‌ای مقادیر زیر را بر میگرداند:

              · برای متغیرهایی که شامل مقدار undefined می‌باشند مقدار "undefined"

              · برای متغیرهای منطقی یا Boolean مقدار "boolean"

              · برای متغیرهای رشته‌ای یا String مقدار "string"

              · برای متغیرهای عددی و مقادیر NaN و Infinity مقدار "number"

              · برای تابع مقدار "function"

              · برای اشیا و مقادیر null مقدار "object"

              var x;
              var n = 12;
              var obj = {};
              var fn = function () { };
              var a = new Array();
              
              alert(typeof x);        // "undefined"
              alert(typeof n);        // "number"
              alert(typeof obj);     // "object"
              alert(typeof fn);       // "function"
              alert(typeof a);        // "object"

              عملگر instanceof

              عملگر typeof بهترین روش جهت تشخیص نوع داده‌ای متغیرهایی است که دارای نوع داده‌ای پایه یا Primitive Type هستند. اما جهت تشخیص نوع داده‌ای اشیاء و به صورت کلی انواع ارجاعی، این عملگر فقط مقدار "object" را برمیگرداند و اشاره‌ای به ماهیت واقعی آن Object ندارد. برای این منظور می‌توانیم از عملگر instanceof استفاده نماییم تا بررسی کنیم یک نوع ارجاعی از جنس چه نوع Object ی می‌باشد. شکل کلی استفاده از این عملگر به صورت زیر است:

              result = variable instanceof constructor

              اگر variable ، از جنس نوع ارجاعی تعیین شده در بخش سازنده یا constructor باشد، عملگر instanceof مقدار true را بر می‌گرداند. به مثال زیر توجه کنید:

              var a = new Array();
              alert(a instanceof Array); // true
              alert(a instanceof Object);   // true
              alert(a instanceof Date); // false
              توجه داشته باشید که اگر عملگر instanceof برای یک نوع ارجاعی به کار رود و با سازنده Object بررسی شود، همیشه مقدار true برمی گرداند.

              عملگر in

              همانطور که قبلا اشاره شد، جهت دسترسی به اعضای یک شیء، می‌توان با آن شیء همانند یک آرایه رفتار نمود. به عبارتی دیگر میتوان نام یک ویژگی یا تابع را در [] قرار داد تا به مقدار آن دسترسی داشت. بنابراین می‌توان همانند یک آرایه و با استفاده از یک حلقه‌ی for-in تمامی اعضای یک شیء را پیمایش نمود. در واقع عملگر in در این حلقه بررسی می‌کند چه ویژگی‌ها و توابعی در یک شیء وجود دارند و تمامی آنها را بر می‌گرداند. به مثال زیر توجه کنید:

              var person = {
                  name: "Meysam",
                  age: 33,
                  sayInfo: function () {
                      alert(name + ":" + age);
                  }
              };
              
              for (var i in person) 
                  alert(i + " => " + person[i]);

              خروجی :

                   name => Meysam
              
                  age => 33
              
                  sayInfo => function() {
                      alert(name + ":" + age);
                  }
              در مثال فوق، توسط حلقه‌ی for-in ، شیء person را پیمایش نمودیم. در این پیمایش، متغیر i ، به تک تک اعضای موجود در این شیء اشاره می‌کند. بنابراین متغیر i شامل نام ویژگی یا تابع می‌باشد و person[i] مقدار موجود در آن ویژگی یا محتوای تابع را بر میگرداند.

              کاربرد دیگر عملگر in بررسی وجود یک ویژگی یا تابع در یک شیء می‌باشد. اگر ویژگی یا تابع مورد نظر در شیء وجود داشته باشد، مقدار true را  بر می‌گرداند. به مثال زیر توجه کنید:

              alert("name" in person); // true
              alert("sayInfo" in person); // true
              alert("birth" in person); // false


              عملگر delete

              از عملگر delete جهت حذف یک ویژگی و یا یک تابع از یک شیء استفاده می‌شود. به مثال زیر توجه کنید:

              var person = {
                  name: "Meysam",
                  age: 33,
                  sayInfo: function () {
                      alert(name + ":" + age);
                  }
              };
              
              alert("sayInfo" in person); // true
              delete person.sayInfo;
              alert("sayInfo" in person); // false
              در مثال فوق پس از به کارگیری عملگر delete ، تابع sayInfo از شیء person حذف شده است. بنابراین در آخرین alert اعلام می‌کند که شیء person دیگر شامل این تابع نمی‌باشد.


              ویژگی constructor

              پس از عملگرهای فوق، یکی از پرکاربردترین ویژگی‌هایی که برای اشیاء وجود دارد، ویژگی constructor می‌باشد. در واقع این ویژگی نیز یکی از راهکارهای بررسی صحت و اعتبار متغیرها، آرگومانها و اشیا می‌باشد. ویژگی constructor ، به تابع سازنده‌ی یک شیء اشاره می‌کند و آن سازنده را به عنوان خروجی بر میگرداند. دقت داشته باشید که خروجی این ویژگی، خود تابع سازنده می‌باشد و یک مقدار رشته‌ای نیست. به مثال زیر توجه کنید:

              var obj = {};
              var a = new Array();
              var x = 10;
              
              alert(obj.constructor);
              alert(obj.constructor === Object);
              alert(typeof obj.constructor);
              alert(a.constructor);
              alert(x.constructor);

              خروجی :

                  function Object() { [native code] }
                  true
                  function
                  function Array() { [native code] }
                  function Number() { [native code] }
              همانطور که در مثال فوق مشاهده می‌نمایید، کدهای obj.constructor ، a.constructor و x.constructor تابع سازنده‌ی این اشیا را برگردانده است. در مقایسه obj.constructor===Object نیز مشاهده می‌کنید که خروجی این ویژگی یک شیء می‌باشد و در typeof obj.constructor هم نشان دادیم که نوع این ویژگی یک تابع است.

              در اینجا دیگر آماده‌ی ورود به برنامه نویسی شیء گرا در جاوا اسکریپت می‌باشیم که در مقالات بعدی به آن خواهیم پرداخت و همچنین با جزئیات بیشتری اشیاء را تشریح می‌نماییم.