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

روش اول : استفاده از یک فیلد در هر جدول برای نگه دارای اسم فایل

مثال:
    public class Article
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Body { get; set; }
        public string RegisterDate { get; set; }
        public string FileName { get; set; }
    }
معایب:
این روش فقط در صورتی پاسخگو می‌باشد که هر رکورد فقط شامل یک فایل باشد. به طور مثال ممکن است برای یک مقاله، چندین عکس و فایل را ضمیمه‌ی آن کنیم. در این حالت این روش پاسخ گو نمی‌باشد؛ ولی می‌توانیم به صورت زیر نیز عمل کنیم:
ایجاد جدولی برای نگهداری فایل‌های هر رکورد از مقاله :


public class ArticleFiles
{
        public int Id { get; set; }
        public string FielName { get; set; }
        public string FileExtension { get; set; } 
        public Article Article { get; set; }
        public int FileSize { get; set; }
}
 
روش دوم : ایجاد جدولی پایه برای نگهدارای تمام فایل‌های آپلود شده

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


public class Attachment
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string FileName { get; set; }
        public string Extension { get; set; }
        public DateTime RegisterDate { get; set; }
        public int Size { get; set; }
        public ICollection<Article> ArticleFiles { get; set; }
        public ICollection<News> NewsFiles { get; set; }
        public int Viewed { get; set; }
    }
در این حالت باید بین تمام جداولی که نیاز به فایل دارند، رابطه ای با جدول Attachment داشته باشد. به طور مثال بین جدول مقالات و جدول Attachment یک رابطه‌ی یک به چند برای لیست فایل‌ها وجود خواهد داشت.


روش سوم : جدولی برای نگه داری اسم فایل‌ها، بدون رابطه

جدول Attachment در این روش، همانند روش دوم می‌باشد؛ با دو تفاوت:
1- با هیچ جدولی رابطه‌ای ندارد.
2- دو فیلد به عنوان نام جدول و Id رکورد به آن اضافه شده است.
تفاوت نسبت به روش دوم:
در روش دوم، ثبت یک رکورد، وابسته‌ی به ثبت رکورد در جدول Attachment بود و ابتدا می‌بایستی فایل در Attachment ذخیره می‌شد و بعد از بدست آوردن Id آن، رکورد مورد نظر (مقاله) را درج می‌کردیم. ولی در این روش ابتدا مقاله درج شده و بعد از آن فایل را با اسم جدول و ID رکورد مورد نظر ذخیره می‌کنیم:
public class Attachment
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string FileName { get; set; }
        public string Extension { get; set; }
        public DateTime RegisterDate { get; set; }
        public int Size { get; set; }
        public string TableName { get; set; }
        public int RowId { get; set; }
        public int Viewed { get; set; }        
    }

حالت پنجم :

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

نظراتی پیرامون حالت‌های مختلف:

1- داشتن یک جدول الحاقات برای هر جدول
  • اضافه کردن یک فیلد: بعضی‌ها این روش را ترجیح می‌دهند. به این دلیل که هر جدول، یک جدول attachment مختص به خود دارد؛ با توجه به فیلدهایی که لازم است. به طور مثال ممکن است بعد از گذشت مدتی، نیاز باشد تا دو فیلد برای فایل‌های هر مقاله اضافه شوند که در این حالت فقط به جدول attachment مقاله اضافه خواهند شد.

2- داشتن یک جدول پایه که کل فایل‌ها در آن ذخیره شوند (روش‌های دوم و سوم)

  • متمرکز شدن کل فایل‌ها در یک جدول: بیشتر پروژه‌ها و یا برنامه نویسان (طبق تجربه‌ی بنده) یک جدول پایه را برای این منظور دوست دارند. به دلیل اینکه تمام اطلاعات یکجا باشد.
  • عدم آپلود چندین باره‌ی یک فایل: در این حالت می‌توان از یک فایل چندین بار در چند جای مختلف استفاده نمود و در فضای هاست صرفه جویی می‌شود. این روش مدیریت سختی دارد و نیازمند کوئری‌های بیشتری می‌باشد.
  • وجود فیلد‌های زیاد null در جدول: در این حالت ممکن است ردیف‌هایی با ستون‌های مقدار null در جدول زیاد شوند. فرض کنید دو فیلد در جدول attachment وجود دارند که فقط توسط جدول مقالات مورد استفاده قرار می‌گیرند و در بقیه‌ی جداول بدون استفاده می‌باشند.


از کدام روش استفاده کنیم؟

نمی توان پیشنهاد کرد که الزاما از کدامیک از روش‌های بالا باید استفاده کنیم؛ چون نیازمندهای‌های هر پروژه با هم متفات است و نمی‌توان نسخه‌ای خاص را برای همه تجویز کرد.

