مسیرراه‌ها
Entity framework code-first
شروع به کار با EF Code first

برای تکمیل بحث نیاز است تغییرات انجام شده از نگارش 4 به 6 را نیز مد نظر داشته باشید:


آشنایی با مباحث Migrations



آشنایی با تنظیمات نگاشت‌ها به دو روش استفاده از ویژگی‌ها و Fluent API



اعتبارسنجی و بررسی استثناءها



ردیابی تغییرات



استفاده از SQL خام و بانک‌های اطلاعاتی متفاوت

      نکات مهم کوئری نویسی در EF



      استفاده از EF در WPF


      لایه بندی پروژه‌های EF Code first



      پروژ‌ه‌های انجام شده با EF Code first

       
      نظرات مطالب
      ارتقاء به ASP.NET Core 1.0 - قسمت 15 - بررسی تغییرات Caching
      ممنون بررسی کردم به نظر میرسد مرورگر‌ها ( کروم و فایرفاکس تست شدند) در حالت reload کردن صفحه مجددا صفحات را درخواست میکنند و در این حالت کش را ندید میگیرند. طبق بررسی‌ها متدهای reload در جاوا اسکریپت نیز به همین شکل کش را ندید میگیرد. جهت تست و بررسی میتوانید لینک مورد نظر را در تب دیگری باز کرده تا نتیجه را مشاهده کنید.
      نکته دیگری که در جست و جو‌ها برخورد کردم این است که ممکن است مرورگر (بیشتر در رابطه با کروم بود) که کش را ممکن است روی حالت ssl نامعتبر نیز عمل نکند و ندید گرفته شود.
      نظرات مطالب
      EF Code First #12
      با سلام
      از پاسختون ممنون
      در برنامه ای که یکی از دوستان زحمت کشیده بود؛ علاوه بر اینترفیس هایی که به ازای هر کلاس تعریف کرده بودند؛ یک اینترفیس هم تعریف کرده بودند و تمام متدها رو در اون تعریف کرده بودند. همه اینترفیس‌ها از اون ارث بری کرده بودند و تمام متدها رو به اون منتقل کرده بودند. داخل خودشون هیچ متدی باقی نمونده بود.
      1- این روش مورد تائید شما میباشد؟
      2- تعریف اینترفیسی که در روش بالا عرض کردم به شکل زیر است:
      منظور از IDisposable  چیست؟
      3- در صورت تائید روش ذکر شده, چه لزومی به تعریف مابقی اینترفیس‌ها است در صورتی که همه EFClass‌ها میتوانند مستقیما از اون اینترفیس ارث بری کنند.
      public interface IGenericService<T>: IDisposable where
       T : class
      مطالب
      کامپوننت‌های متداول طرحبندی صفحات در بوت استرپ 4
      بوت استرپ، به همراه کامپوننت‌هایی برای پیاده سازی اعمال متداول طرحبندی صفحات است؛ مانند jumbotron ،media ،table و card.


      کامپوننت jumbotron

      از Jumbotron برای نمایش متنی مشخص در بالای یک صفحه، استفاده می‌شود. دو روش استفاده‌ی از آن در بوت استرپ 4 وجود دارند:
      - داخل container:
          <div class="container">
              <header class="jumbotron mt-4">
                  <div class="display-2 mb-4">Our Mission</div>
                  <p class="lead">Wisdom Pet Medicine strives to blend the best in
                      traditional and alternative medicine in the diagnosis and
                      treatment of companion animals including dogs, cats, birds,
                      reptiles, rodents, and fish. We apply the wisdom garnered in
                      the centuries old tradition of veterinary medicine, to find the
                      safest treatments and cures.</p>
              </header>
      با این خروجی:


      در اینجا با اعمال کلاس jumbotron، متن header، داخل یک قاب با گوشه‌های گرد قرار می‌گیرد و مشخص‌تر نمایش داده خواهد شد. همچنین با mt-4، فاصله‌ای را بین آن و بالای صفحه ایجاد کرده‌ایم.

      - خارج از container:
          <header class="jumbotron jumbotron-fluid">
              <div class="container">
                  <div class="display-2 mb-4">Our Mission</div>
                  <p class="lead">Wisdom Pet Medicine strives to blend the best in
                      traditional and alternative medicine in the diagnosis and
                      treatment of companion animals including dogs, cats, birds,
                      reptiles, rodents, and fish. We apply the wisdom garnered in
                      the centuries old tradition of veterinary medicine, to find the
                      safest treatments and cures.</p>
              </div>
          </header>
      با این خروجی:


      اگر می‌خواهیم این قاب، تمام عرض صفحه را پر کند و همچمنین لبه‌های گرد آن نیز حذف شوند، می‌توان از کلاس jumbotron-fluid استفاده کرد و آن‌را خارج از container قرار داد. سپس برای اینکه متن داخل آن با container زیر آن تراز شود، می‌توان یک container را در اینجا داخل jumbotron تعریف کرد.


      کنترل ظاهر جداول، در بوت استرپ 4

      بوت استرپ 4 به همراه تعدادی کلاس ویژه است که برای بهبود ظاهر المان استاندارد جدول، ارائه شده‌اند. آن‌ها را در طی مثال‌هایی بررسی خواهیم کرد.


      برای رسیدن به چنین تصویری، تغییرات زیر را بر روی یک جدول استاندارد HTML اعمال کرده‌ایم:
      <table class="table table-striped table-hover table-bordered table-responsive">
        <thead class="thead-light">
      - کلاس table، کلاس پایه اعمال شیوه‌نامه‌های بوت استرپ 4 به المان جدول است که سبب خواهد شد آیتم‌های آن با فاصله‌ی بهتری نسبت به یکدیگر ظاهر شوند. با استفاده از کلاس table-dark می‌توان یک قالب مشکی را به جدول اعمال کرد.
      - کلاس table-striped سبب می‌شود تا ردیف‌ها، یک در میان با رنگی متمایز نمایش داده شوند.
      - با افزودن table-hover، رنگ ردیف‌های جدول با عبور اشاره‌گر ماوس از روی آن‌ها تغییر می‌کند.
      - کلاس table-bordered کار نمایش قاب جدول را انجام می‌دهد.
      - کلاس table-responsive سبب می‌شود تا در اندازه‌های کوچک صفحه، یک اسکرول بار افقی برای نمایش آیتم‌های جدول ظاهر شود و یا می‌توان از کلاس table-sm نیز استفاده کرد تا padding تعریف شده‌ی در جدول، کاهش یابند. این کلاس، قابلیت پذیرش break-pointها را نیز دارد؛ مانند table-responsive-md.
      - کلاس‌های thead-light و یا thead-dark که بر روی تگ thead قرار می‌گیرند، رنگ پس زمینه‌ی هدر جدول را مشخص می‌کنند.
      - برای تغییر رنگ پس زمینه و متن یک ردیف می‌توان از کلاس‌های bg-color و text-color استفاده کرد:
      <tr class="bg-danger text-light">
      - برای تغییر رنگ سلول‌های جدول از کلاس‌های table-color استفاده می‌کنیم:
      <td class="table-success">$100.00 </td>
      فرمول‌های رنگ‌های قابل اعمال به ردیف‌ها، سلول‌ها و متون جداول بوت استرپ 4 را در تصویر ذیل مشاهده می‌کنید:



      کامپوننت جدید card در بوت استرپ 4

      پنل‌های بوت استرپ 3 حذف و بجای آن کامپوننت جدیدی به نام card در نگارش 4 آن ارائه شده‌است که با افزودن کلاس آن به یک div، بلافاصله قابی با گوشه‌های گرد به آن اضافه می‌شود.


              <section class="card mb-5" id="drwinthrop">
                  <div class="card-body">
                      <img class="card-img img-fluid" src="images/testimonial-mcphersons.jpg"
                          alt="Doctor Winthrop Photo">
                      <h2 class="card-title">Dr. Stanley Winthrop</h2>
                      <h5 class="card-subtitle">Behaviorist</h5>
                      <p class="card-text">Dr. Winthrop is the guardian of Missy, a
                          three-year old Llaso mix, who he adopted at the shelter.
                          Dr. Winthrop is passionate about spay and neuter and pet
                          adoption, and works tireless hours outside the clinic,
                          performing free spay and neuter surgeries for the shelter.</p>
                      <a class="card-link" href="#">About Me</a>
                      <a class="card-link" href="#">My Pets</a>
                      <a class="card-link" href="#">Client Slideshow</a>
                  </div>
              </section>
      - برای اینکه عناصر داخل card با فاصله‌ی مناسبی از لبه‌های آن قرار گیرند و همچنین شیوه‌نامه‌های قسمت‌های مختلف آن به درستی اعمال شوند، نیاز است محتوای section ای که با کلاس card مشخص شده (تعیین container)، داخل یک div با کلاس card-body قرار گیرد. در اینجا امکان تعریف card-header و card-footer نیز وجود دارد.
      - سپس یک card می‌تواند دارای تصویری واکنشگرا باشد که عرض card را پوشش می‌دهد. این تصویر با کلاس card-img مشخص می‌شود.
      در اینجا امکان تعریف card-img-top و card-img-bottom نیز وجود دارند. این موارد تصویر card را در بالا و یا پایین آن، بدون padding، نمایش می‌دهند. اگر می‌خواهید متنی را بر روی این تصویر نمایش دهید، از کلاس card-img-overlay استفاده کنید. در این حالت‌ها باید تصویر را خارج از card-body قرار دهید.
      - عنوان و زیرعنوان یک card، توسط کلاس‌های card-title و card-subtitle تعیین می‌شوند.
      - متن داخل آن‌را با کلاس card-text مشخص می‌کنیم.
      - لینک‌های ذیل آن نیز توسط کلاس card-link در طی یک ردیف نمایش داده می‌شوند.

      امکان تعیین رنگ پس زمینه، حاشیه و متن یک card نیز وجود دارند:
      <section class="card mb-5 bg-primary text-light border-warning" id="drchase">
      با این خروجی:


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


      می‌توان برای یک card، هدر و فوتر نیز تعریف کرد:
              <section class="card mb-5" id="drsanders">
                  <div class="card-header">
                      <h2 class="card-title">Dr. Kenneth Sanders</h2>
                      <h5 class="card-subtitle">Nutritionist</h5>
                  </div>
                  <div class="card-body">
                      <img class="card-img img-fluid" src="images/testimonial-mcphersons.jpg"
                          alt="Doctor Sanders Photo">
                      <p class="card-text">Leroy walked into Dr. Sanders front door
                          when she was moving into a new house. After searching for
                          weeks for Leroy's guardians, she decided to make Leroy a
                          part of her pet family, and now has three cats.</p>
                  </div>
                  <div class="card-footer">
                      <a class="card-link" href="#">About Me</a>
                      <a class="card-link" href="#">My Pets</a>
                      <a class="card-link" href="#">Client Slideshow</a>
                  </div>
              </section>
      در اینجا همان card قبلی را مشاهده می‌کنید که عناوین آن به card-header و لینک‌های ذیل آن به card-footer منتقل شده‌اند:


      برای تعریف یک list-group در داخل یک card، به صورت زیر عمل می‌کنیم:
              <section class="card mb-5" id="drwong">
                  <div class="card-body">
                      <img class="card-img img-fluid" src="images/testimonial-mcphersons.jpg"
                          alt="Doctor Wong Photo">
                      <h2 class="card-title">Dr. Olivia Wong</h2>
                      <h5 class="card-subtitle">Preventive Care</h5>
                      <p class="card-text">Dr. Wong is a cancer survivor who was
                          fortunate enough to get to spend time with a therapy dog
                          during her recovery. She became passionate about therapy
                          animals, and has started her own foundation to train and
                          provide education to patients in recovery. Now she gets her
                          own dose of daily therapy from her husky, Lilla.</p>
                  </div>
                  <div class="list-group list-group-flush">
                      <a class="list-group-item" href="#">About Me</a>
                      <a class="list-group-item" href="#">My Pets</a>
                      <a class="list-group-item" href="#">
                          Client Slideshow
                      </a>
                  </div>
              </section>
      ابتدا list-group را به خارج از card-body منتقل می‌کنیم. سپس برای حذف حاشیه‌ی آن و همچنین گوشه‌های گرد آن، جهت یکی شدن با قاب card، کلاس list-group-flush را به آن اضافه می‌کنیم:



      تعیین نحوه‌ی چیدمان cards در بوت استرپ 4

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


      اگر از کلاس card-columns استفاده کنیم، تمام cards را به صورت خودکار در ستون‌ها و ردیف‌ها، قرار می‌دهد که بعضی از آن‌ها بلندتر و بعضی دیگر کوتاه‌تر هستند (نوعی نمایش کاشی‌کاری شده‌است):


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


      المان media در بوت استرپ 4

      برای نمایش متداول متن و تصویر که قرار است تصویر، در یک ستون و متن، در ستونی دیگر باشد، بوت استرپ 4 به همراه کلاس media است که بر اساس Flexbox بازنویسی شده‌است.
      <body>
          <div class="container">
              <section class="media mb-5" id="drwinthrop">
                  <img class="d-flex align-self-center img-fluid rounded mr-3" style="width:30%"
                      src="images/testimonial-mcphersons.jpg" alt="Doctor Winthrop Photo">
                  <div class="media-body">
                      <h2>Dr. Stanley Winthrop</h2>
                      <h5>Behaviorist</h5>
                      <p>Dr. Winthrop is the guardian of Missy,
                          a three-year old Llaso mix, who he adopted at the
                          shelter. Dr. Winthrop is passionate about spay and neuter
                          and pet adoption, and works tireless hours outside the
                          clinic, performing free spay and neuter surgeries for the
                          shelter.</p>
              </section>
          </div>
      </body>
      با این خروجی:


      ابتدا توسط کلاس media یک container را تعریف می‌کنیم. سپس تصویر، یک ستون و media-body ستون دیگر را تشکیل می‌دهد.
      با استفاده از d-flex، المان تصویر را به یک Flexbox container تبدیل کرده و با استفاده از کلاس align-self-center، آن‌را در میانه‌ی ستون قرار می‌دهیم. همچنین در اینجا توسط mr-3، فاصله‌ی آن‌را با متن ستون کناری تنظیم کرده‌ایم.


      کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: Bootstrap4_09.zip
      مطالب
      فشرده سازی اطلاعات در SQL server 2008

      علاوه بر فشرده سازی خودکار بک آپ‌ها که پیشتر در مورد آن‌ها صحبت شد، اس کیوال سرور 2008 دو نوع فشرده سازی دیگر را نیز پشتیبانی می‌کند:

      Row Compression :

      حالت row compression نحوه‌ی ذخیره سازی فیزیکی داده‌ها را تغییر می‌دهد. فعال سازی آن اثرات زیر را خواهد داشت:
      الف) متادیتای هر رکورد را حداقل می‌کند (منظور از متادیتا اطلاعاتی مانند اطلاعات ستون‌ها، طول و آفست و غیره است)
      ب) داده‌های عددی و رشته‌هایی با طول ثابت، به صورت اطلاعاتی با طول متغیر ذخیره خواهند شد، درست مانند varchar ها.

      برای ایجاد جدولی که row compression در آن به صورت پیش‌فرض فعال است، می‌توان مانند مثال زیر عمل کرد:
      CREATE TABLE MyTable
      (
      ID int identity Primary key,
      Name char(100),
      Email char(100)
      )
      WITH (DATA_COMPRESSION = Row);

      GO
      و اگر جدول موجودی را می‌خواهید تغییر داده و این خاصیت را بر روی آن فعال نمائید، روش زیر را اعمال کنید:
      Alter TABLE MyTable REBUILD WITH (DATA_COMPRESSION=Row, MAXDOP=2);
      در اینجا MAXDOP مشخص می‌کند که از چند CPU باید برای فشرده سازی استفاده شود. (اگر جدولی حجیم دارید، به این صورت می‌توان عملیات فشرده سازی را سریعتر به پایان رساند)


      Page Compression :

      در روش دوم فشرده سازی اطلاعات در اس‌کیوال سرور 2008 ، که مهم‌ترین حالت موجود نیز می‌باشد، اطلاعات مشترک، بین سطرهای یک صفحه به اشتراک گذاشته می‌شوند. این روش از فناوری‌های زیر استفاده می‌کند:
      الف) روش row compression که در مورد آن صحبت شد جزئی از این روش است.
      ب) Prefix Compression : به ازای هر ستون در یک صفحه، Prefix های تکراری یافت شده و در هدر مخصوص فشرده سازی ذخیره می‌شوند (محل این هدر پس از هدر صفحه است). سپس هرجایی که به این Prefix ها اشاره شده‌باشد، عدد منحصربفرد شناسایی کننده آن‌ها نسبت داده می‌شود.
      ج) Dictionary Compression : در این حالت مقادیر تکراری یک صفحه جستجو شده و در هدر فشرده سازی صفحه ذخیره می‌شوند. حالت Prefix Compression فقط به یک ستون منحصر می‌شود اما Dictionary Compression به کل صفحه اعمال می‌گردد.

      برای فعال سازی آن در یک جدول جدید به روش زیر می‌توان عمل نمود:
      CREATE TABLE MyTable
      (
      ID int identity Primary key,
      Name char(100),
      Email char(100)
      )
      WITH (DATA_COMPRESSION = Page);
      و برای اعمال آن به جدولی موجود از روش زیر می‌توان استفاده کرد:

      Alter TABLE MyTable REBUILD WITH (DATA_COMPRESSION=Page, MAXDOP=2);
      یک سری رویه‌های ذخیره شده سیستمی جدید نیز برای محاسبه حجم جداول، پیش و پس از فشرده سازی (بدون فشرده سازی واقعی) نیز در این نگارش گنجانده شده‌اند که به شرح زیر هستند:
      -- بررسی اینکه چه میزان فضا با اعمال فشرده سازی صفحات قابل صرفه جویی خواهد بود
      EXEC sp_estimate_data_compression_savings 'schemaname', 'TableName', NULL, NULL, 'PAGE';

      -- بررسی اینکه چه میزان فضا با اعمال فشرده سازی ردیف‌ها قابل صرفه جویی خواهد بود
      EXEC sp_estimate_data_compression_savings 'schemaname', 'TableName', NULL, NULL, 'ROW';

      بنابراین قبل از اینکه فشرده سازی را فعال نمائید، ابتدا بررسی کنید آیا واقعا میزان قابل توجهی اطلاعات فشرده خواهند شد و نتیجه حاصل رضایت بخش است یا خیر. همچنین باید درنظر داشت که جداول و یا ایندکس‌هایی که read و write بالایی دارند برای این منظور مناسب نیستند. برای یافتن آن‌ها کوئری زیر را اجرا کنید:

      USE dbName;
      SELECT objectname = OBJECT_NAME(s.object_id),
      indexname = i.name,
      i.index_id,
      reads = range_scan_count + singleton_lookup_count,
      'leaf_writes' = leaf_insert_count + leaf_update_count + leaf_delete_count,
      'leaf_page_splits' = leaf_allocation_count,
      'nonleaf_writes' = nonleaf_insert_count + nonleaf_update_count +
      nonleaf_delete_count,
      'nonleaf_page_splits' = nonleaf_allocation_count
      FROM sys.dm_db_index_operational_stats (DB_ID(), NULL, NULL, NULL) AS s
      INNER JOIN sys.indexes AS i
      ON i.object_id = s.object_id
      WHERE OBJECTPROPERTY(s.object_id, 'IsUserTable') = 1
      AND i.index_id = s.index_id
      ORDER BY
      leaf_writes DESC,
      nonleaf_writes DESC

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



      مطالب
      شروع به کار با DNTFrameworkCore - قسمت 1 - معرفی و نحوه استفاده از آن

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


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

      • ارائه ساختارهای مشترک بین پروژه‌های مختلف از جمله Cross-Cutting Concern‌ها و ...
      • دنبال کردن اصل DRY به منظور متمرکز شدن صرف برروی منطق تجاری سیستم نه انجام و حل یکسری مسائل تکراری
      • کاهش زمان توسعه و اختصاص زمان بیشتر برای نوشتن آزمون‌های واحد منطق تجاری
      • کاهش باگ و جلوگیری از پخش شدن باگ‌های پیاده سازی در سراسر سیستم
      • کاهش زمان آموزش نیروهای جدید برای ملحق شدن به تیم تولید شما با حداقل دانش طراحی و برنامه نویسی شیء گرا
      • ارائه راهکاری یکپارچه برای توسعه پذیر بودن منطق تجاری پیاده سازی شده از طریق در معرض دید قرار دادن یکسری «Extensibility Point» با استفاده از رویکرد Event-Driven


      امکانات این زیرساخت در زمان نگارش مطلب جاری


      نحوه استفاده از بسته‌های نیوگت مرتبط

      PM> Install-Package DNTFrameworkCore -Version 1.0.0
       services.AddDNTFramework()
           .AddDataAnnotationValidation()
           .AddModelValidation()
           .AddValidationOptions(options =>
           {
               /*options.IgnoredTypes.Add(typeof());*/
           })
           .AddMemoryCache()
           .AddAuditingOptions(options =>
           {
               // options.Enabled = true;
               // options.EnabledForAnonymousUsers = false;
               // options.IgnoredTypes.Add(typeof());
               // options.Selectors.Add(new NamedTypeSelector("SelectorName", type => type == typeof()));
           }).AddTransactionOptions(options =>
           {
               // options.Timeout=TimeSpan.FromMinutes(3);
               //options.IsolationLevel=IsolationLevel.ReadCommitted;
           });

      متدهای الحاقی بالا برای ثبت سرویس‌ها و تنظیمات مرتبط با مکانیزم‌های اعتبارسنجی خودکار، مدیریت تراکنش‌ها، لاگ آماری، Eventing و سایر امکانات ذکر شده، در IoC Container  توکار ASP.NET Core استفاده خواهند شد.

      PM> Install-Package DNTFrameworkCore.EntityFramework -Version 1.0.0
      services.AddDNTUnitOfWork<ProjectDbContext>();

      ‎بسته نیوگت بالا شامل پیاده سازی مبتنی بر EF Core برای واسط‌های تعریف شده در بسته نیوگت DNTFrameworkCore، می‌باشد؛ از جمله آن می‌توان به CrudService پایه اشاره کرد.  متد الحاقی AddDNTUnitOfWork برای ثبت و معرفی واسط‌های IUnitOfWork و ITransactionProvider به عنوان مهیا کننده تراکنش به همراه پیاده سازهای آنها و همچنین ثبت یک سری Hook تعریف شده برای ردیابی تغییرات، در سیستم تزریق وابستگی، استفاده خواهد شد.

      همچنین با نصب بسته بالا، امکان استفاده از مهیا کننده Logging با امکان ذخیره سازی در بانک اطلاعاتی را خواهید داشت:

       public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
                  WebHost.CreateDefaultBuilder(args)
                      .UseDefaultServiceProvider((context, options) =>
                      {
                          options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
                      })
                      .ConfigureLogging((hostingContext, logging) =>
                      {
                          logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                          logging.AddConsole();
                          logging.AddDebug();
                          logging.AddEntityFramework<ProjectDbContext>(options => options.MinLevel = LogLevel.Warning);
                      })
                      .UseStartup<Startup>();

      متد جنریک الحاقی AddEntityFramework برای ثبت مهیا کننده مذکور استفاده می‌شود. 

      PM> Install-Package DNTFrameworkCore.Web -Version 1.0.0

      بسته نیوگت بالا شامل یکسری سرویس برای اعمال دسترسی‌های پویا، CrudController مبتنی بر ASP.NET Core Web API، فیلتر مدیریت سراسری خطاهای برنامه و سایر امکاناتی که در ادامه مقالات با جزئیات بیشتری بررسی خواهیم کرد، می‌باشد. برای ثبت سرویس‌های تعریف شده می‌توانید از متد الحاقی AddDNTCommonWeb و به منظور تغییر محل ذخیره سازی کلیدهای موقت رمزنگاری مرتبط با Data Protection API و انتقال آنها به بانک اطلاعاتی، استفاده کنید. 

      services.AddDNTCommonWeb()
          .AddDNTDataProtection();

      نکته: برای انتقال کلیدهای موقت رمزنگاری به بانک اطلاعاتی، نیاز است تا از متد الحاقی زیر که در بسته نیوگت DNTFrameworkCore.EntityFramework موجود می‌باشد، به شکل زیر استفاده کنید:

      services.AddDNTProtectionRepository<ProjectDbContext>();

      ‎‎

      اگر نیاز به شماره گذاری خودکار دارید، بسته زیر را نیز می‌بایست نصب کنید:
      PM> Install-Package DNTFrameworkCore.EntityFramework.SqlServer -Version 1.0.0

      بسته بالا از امکانات مخصوص SqlServer برای اعمال قفل منطقی برای مدیریت مباحث همزمانی استفاده می‌کند؛ همچنین PreUpdateHook مرتبط با تولید خودکار کد منحصر به فرد، در این کتابخانه پیاده سازی شده است. به شکل زیر می‌توانید سرویس‌های مرتبط با آن را به سیستم تزریق وابستگی‌های معرفی کنید:

      services.AddDNTNumbering(options =>
      {
          options.NumberedEntityMap[typeof(Task)] = new NumberedEntityOption
          {
              Start = 100,
              Prefix = "Task-",
              IncrementBy = 5
          };
      });

      ‎‎به عنوان مثال برای شماره گذاری موجودیت Task، لازم است تنظیمات مرتبط آن را به شکل بالا به سیستم شماره گذاری معرفی کنید.

      اگر قصد استفاده از کتابخانه FluentValidation را داشته باشید، می‌بایست بسته زیر را نیز نصب کنید:

      PM> Install-Package DNTFrameworkCore.FluentValidation -Version 1.0.0  

      برای ثبت و معرفی Adapter مرتبط، به سیستم اعتبارسنجی خودکار معرفی شده، لازم است از طریق متد الحاقی AddFluentModelValidation به شکل زیر اقدام کنید:

       services.AddDNTFramework()
           .AddDataAnnotationValidation()
           .AddModelValidation()
           .AddFluentModelValidation()
           .AddValidationOptions(options =>
           {
               /*options.IgnoredTypes.Add(typeof());*/
           })
           .AddMemoryCache()
           .AddAuditingOptions(options =>
           {
               // options.Enabled = true;
               // options.EnabledForAnonymousUsers = false;
               // options.IgnoredTypes.Add(typeof());
               // options.Selectors.Add(new NamedTypeSelector("SelectorName", type => type == typeof()));
           }).AddTransactionOptions(options =>
           {
               // options.Timeout=TimeSpan.FromMinutes(3);
               //options.IsolationLevel=IsolationLevel.ReadCommitted;
           });

      ‎‎

      برای شروع پروژه جدید، نصب این بسته‌ها کفایت می‌کند. اگر نیاز به طراحی MultiTenancy دارید، بسته زیر را برای شناسایی Tenant جاری و از این قبیل کارها نیز می‌بایست نصب کنید:

      PM> Install-Package DNTFrameworkCore.Web.MultiTenancy -Version 1.0.0
      برای ثبت و معرفی ITenantResolver شخصی سازی شده خود، می‌توانید از متد الحاقی زیر استفاده کنید:
      services.AddMultiTenancy<TenantResolver>();
      همچنین نیاز است با استفاده از متد الحاقی زیر، Middleware تعریف شده در کتابخانه را به «HTTP Request Pipeline» سیستم معرفی کرده و ثبت کنید:
      app.UseMultiTenancy();
      نکته: بسته نیوگت DNTFrameworkCore.Web.MultitTenancy به منظور مهیا کردن طول عمر Tenant-Singleton، وابستگی به کتابخانه StructureMap نیز دارد. البته این بسته نیاز به بهبود هم دارد که در ادامه اعمال خواهد شد. همچنین امضای متدهای الحاقی بالا، در انتشارهای بعدی به AddDNTMultiTenancy و UseDNTMultiTenancy تغییر خواهند کرد.
      در نهایت به منظور تزئین خودکار و پویای سرویس‌های برنامه برای اعمال یکسری Cross-Cutting Concern معرفی شده در بالا، از جمله اعتبارسنجی ورودی‌ها، مدیریت تراکنش‌ها و ... می‌توانید پس از ثبت و معرفی سرویس‌های خود به سیستم تزریق وابستگی توکار، با استفاده از Interceptor‌های پیاده سازی شده در زیرساخت، به شکل زیر اقدام کنید:
      services.Scan(scan => scan
          .FromCallingAssembly()
          .AddClasses(classes => classes.AssignableTo<ISingletonDependency>())
          .AsMatchingInterface()
          .WithSingletonLifetime()
          .AddClasses(classes => classes.AssignableTo<IScopedDependency>())
          .AsMatchingInterface()
          .WithScopedLifetime()
          .AddClasses(classes => classes.AssignableTo<ITransientDependency>())
          .AsMatchingInterface()
          .WithTransientLifetime()
          .AddClasses(classes => classes.AssignableTo(typeof(IDomainEventHandler<>)))
          .AsImplementedInterfaces()
          .WithTransientLifetime());
      
      foreach (var descriptor in services.Where(s => typeof(IApplicationService).IsAssignableFrom(s.ServiceType))
          .ToList())
      {
          services.Decorate(descriptor.ServiceType, (target, serviceProvider) =>
              ProxyGenerator.CreateInterfaceProxyWithTargetInterface(
                  descriptor.ServiceType,
                  target, serviceProvider.GetRequiredService<ValidationInterceptor>(),
                  (IInterceptor) serviceProvider.GetRequiredService<TransactionInterceptor>()));
      }
      به عنوان مثال در اینجا از ValidationInterceptor و TransactionInterceptor استفاده شده است.

      پروژه نمونه‌ای هم برای نمایش امکانات زیرساخت را از اینجا می توانید دریافت کنید.
      آزمون‌های واحد مرتبط با قسمت هایی از این زیرساخت را نیز می‌توانید در اینجا مشاهده کنید.
      مطالب
      آشنایی با قابلیت FileStream اس کیوال سرور 2008 - قسمت سوم

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

      alter table tbl_files set(filestream_on ='default')

      go
      alter table tbl_files
      add

      [systemfile] varbinary(max) filestream null ,
      FileId uniqueidentifier not null rowguidcol unique default (newid())
      go

      در ادامه جدول tblFiles قسمت قبل را در نظر بگیرید:

      CREATE TABLE [tblFiles](
      [FileId] [uniqueidentifier] ROWGUIDCOL NOT NULL,
      [Title] [nvarchar](255) NOT NULL,
      [SystemFile] [varbinary](max) FILESTREAM NULL,
      UNIQUE NONCLUSTERED
      (
      [FileId] ASC
      )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
      ) ON [PRIMARY] FILESTREAM_ON [fsg1]

      ALTER TABLE [dbo].[tblFiles] ADD DEFAULT (newid()) FOR [FileId]
      GO

      نحوه‌ی افزودن رکوردی جدید به جدول tblFiles :

      INSERT INTO [tblFiles]
      (
      [Title],
      [SystemFile]
      )
      VALUES
      (
      'file-1',
      CAST('data data data' AS VARBINARY(MAX))
      )
      در اینجا سعی کرده‌ایم یک رشته ساده را در فیلدی از نوع فایل استریم ذخیره کنیم که روش کار به صورت فوق است. از آنجائیکه مقدار پیش فرض FileId را هنگام تعریف جدول به NEWID تنظیم کرده‌ایم، نیازی به ذکر آن نیست و به صورت خودکار محاسبه و ذخیره خواهد شد.
      اگر کنجکاو باشید که این فایل اکنون کجا ذخیره شده و نحوه‌ی مدیریت آن توسط اس کیوال سرور به چه صورتی است، فقط کافی است به مسیری که هنگام افزودن گروه فایل‌ها و فایل مربوطه در تنظیمات خواص دیتابیس در قسمت قبل مشخص کردیم، مراجعه کرد (شکل زیر).



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

      using System;
      using System.IO;
      using System.Data.SqlClient;
      using System.Data;

      namespace FileStreamTest
      {
      class CFS
      {
      /// <summary>
      /// افزودن رکورد به جدول حاوی ستونی از نوع فایل استریم
      /// </summary>
      /// <param name="filePath">مسیر فایل</param>
      /// <param name="title">عنوانی دلخواه</param>
      public static void AddNewRecord(string filePath, string title)
      {
      //آیا فایل وجود دارد؟
      if (!File.Exists(filePath))
      throw new FileNotFoundException(
      "لطفا مسیر فایل معتبری را مشخص نمائید", filePath);

      //خواندن اطلاعات فایل در آرایه‌ای از بایت‌ها
      byte[] buffer = File.ReadAllBytes(filePath);

      using (SqlConnection objSqlCon = new SqlConnection())
      {
      //todo: کانکشن استرینگ باید از یک فایل کانفیگ خوانده شود
      objSqlCon.ConnectionString =
      "Data Source=(local);Initial Catalog=testdb2009;Integrated Security = true";
      objSqlCon.Open();

      //شروع یک تراکنش
      using (SqlTransaction objSqlTran = objSqlCon.BeginTransaction())
      {
      //ساخت عبارت افزودن پارامتری
      using (SqlCommand objSqlCmd = new SqlCommand(
      "INSERT INTO [tblFiles]([Title],[SystemFile]) VALUES(@title , @file)",
      objSqlCon, objSqlTran))
      {
      objSqlCmd.CommandType = CommandType.Text;

      //تعریف وضعیت پارامترها و مقدار دهی آن‌ها
      objSqlCmd.Parameters.AddWithValue("@title", title);
      objSqlCmd.Parameters.AddWithValue("@file", buffer);

      //اجرای فرامین
      objSqlCmd.ExecuteNonQuery();
      }

      //پایان تراکنش
      objSqlTran.Commit();
      }
      }
      }

      /// <summary>
      /// دریافت اطلاعات فایل ذخیره شده به صورت آرایه‌ای از بایت‌ها
      /// </summary>
      /// <param name="fileId">کلید مورد استفاده</param>
      /// <returns></returns>
      public static byte[] GetDataFromDb(string fileId)
      {
      byte[] data = null;

      using (SqlConnection objConn = new SqlConnection())
      {
      //کوئری اس کیوال پارامتری جهت دریافت محتویات فایل
      string cmdText = "SELECT SystemFile FROM tblFiles WHERE FileId=@id";
      using (SqlCommand objCmd = new SqlCommand(cmdText, objConn))
      {
      //todo: کانکشن استرینگ باید از یک فایل کانفیگ خوانده شود
      objConn.ConnectionString =
      "Data Source=(local);Initial Catalog=testdb2009;Integrated Security = true";
      objConn.Open();

      //تنظیم کردن وضعیت و مقدار پارامتر تعریف شده در کوئری
      objCmd.Parameters.AddWithValue("@id", fileId);

      //اجرای فرامین و دریافت فایل
      using (SqlDataReader objread = objCmd.ExecuteReader())
      {
      if (objread != null)
      if (objread.Read())
      {
      if (objread["SystemFile"] != DBNull.Value)
      data = (byte[])objread["SystemFile"];
      }
      }
      }
      }

      return data;
      }
      }
      }

      مثالی در مورد روش استفاده از کلاس فوق :

      using System.IO;

      namespace FileStreamTest
      {
      class Program
      {
      static void Main(string[] args)
      {
      CFS.AddNewRecord(@"C:\filest05.PNG", "test1");

      //آی دی رکورد ذخیره شده در دیتابیس برای مثال
      byte[] data = CFS.GetDataFromDb("BB848D45-382C-4D95-BF4E-52C3509407D4");
      if (data != null)
      {
      File.WriteAllBytes(@"C:\tst.PNG", data);
      }
      }
      }
      }
      روش فوق با روش متداول افزودن یک فایل به دیتابیس اس کیوال سرور هیچ تفاوتی ندارد و این‌جا هم بدون مشکل کار می‌کند. اطلاعات نهایی به صورت فایل‌هایی بر روی سیستم که توسط اس کیوال سرور مدیریت خواهند شد و با جدول شما یکپارچه‌اند، ذخیره می‌شوند.

      در روش دیگری که در اکثر مقالات مرتبط مورد استفاده است، از شیء SqlFileStream کمک گرفته شده و نحوه‌ی انجام آن نیز به صورت زیر می‌باشد.
      در ابتدا دو رویه ذخیره شده زیر را ایجاد می‌کنیم:

      CREATE PROCEDURE [AddFile](@Title NVARCHAR(255), @filepath VARCHAR(MAX) OUTPUT)
      AS
      BEGIN
      SET NOCOUNT ON;

      DECLARE @ID UNIQUEIDENTIFIER
      SET @ID = NEWID()

      INSERT INTO [tblFiles]
      (
      [FileId],
      [title],
      [SystemFile]
      )
      VALUES
      (
      @ID,
      @Title,
      CAST('' AS VARBINARY(MAX))
      )

      SELECT @filepath = SystemFile.PathName()
      FROM tblFiles
      WHERE FileId = @ID
      END
      GO

      CREATE PROCEDURE [GetFilePath](@Id VARCHAR(50))
      AS
      BEGIN
      SET NOCOUNT ON;

      SELECT SystemFile.PathName()
      FROM tblFiles
      WHERE FileId = @ID
      END
      در رویه ذخیره شده AddFile ، ابتدا رکوردی بر اساس عنوان دلخواه ورودی با یک فایل خالی ایجاد می‌شود. سپس مسیر سیستمی این فایل را در آرگومان خروجی filepath قرار می‌دهیم. SystemFile.PathName از اس کیوال سرور 2008 جهت فیلدهای فایل استریم به اس کیوال سرور اضافه شده است. از این مسیر در برنامه خود جهت نوشتن بایت‌های فایل مورد نظر در آن توسط شیء SqlFileStream استفاده خواهیم کرد.
      رویه ذخیره شده GetFilePath نیز تنها مسیر سیستمی فایل استریم ذخیره شده را بر می‌گرداند.
      به این ترتیب کدهای برنامه به صورت زیر تغییر خواهند کرد:

      using System.Data.SqlClient;
      using System.Data;
      using System.Data.SqlTypes;
      using System.IO;

      namespace FileStreamTest
      {
      class CFSqlFileStream
      {
      /// <summary>
      /// افزودن رکورد به جدول حاوی ستونی از نوع فایل استریم
      /// </summary>
      /// <param name="filePath">مسیر فایل</param>
      /// <param name="title">عنوانی دلخواه</param>
      public static void AddNewRecord(string filePath, string title)
      {
      //آیا فایل وجود دارد؟
      if (!File.Exists(filePath))
      throw new FileNotFoundException(
      "لطفا مسیر فایل معتبری را مشخص نمائید", filePath);

      //خواندن اطلاعات فایل در آرایه‌ای از بایت‌ها
      byte[] buffer = File.ReadAllBytes(filePath);

      using (SqlConnection objSqlCon = new SqlConnection())
      {
      //todo: کانکشن استرینگ باید از یک فایل کانفیگ خوانده شود
      objSqlCon.ConnectionString =
      "Data Source=(local);Initial Catalog=testdb2009;Integrated Security = true";
      objSqlCon.Open();

      //شروع یک تراکنش
      using (SqlTransaction objSqlTran = objSqlCon.BeginTransaction())
      {
      //استفاده از رویه ذخیره شده افزودن فایل
      using (SqlCommand objSqlCmd = new SqlCommand(
      "AddFile", objSqlCon, objSqlTran))
      {
      objSqlCmd.CommandType = CommandType.StoredProcedure;

      //مشخص ساختن وضعیت و مقدار پارامتر عنوان
      SqlParameter objSqlParam1 = new SqlParameter("@Title", SqlDbType.NVarChar, 255);
      objSqlParam1.Value = title;

      //مشخص ساختن پارامتر خروجی رویه ذخیره شده
      SqlParameter objSqlParamOutput = new SqlParameter("@filepath", SqlDbType.VarChar, -1);
      objSqlParamOutput.Direction = ParameterDirection.Output;

      //افزودن پارامترها به شیء کامند
      objSqlCmd.Parameters.Add(objSqlParam1);
      objSqlCmd.Parameters.Add(objSqlParamOutput);

      //اجرای رویه ذخیره شده
      objSqlCmd.ExecuteNonQuery();

      //و سپس دریافت خروجی آن
      string Path = objSqlCmd.Parameters["@filepath"].Value.ToString();

      //زمینه تراکنش فایل استریم موجود را دریافت کرده و از آن برای نوشتن محتویات فایل استفاده خواهیم کرد
      //این مورد نیز یکی از تازه‌های اس کیوال سرور 2008 است
      using (SqlCommand objCmd = new SqlCommand(
      "SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()", objSqlCon, objSqlTran))
      {
      byte[] objContext = (byte[])objCmd.ExecuteScalar();
      using (SqlFileStream objSqlFileStream =
      new SqlFileStream(Path, objContext, FileAccess.Write))
      {
      objSqlFileStream.Write(buffer, 0, buffer.Length);
      }
      }
      }

      objSqlTran.Commit();
      }
      }
      }

      /// <summary>
      /// دریافت اطلاعات فایل ذخیره شده به صورت آرایه‌ای از بایت‌ها
      /// </summary>
      /// <param name="fileId">کلید مورد استفاده</param>
      /// <returns></returns>
      public static byte[] GetDataFromDb(string fileId)
      {
      byte[] buffer = null;

      using (SqlConnection objSqlCon = new SqlConnection())
      {
      //todo: کانکشن استرینگ باید از یک فایل کانفیگ خوانده شود
      objSqlCon.ConnectionString =
      "Data Source=(local);Initial Catalog=testdb2009;Integrated Security = true";
      objSqlCon.Open();

      //شروع یک تراکنش
      using (SqlTransaction objSqlTran = objSqlCon.BeginTransaction())
      {
      //استفاده از رویه ذخیره شده دریافت مسیر فایل
      using (SqlCommand objSqlCmd =
      new SqlCommand("GetFilePath", objSqlCon, objSqlTran))
      {
      objSqlCmd.CommandType = CommandType.StoredProcedure;

      //مشخص ساختن پارامتر ورودی رویه ذخیره شده و مقدار دهی آن
      SqlParameter objSqlParam1 = new SqlParameter("@ID", SqlDbType.VarChar, 50);
      objSqlParam1.Value = fileId;
      objSqlCmd.Parameters.Add(objSqlParam1);

      //اجرای رویه ذخیره شده و دریافت مسیر سیستمی فایل استریم
      string path = string.Empty;
      using (SqlDataReader sdr = objSqlCmd.ExecuteReader())
      {
      sdr.Read();
      path = sdr[0].ToString();
      }

      //زمینه تراکنش فایل استریم موجود را دریافت کرده و از آن برای خواندن محتویات فایل استفاده خواهیم کرد
      //این مورد نیز یکی از تازه‌های اس کیوال سرور 2008 است
      using (SqlCommand objCmd = new SqlCommand(
      "SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()", objSqlCon, objSqlTran))
      {
      byte[] objContext = (byte[])objCmd.ExecuteScalar();

      using (SqlFileStream objSqlFileStream =
      new SqlFileStream(path, objContext, FileAccess.Read))
      {
      buffer = new byte[(int)objSqlFileStream.Length];
      objSqlFileStream.Read(buffer, 0, buffer.Length);
      }
      }
      }

      objSqlTran.Commit();
      }
      }

      return buffer;
      }
      }
      }
      در پایان برای تکمیل بحث می‌توان به مقاله‌ی مرجع زیر مراجعه کرد:
      FILESTREAM Storage in SQL Server 2008

      نظرات مطالب
      گروه بندی دینامیک(پویا) در StimulSoft
      سلام و احترام 
      من یک گزارش ساختم که قراره در پایین اون، اسم و سمت امضاداران، نشون داده بشه. برای این کار اومدم در page footer یه پنل گذاشتم و در اون پنل، cross data گذاشتم. تا وقتی که تعداد امضا داران کم هست، همه چیز درسته. ولی وقتی از یه تعداد بیشتر میشه، انگار کلا به هم میریزه. در صفحه اول گزارش، اخرین شخص امضادار دوبار تکرار میشه. ضمن اینکه حتی صفحه بندی هم به هم میریزه و شماره صفحه اول یک هست و صفحه دوم میشه سه و یک سوال دیگه اینکه فرض کنید مشکل اول حل شده، حالا اگه صفحه رو بذارم در حالت landscape، پیج هدر و پیج فوتر نظمشون به هم میریزه. چکار کنم که این مشکل هم حل بشه؟
      صفحه اول وقتی تعداد امضاداران کمه:

      صفحه دوم:

      صفحه اول وقتی تعداد امضاداران زیادمیشه:

      صفحه دوم:

      فایل mrt.
      الان برای حل مشکل چیکار کنم؟ کلا آیا برای اضافه کردن امضاداران این کار(یعنی استفاده از پنل و کراس دیتا) و این‌ها درسته؟

      نظرات مطالب
      انجام کارهای زمانبندی شده در برنامه‌های ASP.NET توسط DNT Scheduler
      - در جدول مرتبط باید فیلدهای «وضعیت پاسخ بانک» و «تاریخ و زمان ارسال به درگاه» وجود داشته باشند.
      - سپس یک جاب تعریف کنید که هر 10 دقیقه یکبار اجرا شود. در هر بار اجرای آن، در طی یک کوئری لیست مواردی را که «به درگاه پرداخت فرستاده شده»اند و «وضعیت پاسخ بانک» آن‌ها مشخص نیست را واکشی کنید. در اینجا تنها کافی است زمان ارسال واکشی شده را با تاریخ و زمان فعلی مقایسه کنید و عکس العمل لازم (مانند بررسی وضعیت پرداخت) را انجام دهید.
      نظرات مطالب
      EF Code First #3
      در مورد مسئله همزمانی بهترین راهکار چیست از نظر شما؟ (منظور در همین EF هست)
      استفاده از ConcurrencyCheck یا Timestamp و به چه صورتی؟ (فرقشون رو از لحاظ فنی می‌دونم, اینکه کدوم رو در کجا و چه مسائلی باید استفاده کرد رو می‌خوام بدونم)

      1. مثلا استفاده از فیلدی جداگانه (مثلا LastModifiedTime) در جداول مهم که امکان تداخل همزمانی در شبکه را دارند, و مزین کردن این فیلد با [ConcurrencyCheck]؟
      2. یا ستونهای جدول رو همگی مزین کنیم به [ConcurrencyCheck]؟
      3. یا یک فیلد از جنس timestamp تعریف کنیم؟
      4. یا جور دیگه ای حل کنیم این قضیه رو در جاهای مختلف؟

      ممنون