مطالب
مفاهیم پایه سیستم های کنترل نسخه؛ قسمت سوم : جمع بندی
در اولین قسمت این سری، گیت و در قسمت دوم ، SVN را بررسی کردیم؛ در این مقاله قصد داریم یک جمع بندی از این دو مقاله داشته باشیم.
احتمالا در مورد این دو سیستم حرف‌های زیادی شنیده‌اید و احتمالا بیشتر آن‌ها در مورد گیت نظر مساعدتری داشته‌اند؛ ولی تفاوت‌هایی بین این دو سیستم هست که باید به نسبت هدف و نیازی که دارید آن را مشخص کنید. یکی از اصلی‌ترین این تفاوت‌ها این است که svn یک سیستم مرکزی است؛ ولی گیت اینگونه نیست که در ادامه تفاوت این دو مورد را تشریح می‌کنیم.
یک. SVN یک مخزن مرکزی دارد که همه‌ی تغییراتی که روی کپی‌ها انجام می‌شود، باید به سمت مخزن مرکزی Commit یا ارسال شوند. ولی در سیستم گیت یک سیستم مرکزی وجود ندارد و هر مخزنی که fork یا Clone می‌شود، یک مخزن جداگانه به حساب می‌آید و Commit شدن تنها به مخزن کپی شده صورت میگیرد و در صورت pull request ادغام با مخزن اولیه خودش صورت میگیرد.
دو. گیت به نسبت svn از پیچیدگی بیشتری برخوردار است؛ ولی برای پروژه‌های بزرگتر که کاربران زیادی با آن کار می‌کنند و احتمال شاخه بندی‌های زیادتر، در آن وجود دارد بهتر عمل می‌کند. موقعی که یک پروژه یا تیم کوچکی روی آن کار می‌کنند به دلیل commit شدن مستقیمی که svn دارد، کار راحت‌تر و آسان‌تر صورت می‌گیرد ولی با زیاد شدن کاربران و حجم کار، گیت کارآیی بالاتری دارد.
سه. از آن جا که گیت نیاز به fork شدن دارد و یک مخزن کاملا مجزا از پروژه اصلی تولید می‌کند؛ سرعت بهتری نسبت به svn که یک کپی از زیر مجموعه ساختار اصلی ایجاد می‌کند دارد.
چهار. شاخه بندی یک مفهوم اصلی و مهم در گیت به شمار می‌آید که اکثر کاربران همه روزه از آن استفاده می‌کنند و این اجازه را می‌دهد که که تغییرات و تاریخچه فعالیت هر کاربر را بر روی هر شاخه، جداگانه ببینیم. در svn پیاده سازی شاخه‌ها یا تگ‌ها سخت و مشکل است. همچنین شاخه بندی کار در svn به شکل سابق با کپی کردن صورت گرفته که گاهی اوقات به دلایلی که در قسمت قبل گفتیم، باعث ناسازگاری می‌گردد.
پنج. حجم مخازن گیت به نسبت svn خیلی کمتر است برای نمونه پروژه موزیلا 30 درصد حجم کمتری در مخزن گیت دارد. یکی از دلایلی که svn حجم بیشتری میگیرد این است که به ازای هر فایل دو فایل موجود است یکی که همان فایل اصلی است که کاربر با آن کار می‌کند و دیگری یک فایل دیگر در شاخه svn. است که برای کمک به عملیاتی چون وضعیت، تفاوت ها، ثبت تغییرات به کار می‌رود. در صورتی که در آن سمت، گیت، تنها به یک فایل شاخص 100 بایتی برای هر  دایرکتوری کاری نیاز دارد
شش. گیت عملیات کاربری را به جز fetch و push، خیلی سریع انجام میدهد. این عملیات شامل یافتن تفاوت‌ها، نمایش تاریخچه، ثبت تغییرات، ادغام شاخه‌ها و جابجایی بین شاخه‌ها می‌گردد.
هفت. در سیستم SVN به دلیل ساختار درختی که دارد، می‌توانید زیر مجموعه‌ی یک مخزن را بررسی کنید ولی در سیستم گیت اینکار امکان پذیر نیست. البته باید به این نکته توجه داشت که برای یک پروژه‌ی بزرگ شما مجبور هستید همیشه کل مخزن را دانلود کنید. حتی اگر تنها نسخه‌ی خاصی از این زیرمجموعه را در نظر داشته باشید. به همین علت در شهرهایی که اینترنت گرانقیمت و یا سرعت پایین عرضه می‌شود، گیت به صرفه‌تر است و زمان کمتری برای دانلود آن می‌ برد.
موارد تعریف شده زیر طبق گفته ویکی سایت Kernel.Org ذکر می‌شود:
  • گیت از سیستم SVN سریعتر عمل می‌کند.
  • در سیستم گیت هر شاخه بندی کل تاریخچه خود را به دنبال دارد.
  • فایل git که تنظیمات مخزن داخلش قرار دارد، ساختار ساده‌ای دارد و به راحتی می‌توان در صورت ایجاد مشکل، آن را حل کرد و به ندرت هم پیش می‌آید که مشکلی برایش پیش بیاید.
  • پشتیبانی گیری از یک سیستم مرکزی مثل SVN راحت‌تر از پشتیبانی گیری از پوشه‌های توزیع شده در مخزن گیت است.
  • ابزارهای کاربری svn تا به الان پیشرفت‌های چشمگیری داشته است. پلاگین‌ها و برنامه‌های بیشتری نسبت به سیستم گیت دارد. یکی از معروفترین این پلاگین‌ها، ابزار  tortoisesvn  است (البته ابزارهای گیت امروز رشد چشمگیرتری داشته اند که در قسمت اول نمونه‌های آن ذکر شد).
  • سیستم svn برای نسخه بندی و تشخیص تفاوت‌ها از یک سیستم ساده اعداد ترتیبی استفاده می‌کند که اولین ثبت با شماره یک آغاز شده و به ترتیب ادامه می‌یابد و برای کاربران هم خواندنش راحت است و هم قابل پیش بینی است. به همین جهت برای بررسی تاریخچه‌ها و دیگر گزارش‌ها تا حدی راحت عمل می‌کند. در سیستم شاخه بندی این سیستم شماره گذاری چندان مطلوب نیست و متوجه نمی‌شوید که این شاخه از کجا نشات گرفته است. در حال حاضر برای پروژه‌ی موزیلا این عدد به 6 رقم رسیده است ولی در آن سمت، سیستم گیت از هش SH-1 استفاده می‌کند که یک رشته 40 کاراکتری است و 8 رقم اول آن به منشاء اشاره می‌کند که باعث می‌شود متوجه بشویم که این شاخه از کجا آمده است ولی از آنجا که این عدد یکتا ترتیبی نیست، برای خواندن و گزارشگیری‌هایی که در SVN راحت صورت می‌گیرد، در گیت ممکن نیست یا مشکل است.
  • گیت رویدادهای ادغام و شاخه بندی را بهتر انجام می‌دهد.

مطالب
خلاصه‌ای از روش‌های ارسال داده‌های سمت سرور به کدهای جاوا اسکریپتی در ASP.NET MVC
روش‌های زیادی برای ارسال داده‌های سمت سرور تهیه شده در یک برنامه‌ی ASP.NET به کدهای سمت کاربر JavaScript ایی وجود دارند که تعدادی از مهم‌ترین‌های آن‌ها را در این مطلب بررسی خواهیم کرد.

روش اول: دریافت اطلاعات سمت سرور به کمک درخواست‌های Ajax

استفاده از Ajax یکی از روش‌های کلاسیک دریافت اطلاعات سمت سرور در کدهای جاوا اسکریپتی است.
<script type="text/javascript">
var products = [];
$(function() {
    $.getJSON("/home/products", function(response) {
        products = response.products;
    });
});
</script>
برای نمونه در اینجا با استفاده از امکانات jQuery، درخواست Ajax ایی به سرور ارسال شده و سپس نتیجه‌ی دریافتی، به آرایه‌ی جاوا اسکریپتی products نسبت داده شده‌است.
- مزایا: استفاده از Ajax، روشی بسیار متداول و شناخته شده‌است و به کمک انواع و اقسام روش‌های بازگشت JSON از سرور، می‌توان با آن کار کرد.
- معایب: درخواست Ajax، صرفا پس از بارگذاری اولیه‌ی صفحه به سمت سرور ارسال خواهد شد و در این بین، کاربر وقفه‌ای را مشاهده خواهد کرد. همچنین در اینجا بجای یک درخواست از سرور، حداقل دو درخواست باید ارسال شوند؛ یکی برای بارگذاری صفحه‌ی اصلی و دیگری برای دریافت اطلاعات Ajax ایی از سرور به صورت غیرهمزمان.


روش دوم: دریافت اطلاعات از یک فایل جاوا اسکریپتی خارجی

اطلاعات سمت کاربر را از یک فایل جاوا اسکریپتی خارجی الحاق شده‌ی به صفحه‌ی جاری نیز می‌توان تهیه کرد:
 <script src="/file.js"></script>
یک چنین فایلی را می‌توان توسط کدهای سمت سرور نیز بازگشت داد و اطلاعات آن‌را تهیه و پر کرد. در این حالت فقط کافی است که ContentType شیء Response را از نوع application/javascript مشخص کنیم و سپس یک خروجی متنی جاوا اسکریپتی را از سمت سرور بازگشت دهیم.
این روش نیز تقریبا مانند حالت یک درخواست Ajax ایی کار می‌کند و اطلاعات مورد نیاز را در طی یک درخواست جداگانه، پس از بارگذاری صفحه‌ی اصلی، از سرور دریافت خواهد کرد. البته در حالت کار با Ajax، می‌توان در طی یک callback، نتیجه را دریافت کرد و سپس عکس العمل نشان داد؛ اما در اینجا callback ایی وجود ندارد.


روش سوم: استفاده از SignalR

در SignalR ابتدا سعی می‌شود تا با استفاده از Web Sockets ارتباطی ماندگار بین کلاینت و سرور برقرار شود و سپس در این حالت، سرور می‌تواند مدام اطلاعاتی، مانند تغییرات داده‌های خود را به سمت کاربر، جهت نمایش و یا محاسبات خاص خود ارسال کند. اگر حالت Web Socket میسر نباشد (توسط سرور یا کلاینت پشتیبانی نشود)، به حالت‌های دیگری مانند server events, forever frames, long polling سوئیچ خواهد کرد. اطلاعات بیشتر


روش چهارم: قرار دادن اطلاعات سمت سرور در کدهای HTML صفحه

روش متداول دیگری جهت تامین اطلاعات جاوا اسکریپتی سمت کاربر، قرار دادن آن‌ها در ویژگی‌های data-* ارائه شده در HTML5 است.
<ul>
@foreach (var product in products)
{
  <li id="product@product.Id" data-rank="@product.Rank">@product.Name</li>
}
</ul>
در اینجا لیستی از محصولات، به صورت متداولی از کنترلر دریافت گردیده و سپس ویژگی data-rank هر ردیف نمایش داده شده نیز توسط این اطلاعات سمت سرور مقدار دهی شده‌اند.
اکنون برای دسترسی به مقدار data-rank سطری مانند product1، در کدهای جاوا اسکریپتی صفحه می‌توان نوشت:
<script type="text/javascript">
var product1Rank = $("#product1").data("rank");
</script>
این روش برای قرار دادن اطلاعات ثابت اشیاء، روش مرسومی است.


روش پنجم: قرار دادن اطلاعات سمت سرور در کدهای جاوا اسکریپتی صفحه

این روش همانند روش چهارم است، با این تفاوت که اینبار اطلاعات مورد نیاز، مستقیما به یک متغیر جاوا اسکریپتی انتساب داده شده‌است:
 <script type="text/javascript">
var product1Name = "@product1.Name";
</script>
مزیت این روش، عدم ارسال درخواست اضافه‌تری به سرور برای دریافت اطلاعات مورد نیاز است. مقدار product1Name در همان بار اول رندر صفحه از سمت سرور، مشخص می‌گردد.


روش ششم: انتساب یک شیء دات نتی به یک متغیر جاوا اسکریپتی

این روش همانند روش پنجم است، با این تفاوت که اینبار قصد داریم بجای یک مقدار ثابت رشته‌ای یا عددی، برای مثال، آرایه‌ای از اشیاء را به یک متغیر جاوا اسکریپتی انتساب دهیم. در اینجا ابتدا اطلاعات مورد نظر را به فرمت JSON تبدیل می‌کنیم:
//سمت سرور
[HttpGet]
public ActionResult Index()
{
    var array = new[]
    {
        "Afghanistan",
        "Albania",
        "Algeria",
        "American Samoa",
        "Andorra",
        "Angola",
        "Anguilla",
        "Antarctica",
        "Antigua and/or Barbuda"
     };
     ViewBag.JsonString = new JavaScriptSerializer().Serialize(array);
     return View();
}
سپس توسط متد Html.Raw می‌توان این رشته‌ی JSON را که اکنون حاوی آرایه جاوا اسکریپتی سمت سرور است، به یک متغیر جاوا اسکریپتی نسبت داد:
 //سمت کلاینت
<script type="text/javascript">
var jsonArray = @Html.Raw(@ViewBag.JsonString);
</script>
استفاده از Html.Raw در این حالت از این جهت ضروری است که اطلاعاتی مانند [] به صورت encode شده در سمت کاربر نمایش داده نشوند؛ چون Razor به صورت پیش فرض اطلاعات را Encode می‌کند.
و یا اینکار را به صورت خلاصه به شکل زیر نیز می‌توان در سمت کاربر انجام داد:
 <script type="text/javascript">
  var model = @Html.Raw(Json.Encode(Model));
  // your js code here
</script>
در اینجا کار تبدیل اطلاعات مدل به JSON، در همان سمت RazorView انجام شده‌است.
مطالب
آموزش Knockout.Js #2
در پست قبلی با مفاهیم و ویژگی‌های کلی KO آشنا شدید. KO از الگوی طراحی MVVM استفاده می‌کند. از آن جا که یکی از پیش نیاز‌های KO آشنایی اولیه با مفاهیم View و Model است نیاز به توضیح در این موارد نیست اما اگر به هر دلیلی با این مفاهیم آشنایی ندارید می‌توانید از اینجا شروع کنید. اما درباره ViewModel که کمی مفهوم متفاوتی دارد، این نکته قابل ذکر است که KO از ViewModel برای ارتباط مستقیم بین View و Model استفاده می‌کند، چیزی شبیه به منطق MVC با این تفاوت که ViewModel به جای Controller قرار خواهد گرفت.

ابتدا باید به شرح برخی مفاهیم در KO بپردازم:
»Observable(قابل مشاهده کردن تغییرات)
KO از Observable برای ردیابی و مشاهده تغییرات خواص ViewModel استفاده می‌کند. در واقع Observable دقیقا شبیه به متغیر‌ها در JavaScript عمل می‌کنند با این تفاوت که به KO اجازه می‌دهند که تغییرات این خواص را پیگیری کند و این تغییرات را به بخش‌های مرتبط View اعمال نماید. اما سوال این است که KO چگونه متوجه می‌شود که این تغییرات بر کدام قسمت در View تاثیر خواهند داشت؟ جواب این سوال در مفهوم Binding است.
»Binding
برای اتصال بخش‌های مختلف View به Observable‌ها باید از binding(مقید سازی) استفاده کنیم. بدون عملیات binding، امکان اعمال تغییرات Observable‌ها بر روی عناصر HTML امکان پذیر نیست.
برای مثال در شکل زیر یکی از خواص ViewModel را به View متناظر مقید شده است.

با کمی دقت در شکل بالا این نکته به دست می‌آید که می‌توان در یک ViewModel، فقط خواص مورد نظر را به عناصر Html مقید کرد.

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

فایل‌های مورد نیاز برای KO رو می‌توانید از اینجا دانلود نمایید و به پروژه اضافه کنید. به صورت پیش فرض فایل‌های مورد نیاز KO، در پروژه‌های MVC 4 وجود دارد و نیاز به دانلود آن‌ها نیست و شما باید فقط مراحل BundleConfig را انجام دهید.

تعریف ViewModel

برای تعریف ViewModel و پیاده سازی مراحل Observable و binding باید به صورت زیر عمل نمایید:

<html lang='en'>
<head>
<title>Hello, Knockout.js</title>
<meta charset='utf-8' />
<link rel='stylesheet' href='style.css' />
</head>
<body>
<h1>Hello, Knockout.js</h1>
<script type='text/javascript' src='knockout-2.1.0.js'>   
      <script type='text/javascript'>
             var personViewModel = {
                  firstName: "Masoud",
                  lastName: "Pakdel"
                };
                 ko.applyBindings(personViewModel);
       </script>
</script>
</body>
</html>
مشاهده می‌کنید که ابتدا یک ViewModel به نام person ایجاد کردم همراه با دو خاصیت به نام‌های firstName و lastName. تابع applyBinding برای KO بدین معنی است که این آبجکت به عنوان یک ViewModel در این صفحه مورد استفاده قرار خواهد گرفت. اما برای مشاهده تغییرات باید یک عنصر HTML را یه این ViewModel مقید(bind) کنیم.

مقید سازی عناصر HTML

برای مقید سازی عناصر HTML به ViewModel‌ها باید از data-bind attribute استفاده نماییم. برای مثال:
<p><span data-bind='text: firstName'></span>'s Shopping Cart</p>
اگر به data-bind در تگ span بالا توجه کنید خواهید دید که مقدار text در این تگ را به خاصیت firstName در viewModel این صفحه bind شده است. تا اینجا KO می‌داند که چه عنصر از DOM به کدام خاصیت از ViewModel مقید شده است اما هنوز دستور ردیابی تغییرات(Observable) را برای KO تعیین نکردیم.

چگونه خواص را Observable کنیم
در پروژه‌های WPF، فقط در صورتی تغییرات خواص یک کلاس ردیابی می‌شوند که اولا کلاس اینترفیس INotifyPropertyChanged را پیاده سازی کرده باشد ثانیا، در متد set این خواص، متد OnPropertyChanged(البته این متد می‌تواند هر نام دیگری نیز داشته باشد) صدا زده شده باشد. نکته مهم و اساسی در KO نیز همین است که برای اینکه KO بتواند تغییرات هر خاصیت را مشاهده کند حتما خواص مورد نظر  باید Observable  شوند. برای این کار کافیست به صورت عمل کنید:
var personViewModel = {
  firstName: ko.observable("Masoud"),
  lastName: ko.observable("Pakdel")
};
مزیت اصلی برای اینکه حتما خواص مورد نظرتان  Observable شوند این است که، در صورتی که مایل نباشید تغییرات یک خاصیت  بر روی View اعمال شود کافیست از دستور بالا استفاده نکنید. درست مثل اینکه هرگز مقدار آن تغییر نکرده است.

پیاده سازی متد‌های get و set
همان طور که متوجه شدید، Observable‌ها متغیر نیستند بلکه تابع هستند در نتیجه برای دستیابی به مقدار یک observable کافیست آن را بدون پارامتر ورودی صدا بزنیم و برای تغییر در مقدار آن باید همان تابع را با مقدار جدید صدا بزنیم. برای مثال:
personViewModel.firstName() // Get
personViewModel.firstName("Masoud") // Set
البته این نکته را هم متذکر شوم که در ViewModel‌های خود می‌توانید توابع سفارشی مورد نیاز را بنویسید و از آن‌ها در جای مناسب استفاده نماید(شبیه به مفاهیم Command‌ها در WPF)
مقید سازی تعاملی
اگر با WPF آشنایی دارید می‌دانید که در این گونه پروژه‌ها می‌توان رویداد‌های مورد نظر را به Command‌های خاص در ViewModel مقید کرد. در KO نیز این امر به آسانی امکان پذیر است که به آن Interactive Bindings می‌گویند. فقط کافیست در data-bind attribute  از نام رویداد استفاده نماییم. مثال:
ایتدا بک ViewModel به صورت زیر خواهیم داشت:
function PersonViewModel() {
   this.firstName = ko.observable("Masoud");
   this.lastName = ko.observable("Pakdel");
   this.clickMe= function() {
    alert("this is test!");
  };
};
تنها نکته قابل ذکر تعریف تابع سفارشی به نام clickMe است که به نوعی معادل Command مورد نظر ما در WPF است.  در عنصر HTML مورد نظر که در این جا button است باید data-binding به صورت زیر باشد:
<button data-bind='click: clickMe'>Click Me...</button>
در نتیجه بعد از کلیک بر روی button بالا تابع مورد نظر در viewModel اجرا خواهد شد.
پس به صورت خلاصه:
  • ابتدا ViewModel مورد نظر را ایجاد نمایید؛
  • سپس با استفاده از data-bind عملیات مقید سازی بین View و ViewModel را انجام دهید
  • در نهایت با استفاده از Obsevable تغییرات خواص مورد نظر را ردیابی نمایید.

ادامه دارد...

 
مطالب
Func یا Expression Func در EF
با بررسی کدهای مختلف Entity framework گاهی از اوقات در امضای توابع کمکی نوشته شده، <>Func مشاهده می‌شود و در بعضی از موارد <<>Expression<Func و ... به نظر استفاده کنندگان دقیقا نمی‌دانند که تفاوت این دو در چیست و کدامیک را باید/یا بهتر است بکار برد.

ابتدا مثال کامل ذیل را در نظر بگیرید:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
using System.Linq.Expressions;

namespace Sample
{
    public abstract class BaseEntity
    {
        public int Id { set; get; }
    }

    public class Receipt : BaseEntity
    {
        public int TotalPrice { set; get; }
    }

    public class MyContext : DbContext
    {
        public DbSet<Receipt> Receipts { get; set; }
    }

    public class Configuration : DbMigrationsConfiguration<MyContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = true;
            AutomaticMigrationDataLossAllowed = true;
        }

        protected override void Seed(MyContext context)
        {
            if (!context.Receipts.Any())
            {
                for (int i = 0; i < 20; i++)
                {
                    context.Receipts.Add(new Receipt { TotalPrice = i });
                }
            }
            base.Seed(context);
        }
    }

    public static class EFUtils
    {
        public static IList<T> LoadEntities<T>(this DbContext ctx, Expression<Func<T, bool>> predicate) where T : class
        {
            return ctx.Set<T>().Where(predicate).ToList();
        }

        public static IList<T> LoadData<T>(this DbContext ctx, Func<T, bool> predicate) where T : class
        {
            return ctx.Set<T>().Where(predicate).ToList();
        }
    }

    public static class Test
    {
        public static void RunTests()
        {
            startDB();

            using (var context = new MyContext())
            {
                var list1 = context.LoadEntities<Receipt>(x => x.TotalPrice == 10);
                var list2 = context.LoadData<Receipt>(x => x.TotalPrice == 20);
            }
        }

        private static void startDB()
        {
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Configuration>());
            // Forces initialization of database on model changes.
            using (var context = new MyContext())
            {
                context.Database.Initialize(force: true);
            }
        }
    }
}
در این مثال ابتدا کلاس Receipt تعریف شده و سپس توسط کلاس MyContext در معرض دید EF قرار گرفته است. در ادامه توسط کلاس Configuration نحوه آغاز بانک اطلاعاتی مشخص گردیده است؛ به همراه ثبت تعدادی رکورد نمونه.
نکته اصلی مورد بحث، کلاس کمکی EFUtils است که در آن دو متد الحاقی LoadEntities و LoadData تعریف شده‌اند. در متد LoadEntities، امضای متد شامل Expression Func است و در متد LoadData فقط Func ذکر شده است.
در ادامه اگر برنامه را توسط فراخوانی متد RunTests اجرا کنیم، به نظر شما خروجی SQL حاصل از list1 و list2 چیست؟
احتمالا شاید عنوان کنید که هر دو یک خروجی SQL دارند (با توجه به اینکه بدنه متدهای LoadEntities و LoadData دقیقا/یا به نظر یکی هستند) اما یکی از پارامتر 10 استفاده می‌کند و دیگری از پارامتر 20. تفاوت دیگری ندارند.
اما ... اینطور نیست!
خروجی SQL متد LoadEntities در متد RunTests به صورت زیر است:
 SELECT
[Extent1].[Id] AS [Id],
[Extent1].[TotalPrice] AS [TotalPrice]
FROM [dbo].[Receipts] AS [Extent1]
WHERE 10 = [Extent1].[TotalPrice]
و ... خروجی متد LoadData به نحو زیر:
 SELECT
[Extent1].[Id] AS [Id],
[Extent1].[TotalPrice] AS [TotalPrice]
FROM [dbo].[Receipts] AS [Extent1]
بله. در لیست دوم هیچ فیلتری انجام نشده (در حالت استفاده از Func خالی) و کل اطلاعات موجود در جدول Receipts، بازگشت داده شده است.
چرا؟
Func اشاره‌گری است به یک متد و Expression Func بیانگر ساختار درختی عبارت lambda نوشته شده است. این ساختار درختی صرفا بیان می‌کند که عبارت lambda منتسب، چه کاری را قرار است یا می‌تواند انجام دهد؛ بجای انجام واقعی آن.
 public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
اگر از Expression Func استفاده شود، از متد Where ایی استفاده خواهد شد که خروجی IQueryable دارد. اگر از Func استفاده شود، از overload دیگری که خروجی و ورودی  IEnumerable دارد به صورت خودکار استفاده می‌گردد.
بنابراین هرچند بدنه دو متد LoadEntities و LoadData به ظاهر یکی هستند، اما بر اساس نوع ورودی Where ایی که دریافت می‌کنند، اگر Expression Func باشد، EF فرصت آنالیز و ترجمه عبارت ورودی را خواهد یافت اما اگر Func باشد، ابتدا باید کل اطلاعات را به صورت یک لیست IEnumerable دریافت و سپس سمت کلاینت، خروجی نهایی را فیلتر کند.
اگر برنامه را اجرا کنید نهایتا هر دو لیست یک و دو، بر اساس شرط عنوان شده عمل خواهند کرد و فیلتر خواهند شد. اما در حالت اول این فیلتر شدن سمت بانک اطلاعاتی است و در حالت دوم کل اطلاعات بارگذاری شده و سپس سمت کاربر فیلتر می‌شود (که کارآیی پایینی دارد).


نتیجه گیری
به امضای متد Where ایی که در حال استفاده است دقت کنید. همینطور در مورد Sum ، Count و یا موارد مشابه دیگری که predicate قبول می‌کنند.
مطالب
Tuple در دات نت 4

نوع جدیدی در دات نت 4 به نام Tuple اضافه شده است که در این مطلب به بررسی آن خواهیم پرداخت.
در ریاضیات، Tuple به معنای لیست مرتبی از اعضاء با تعداد مشخص است. Tuple در زبان‌های برنامه نویسی Dynamic مانند اف شارپ، Perl ، LISP و بسیاری موارد دیگر مطلب جدیدی نیست. در زبان‌های dynamic برنامه نویس‌ها می‌توانند متغیرها را بدون معرفی نوع آن‌ها تعریف کنند. اما در زبان‌های Static مانند سی شارپ، برنامه نویس‌ها موظفند نوع متغیرها را پیش از کامپایل آن‌ها معرفی کنند که هر چند کار کد نویسی را اندکی بیشتر می‌کند اما به این صورت شاهد خطاهای کمتری نیز خواهیم بود (البته سی شارپ 4 این مورد را با معرفی واژه‌ی کلیدی dynamic تغییر داده است).
برای مثال در اف شارپ داریم:
let data = (“John Doe”, 42)

که سبب ایجاد یک tuple که المان اول آن یک رشته و المان دوم آن یک عدد صحیح است می‌شود. اگر data را بخواهیم نمایش دهیم خروجی آن به صورت زیر خواهد بود:
printf “%A” data
// Output: (“John Doe”,42)

در دات نت 4 فضای نام جدیدی به نام System.Tuple معرفی شده است که در حقیقت ارائه دهنده‌ی نوعی جنریک می‌باشد که توانایی در برگیری انواع مختلفی را دارا است :
public class Tuple<T1>
up to:
public class Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>

همانند آرایه‌ها، اندازه‌ی Tuples نیز پس از تعریف قابل تغییر نیستند (immutable). اما تفاوت مهم آن با یک آرایه در این است که اعضای آن می‌توانند نوع‌های کاملا متفاوتی داشته باشند. همچنین تفاوت مهم آن با یک ArrayList یا آرایه‌ای از نوع Object، مشخص بودن نوع هر یک از اعضاء آن است که type safety بیشتری را به همراه خواهد داشت و کامپایلر می‌تواند در حین کامپایل دقیقا مشخص نماید که اطلاعات دریافتی از نوع صحیحی هستند یا خیر.

یک مثال کامل از Tuples را در کلاس زیر ملاحظه خواهید نمود:

using System;
using System.Linq;
using System.Collections.Generic;

namespace TupleTest
{
class TupleCS4
{
#region Methods (4)

// Public Methods (4)

public static Tuple<string, string> GetFNameLName(string name)
{
if (string.IsNullOrWhiteSpace(name))
throw new NullReferenceException("name is empty.");

var nameParts = name.Split(',');

if (nameParts.Length != 2)
throw new FormatException("name must contain ','");

return Tuple.Create(nameParts[0], nameParts[1]);
}

public static void PrintSelectedTuple()
{
var list = new List<Tuple<string, int>>
{
new Tuple<string, int>("A", 1),
new Tuple<string, int>("B", 2),
new Tuple<string, int>("C", 3)
};

var item = list.Where(x => x.Item2 == 2).SingleOrDefault();
if (item != null)
Console.WriteLine("Selected Item1: {0}, Item2: {1}",
item.Item1, item.Item2);
}

public static void PrintTuples()
{
var tuple1 = new Tuple<int>(12);
Console.WriteLine("tuple1 contains: item1:{0}", tuple1.Item1);

var tuple2 = Tuple.Create("Item1", 12);
Console.WriteLine("tuple2 contains: item1:{0}, item2:{1}",
tuple2.Item1, tuple2.Item2);

var tuple3 = Tuple.Create(new DateTime(2010, 5, 6), "Item2", 20);
Console.WriteLine("tuple3 contains: item1:{0}, item2:{1}, item3:{2}",
tuple3.Item1, tuple3.Item2, tuple3.Item3);
}

public static void Tuple8()
{
var tup =
new Tuple<int, int, int, int, int, int, int, Tuple<int, int>>
(1, 2, 3, 4, 5, 6, 7, new Tuple<int, int>(8, 9));

Console.WriteLine("tup.Rest Item1: {0}, Item2: {1}",
tup.Rest.Item1,tup.Rest.Item2);
}

#endregion Methods
}
}

using System;

namespace TupleTest
{
class Program
{
static void Main()
{
var data = TupleCS4.GetFNameLName("Vahid, Nasiri");
Console.WriteLine("Data Item1:{0} & Item2:{1}",
data.Item1, data.Item2);

TupleCS4.PrintTuples();

TupleCS4.PrintSelectedTuple();

TupleCS4.Tuple8();

Console.WriteLine("Press a key...");
Console.ReadKey();
}
}
}

توضیحات :
- روش‌های متفاوت ایجاد Tuples را در متد PrintTuples می‌توانید ملاحظه نمائید. همچنین نحوه‌ی دسترسی به مقادیر هر کدام از اعضاء نیز مشخص شده است.
- کاربرد مهم Tuples در متد GetFNameLName نمایش داده شده است؛ زمانیکه نیاز است تا چندین خروجی از یک تابع داشته باشیم. به این صورت دیگر نیازی به تعریف آرگومان‌هایی به همراه واژه کلیدی out نخواهد بود یا دیگر نیازی نیست تا یک شیء جدید را ایجاد کرده و خروجی را به آن نسبت دهیم. به همان سادگی زبان‌های dynamic در اینجا نیز می‌توان یک tuple را ایجاد و استفاده کرد.
- بدیهی است از Tuples در یک لیست جنریک و یا حالات دیگر نیز می‌توان استفاده کرد. مثالی از این دست را در متد PrintSelectedTuple ملاحظه خواهید نمود. ابتدا یک لیست جنریک از Tuple ایی با دو عضو تشکیل شده است. سپس با استفاده از امکانات LINQ ، عضوی که آیتم دوم آن مساوی 2 است یافت شده و سپس المان‌های آن نمایش داده می‌شود.
- نکته‌ی دیگری را که حین کار با Tuples می‌توان در نظر داشت این است که اعضای آن حداکثر شامل 8 عضو می‌توانند باشند که عضو آخر باید یک Tuple تعریف گردد و بدیهی است این Tuple‌ نیز می‌تواند شامل 8 عضو دیگر باشد و الی آخر که نمونه‌ای از آن را در متد Tuple8 می‌توان مشاهده کرد.

بازخوردهای دوره
متدهای async تقلبی
در این مثال عملیات تبدیل sync به async درست انجام شده ؟
کد sync :
        public string AESEncrypt(string Text)
        {
            byte[] dataToEncrypt = Encoding.UTF8.GetBytes(Text);
            byte[] key = Convert.FromBase64String(AES_Key);
            byte[] iv = Convert.FromBase64String(AES_Iv);

            using (var aes = new AesManaged())
            {
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.PKCS7;
                aes.Key = key;
                aes.IV = iv;
                using (var ms = new MemoryStream())
                {
                    using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(dataToEncrypt, 0, dataToEncrypt.Length);
                        cs.FlushFinalBlock();
                        return Convert.ToBase64String(ms.ToArray());
                    }
                }
            }
        }
کد async :
        public async Task<string> AESEncryptAsync(string Text)
        {
            byte[] dataToEncrypt = Encoding.UTF8.GetBytes(Text);
            byte[] key = Convert.FromBase64String(AES_Key);
            byte[] iv = Convert.FromBase64String(AES_Iv);

            using (var aes = new AesManaged())
            {
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.PKCS7;
                aes.Key = key;
                aes.IV = iv;
                using (var ms = new MemoryStream())
                {
                    using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        await cs.WriteAsync(dataToEncrypt, 0, dataToEncrypt.Length);
                        cs.FlushFinalBlock();
                        return Convert.ToBase64String(ms.ToArray());
                    }
                }
            }
        }
من این دو متد رو از نظر "زمان اجرا" ، "ترد مشغول" ، "هسته cpu مشغول" در asp.net mvc تست گرفتم {1000 بار فرخوانی} .
در این تست متد async  هر بار زمان بیشتری برای محاسبه برد (که خوب چون ترد جدید باز میکنه طبیعی هست) و در ترد‌ها و هسته‌های مختلف cpu اجرا میشد ، که این هم طبیعی هست ، طرف نظر از این که طبق مقاله حاضر کلا اینجا async نباید اجرا بشه چون خارج از مرز سیستم نیست ولی عملیات async کامل انجام میشه (فکر میکنم).
اما متد sync که بررسی شد ، خروجی به صورتی بود که آن هم هر بار در ترد و هسته مختلف سی پی یو اجرا میشد ! سوال دلیل چیست ؟
مگر تفاوت sync و async  دقیقا به اجرای آنها نیست ؟ پس چرا در هر دو حالت ما اجرا‌های یکسانی داریم ؟
ممنونم.
بازخوردهای دوره
مدیریت استثناءها در حین استفاده از واژه‌های کلیدی async و await
همانطور که در بالا اشاره کردید "در مثال فوق، نحوه‌ی ترکیب دو Task را توسط Task.WhenAll .... "در برخی موارد استفاده از async باعث افزایش کارآیی نیز می‌شود، آیا در موردی که مثلا من در یک اکشن برای انجام کاری نیاز به 4 درخواست  مجزا به دیتابیس دارم و بعد از گرفتن نتیجه این 4 درخواست می‌توانم درخواست نهایی را به دیتابیس بفرستم، استفاده از async باعث افزایش کارایی نیز می‌شود ؟
برای تشریح بهتر من نتیجه تست خود را اضافه میکنم. من از mvc5 و EF6  database first استفاده کردم.
حالت sync :
var watch = Stopwatch.StartNew();

 int actionId = db.CF_AccessLevel.Where(a => a.Name.ToLower().Trim() == "Edit").Select(a => a.CF_AccessLevelId).Single();

 int moduleItemId = db.CF_ModuleItem.Where(m => m.Title.ToLower().Trim() == "license".ToLower().Trim()).Select(m => m.CF_ModuleItemId).Single();

 int groupRoleId = db.Users.Where(u => u.UserId == 1).Select(u => u.UserRoleId).Single();

watch.Stop();
 var elapsedMs = watch.ElapsedMilliseconds;

حالت async:
var watch = Stopwatch.StartNew();
 var something = Task<int>.Factory.StartNew(() => db.CF_AccessLevel.Where(a => a.Name.ToLower().Trim() == "Edit").Select(a => a.CF_AccessLevelId).Single());
something.Wait();
int actionId = something.Result;

var something1 = Task<int>.Factory.StartNew(() => db.CF_ModuleItem.Where(m => m.Title.ToLower().Trim() == "license".ToLower().Trim()).Select(m => m.CF_ModuleItemId).Single());
 something1.Wait();
 int moduleItemId = something1.Result;

var something2 = Task<int>.Factory.StartNew(() => db.Users.Where(u => u.UserId == 1).Select(u => u.UserRoleId).Single());
 something2.Wait();
 int groupRoleId = something2.Result;
 watch.Stop();
 var elapsedMs = watch.ElapsedMilliseconds;
در هر  حالت بعد از انجام  3 درخواست ، درخواست نهایی را به سرور میفرستم (در کدهای بالا موجود نیست)
و نتیجه با جزئیات را در آخر اضافه کرده ام :
اما خلاصه میانگین روش sync 222 ms و روش async 191.75ms می‌باشد حدود 35.25ms تفات وجود دارد.
حال آیا تفاوت معنی دار می‌باشد؟ آیا کد async نوشته شده صحیح است؟ اگر صحیح نیست چه روشی صحیح میباشد؟ اگر نباید از async استفاده شود چه روشی بهتر است؟
همانطور که از کد مشخص است برای هدف authorization  نوشتم، ولی اگر بخواهم به صورت async در فیلتر استفاده کنم امکانپذیر نیست ، آیا راهی وجود دارد برای استفاده از async در فیلتر سفارشی توی    mvc5 ؟
202  221
 179 226
 198 208
 197  219
 188  245
 195  207
 193  217
 187  220
 171  212
 227  215
 177  312
 187  222
 191.75  227
   
نظرات مطالب
بررسی تغییرات Blazor 8x - قسمت چهارم - معرفی فرم‌های جدید تعاملی

یک نکته‌ی تکمیلی: روش طراحی binding دو طرفه در Blazor SSR

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

[Parameter] public T? Value { set; get; }

[Parameter] public EventCallback<T?> ValueChanged { get; set; }

[Parameter] public Expression<Func<T?>> ValueExpression { get; set; } = default!;

اگر این خواص را به کامپوننت‌های توکار خود Blazor متصل کنیم (مانند InputBox آن و مابقی آن‌ها)، نیازی به کدنویسی بیشتری ندارند و کار می‌کنند. اما اگر قرار است از یک input ساده‌ی Html ای استفاده کنیم، نیاز است ValueChanged آن‌را اینبار در متد OnInitialized فراخوانی کنیم؛ چون در زمان post-back به سرور است که مقدار آن در اختیار مصرف کننده‌ی کامپوننت قرار می‌گیرد. این مورد، مهم‌ترین تفاوت نحوه‌ی طراحی binding دوطرفه در Blazor SSR با مابقی حالات و نگارش‌های Blazor است.

بررسی وقوع post-back به سرور به دو روش زیر میسر است:

الف) بررسی کنیم که آیا HttpPost ای رخ‌داده‌است؟ سپس در همین لحظه، متد ValueChanged.InvokeAsync را فراخوانی کنیم:

[CascadingParameter] internal HttpContext HttpContext { set; get; } = null!;

protected override void OnInitialized()
{
   base.OnInitialized();

   if (HttpContext.IsPostRequest())
   {
      SetValueCallback(Value);
   }
}

private void SetValueCallback(string value)
{
   if (!ValueChanged.HasDelegate)
   {
      return;
   }

   _ = ValueChanged.InvokeAsync(value);
}

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

ب) می‌توان در قسمت OnInitialized، بررسی کرد که آیا درخواست جاری به همراه اطلاعات یک فرم ارسال شده‌ی به سمت سرور است یا خیر؟ روش کار به صورت زیر است:

protected override void OnInitialized()
{
   base.OnInitialized();

   if (HttpContext.Request.HasFormContentType &&
       HttpContext.Request.Form.TryGetValue(ValueField.HtmlFieldName, out var data))
   {
      SetValueCallback(data.ToString());
   }
}

در اینجا از ValueField.HtmlFieldName که در نکته‌ی قبلی معرفی BlazorHtmlField به آن اشاره شد، جهت یافتن نام واقعی فیلد ورودی، استفاده شده‌است.

نظرات مطالب
نوشتن Middleware سفارشی در ASP.NET Core

یک نکته‌ی تکمیلی: کار با اینترفیس IMiddleware جهت تعریف میان‌افزارهای سفارشی

اگر امروز قصد تعریف میان‌افزارهای سفارشی را دارید، بهتر است از روش باز public async Task Invoke(HttpContext context) که در این مطلب معرفی شد، دیگر استفاده نکنید؛ چون مهم‌ترین محدودیت‌های آن، داشتن طول عمر Singleton غیرقابل تغییر و همچنین عدم امکان پیاده سازی اینترفیس IDisposable در آن جهت پاکسازی خودکار منابع است. امروز روش توصیه شده‌، استفاده از اینترفیس IMiddleware است. در این حالت متد Task Invoke فوق، به متد مشخص و ثابت Task InvokeAsync(HttpContext context, RequestDelegate next) تغییر می‌کند. چون در این حالت دیگر نمی‌توان پارامترهای این متد مشخص را مانند قبل که اینترفیسی را پیاده سازی نمی‌کرد، به صورت پویا کم و زیاد کرد، می‌توان سرویس‌های مدنظر را به سازنده‌ی کلاس، تزریق کرد. به همین جهت نیاز است، آن‌را به نحو زیر به سیستم تزریق وابستگی‌ها معرفی کرد:

builder.Services.AddTransient<MyNewMiddleware>();

این الزام به تعریف آن به صورت یک سرویس رسمی، مزیت‌های زیر را به همراه دارد:

الف) می‌توان طول عمری، غیر از Singleton را هم در صورت نیاز، تعریف کرد (و مشکل کار با سرویس‌هایی با طول عمرهای غیر از Singleton کمتر می‌شود).

ب) چون طول عمر این میان‌افزار اکنون توسط سیستم تزریق وابستگی‌ها مدیریت می‌شود، اگر این میان‌افزار اینترفیس IDisposable را پیاده سازی کند، کار پاکسازی منابع آن خودکار خواهد شد.

نکته 1: روش معرفی آن به سیستم تزریق وابستگی‌ها، به صورت Concrete type است؛ یعنی اصل کلاس باید معرفی شود (مانند سطر فوق) و نه اینکه به صورت متداول زیر به همراه ذکر اینترفیس IMiddleware باشد:

builder.Services.AddTransient<IMiddleware, MyNewMiddleware>();

مابقی کار با آن، با میان‌افزارهای متداول، تفاوتی ندارد. یعنی قسمت UseMiddleware آن یکی است:

app.UseMiddleware<MyNewMiddleware>();

بنابراین این روش نسبت به روش متداول قبلی، دو تفاوت پیاده سازی اینترفیس مشخص IMiddleware و ثبت کلاس آن به صورت یک سرویس رسمی را دارد؛ مابقی نکات آن، مانند قبل است.

نکته 2: اگر از Scrutor برای ثبت خودکار سرویس‌های برنامه استفاده می‌کنید، روش ثبت خودکار اینگونه سرویس‌ها به صورت زیر و با استفاده از متد ()AsSelf است:

services.Scan(scan => scan.FromAssembliesOf(typeof(IDataSeedersRunner))
            .AddClasses(classes => classes.Where(type =>
            {
                var allInterfaces = type.GetInterfaces();

                return allInterfaces.Contains(typeof(IMiddleware)) && 
                       allInterfaces.Contains(typeof(ISingletonService));
            }))
            .AsSelf()
            .WithSingletonLifetime());

در این مثال، تمام IMiddleware هایی که با نشانگر ISingletonService هم مزین شده‌اند، یافت شده و به صورت Concrete type هایی، با طول عمر Singleton، به سیستم تزریق وابستگی‌ها اضافه می‌شوند.