مطالب
کلاس کمکی جهت مشاهده آیتم های موجود در حافظه کش و حذف آنها
مواقع بسیاری پیش می‌آید که در زمان کار با یک نرم افزار تحت وب زمان اشکال زدایی پیش می‌آید که به دلیل موجود بودن داده در حافظه کش برنامه نویس نمی‌تواند داده‌های واقعی را ببیند و داده‌های موجود در حافظه کش را مشاهده می‌کند (بیشتر مواقعی که از طریق بانک اطلاعاتی مستقیما اقدام به حذف و اضافه داده می‌کنیم) در این بخش یک کلاس آماده کرده ام که همیشه خودم در نرم افزار هایم استفاده می‌کنم.

شما می‌توانید این کلاس را به یک GridView یا کنترل‌های دیگر بایند کرده و کلید‌های موجود در حافظه کش را مشاهده کنید، و در صورتی که خواستید یک کلید خاص را از حافظه کش حذف نمایید (البته این کلاس بیشتر برای مدیر نرم فزار کاربرد دارد).

  می‌توانید فایل مورد نظر را از طریق لینک کلاس کمکی جهت مشاهده آیتم‌های موجود در حافظه کش و حذف آنها دانلود نمایید.
در کلاس زیر هر کدام از قسمت‌ها را شرح می‌دهیم.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web;
using System.Web.Caching;

namespace PWS.BLL
{
    /// <summary>
    /// کلاس  آیتم‌های حافظه کش
    /// </summary>
    [DataObject(true)]
    public class CacheItems
    {
#region Constructors (2) 

        /// <summary>
        /// سازنده اصلی
        /// </summary>
        /// <param name="cacheItem">عنوان آیتم ذخیره شده در حافظه کش</param>
        public CacheItems(String cacheItem)
        {
            CacheItem = cacheItem;
        }

        /// <summary>
        /// سازنده پیش فرض
        /// </summary>
        public CacheItems(){}

#endregion Constructors 

#region Properties (2) 

        /// <summary>
        /// کش کانتکست جاری
        /// </summary>
        /// <value>
        /// The cache.
        /// </value>
        private static Cache Cache
        {
            get {return HttpContext.Current.Cache; }
        }

        /// <summary>
        /// عنوان آیتم ذخیره شده در حافظه کش
        /// </summary>
        public String CacheItem{ get; set;}

#endregion Properties 

#region Methods (4) 

// Public Methods (3) 

        /// <summary>
        /// لیست تمام آیتم‌های ذخیره شده در حافظه کش
        /// </summary>
        /// <returns></returns>
        public List<CacheItems> GetCaches()
        {
            var items = new List<CacheItems>();
            //بازیابی کل کلید‌های موجود در حافظه کش و اضافه کردن آن به لیست مربوطه
            var enumerator = Cache.GetEnumerator();
            while (enumerator.MoveNext())
            {
                 items.Add(new CacheItems(enumerator.Key.ToString()));
            }
            return items;
        }

        /// <summary>
        /// حذف آیتم جاری از حافظه کش
        /// </summary>
        public void RemoveItemFromCache()
        {
            RemoveItemFromCache(CacheItem);
        }

        /// <summary>
        /// حذف کردن یک آیتم از حافظه کش
        /// </summary>
        /// <param name="key">کلید ذخیره شده در حافظه کش</param>
        public static void RemoveItemFromCache(string key)
        {
            PurgeCacheItems(key);
        }
// Private Methods (1) 

        /// <summary>
        /// حذف کردن یک ایتم از حافظه کش با پشوند وارد شده
        /// </summary>
        /// <param name="prefix">پیشوندی از کلید موجود در حافظه کش</param>
        private static void PurgeCacheItems(String prefix)
        {
            prefix = prefix.ToLower();
            var itemsToRemove = new List<String>();
           //لیست آیتم‌های موجود در حافظه کش
            var enumerator = Cache.GetEnumerator();
            while (enumerator.MoveNext())
            {
//در صورتی که کلید مورد نظر با پارامتر وارد شده شروع شده باشد آن را به یک لیست اضافه می‌کنیم
 if (enumerator.Key.ToString().ToLower().StartsWith(prefix)) itemsToRemove.Add(enumerator.Key.ToString()); } //لیست مورد نظر را پیمایش کرده و گزینه‌های آن را از حافظه کش حذف می‌کنیم foreach (var itemToRemove in itemsToRemove) Cache.Remove(itemToRemove); } #endregion Methods  } }
موفق وموید باشید
مطالب
آشنایی با NHibernate - قسمت نهم

استفاده از Log4Net جهت ثبت خروجی‌های SQL حاصل از NHibernate

هنگام استفاده از NHibernate، پس از افزودن ارجاعات لازم به اسمبلی‌های مورد نیاز آن به برنامه، یکی از اسمبلی‌هایی که به پوشه build برنامه به صورت خودکار کپی می‌شود، فایل log4net.dll است (حتی اگر ارجاعی را به آن اضافه نکرده باشیم) که جهت ثبت وقایع مرتبط با NHibernate مورد استفاده قرار می‌گیرد. خوب اگر مجبوریم که این وابستگی کتابخانه NHibernate را نیز در پروژه‌های خود داشته باشیم، چرا از آن استفاده نکنیم؟!
شرح مفصل استفاده از این کتابخانه سورس باز را در سایت اصلی آن می‌توان مشاهده کرد:


برای اینکه از این کتابخانه در برنامه خود جهت ثبت عبارات SQL تولیدی توسط NHibernate استفاده کنیم، باید مراحل زیر طی شوند:
الف) ارجاعی را به اسمبلی log4net.dll اضافه نمائید (کنار اسمبلی NHibernate در پوشه build برنامه باید موجود باشد)
ب) فایل app.config برنامه را (برنامه ویندوزی) به صورت زیر ویرایش کرده و چند سطر مربوطه را اضافه نمائید (در مورد برنامه‌های وب هم به همین شکل است. configSections فایل web.config تنظیم شده و سپس تنظیمات log4net را قبل از بسته شدن تگ configuration اضافه نمائید ) :

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net"
type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>

<connectionStrings>
<!--NHSessionManager-->
<add name="DbConnectionString"
connectionString="Data Source=(local);Initial Catalog=HelloNHibernate;Integrated Security = true"/>
</connectionStrings>

<log4net>
<appender name="rollingFile"
type="log4net.Appender.RollingFileAppender,log4net" >
<param name="File" value="NHibernate_Log.txt" />
<param name="AppendToFile" value="true" />
<param name="DatePattern" value="yyyy.MM.dd" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="500KB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout,log4net">
<conversionPattern value="%d %p %m%n" />
</layout>
</appender>
<logger name="NHibernate.SQL">
<level value="ALL" />
<appender-ref ref="rollingFile" />
</logger>
</log4net>

</configuration>
ج) سپس باید فراخوانی زیر نیز در ابتدای کار برنامه صورت گیرد:

log4net.Config.XmlConfigurator.Configure();
در یک برنامه ASP.Net این فراخوانی باید در Application_Start فایل Global.asax.cs صورت گیرد.
یا در یک برنامه از نوع WinForms تنها کافی است سطر زیر را به فایل AssemblyInfo.cs برنامه اضافه کرد:

// Configure log4net using the .config file
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
یا این سطر را به فایل Global.asax.cs یک برنامه ASP.Net نیز می‌توان اضافه کرد. Watch=true آن، با کمک FileSystemWatcher تغییرات فایل کانفیگ را تحت نظر داشته و هر بار که تغییر کند بلافاصله، تغییرات جدید را اعمال خواهد کرد.

د) هنگام استفاده از کتابخانه Fluent NHibernate حتما باید متد ShowSql در جایی که دیتابیس برنامه را تنظیم می‌کنیم (Fluently.Configure().Database) ذکر گردد (که نمونه آن‌را در مثال‌های قسمت‌های قبل ملاحظه‌ کرده‌اید).

توضیحاتی در مورد تنظیمات فوق:
configSections حتما باید در ابتدای فایل app.config‌ ذکر شود و گرنه برنامه کار نخواهد کرد.
سپس کانکشن استرینگ مورد استفاده در قسمت کانفیگ برنامه ذکر شده است.
در ادامه تنظیمات استاندارد مربوط به log4net را مشاهده می‌کنید.
در تنظیمات این کتابخانه، appender مشخص کننده محل ثبت وقایع است. زمانیکه که از RollingFileAppender استفاده کنیم، اطلاعات را در یک سری فایل ذخیره خواهد کرد (امکان ثبت وقایع در EventLog ویندوز، ارسال از طریق ایمیل و غیره نیز میسر است که جهت توضیحات بیشتر می‌توان به مستندات آن رجوع نمود).
سپس نام فایلی که اطلاعات وقایع در آن ثبت خواهند شد ذکر شده است (برای مثال NHibernate_Log.txt)، در ادامه مشخص گردیده که اطلاعات باید هر بار به این فایل Append و اضافه شوند. سطرهای بعدی مشخص می‌کنند که هر زمانیکه این لاگ فایل به 10 مگابایت رسید، یک فایل جدید تولید کن و هر بار 10 فایل آخر را نگه دار و مابقی فایل‌های قدیمی را حذف کن.
در قسمت PatternLayout مشخصات می‌کنیم که خروجی ثبت شده با چه فرمتی باشد. برای مثال یک سطر خروجی مطابق با تنظیمات فوق به شکل زیر خواهد بود:

2009-10-18 20:03:54,187 DEBUG INSERT INTO [Student] (Name) VALUES (@p0); select SCOPE_IDENTITY();@p0 = 'Vahid'
در قسمت Logger یک نام دلخواه ذکر شده و میزان اطلاعاتی که باید درج شود، از طریق مقدار level مورد نظر، قابل تنظیم است که می‌تواند یکی از مقادیر ALL ،DEBUG ،INFO ،WARN ،ERROR ،FATAL و یا OFF باشد. اینجا level در نظر گرفته شده ALL است که تمامی اطلاعات مرتبط با اعمال پشت صحنه NHibernate را لاگ خواهد کرد.
توسط appender-ref آن appender ایی را که در ابتدای کار تعریف و تنظیم کردیم، مشخص خواهیم کرد.

اگر هم با برنامه نویسی بخواهیم اطلاعاتی را به این لاگ فایل اضافه کنیم تنها کافی است بنویسیم:

log4net.LogManager.GetLogger("NHibernate.SQL").Info("test1");

اطلاعات بیشتر

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

مطالب
کار با Docker بر روی ویندوز - قسمت اول - Container چیست؟
نصب بسیاری از نرم افزارها، کاری مشکل است

فرض کنید می‌خواهید یک فایل ویدیویی با قالب m4v را بر روی تلویزیون خود نمایش دهید؛ اما تلویزیون شما تنها از فایل‌های mp4، پشتیبانی می‌کند. برای رفع این مشکل نیاز به یک نرم افزار تبدیل کننده‌ی فرمت‌های ویدیویی را داریم و یکی از قوی‌ترین‌های آن‌ها، FFmpeg است. اگر به سایت آن مراجعه کنید، لینک دانلود آن به یک فایل tar.bz2 ختم می‌شود که حاوی سورس آن است! هرچند در قسمتی از آن، فایل‌های نهایی کامپایل شده‌ی مخصوص سیستم عامل‌های مختلف را نیز می‌توانید پیدا کنید، اما باز هم با انبوهی از لینک‌ها مواجه خواهید شد که دقیقا مشخص نیست کدام را باید دریافت کرد و آیا نگارش دریافت شده، با سیستم عامل فعلی سازگار است یا خیر.
همانطور که مشاهده می‌کنید، هنوز هم شروع به کار با نرم افزارهای مختلف برای بسیاری از کاربران، کاری مشکل و طاقت‌فرسا است. در اینجا شاید این سؤال مطرح شود که این موضوع چه ربطی به Docker (Docker) و کانتینرها (Containers) دارد؟ تمام هیاهویی که پیرامون Docker ایجاد شده‌است، در اصل جهت ساده سازی نصب، راه اندازی و تعامل با نرم افزارهای مختلف است.


چالش‌های پیش روی یافتن نرم افزارهای مناسب


این روزها بیشتر نرم افزارهای مورد نیاز خود را از اینترنت تهیه می‌کنیم. اولین مرحله‌ی آن و اولین چالشی که در اینجا وجود دارد، یافتن نرم افزاری با مشخصات مدنظر است. برای نمونه حتی اگر با FFmpeg آشنا نیز باشید، به سادگی مشخص نیست که برای سیستم عامل و معماری خاص پردازنده‌ی آن، دقیقا کدام نگارش آن‌را از چه آدرسی می‌توان دریافت کرد. پس از یافتن نرم افزار و نگارش مدنظر، مرحله‌ی بعد، استخراج محتویات آن از یک فایل zip و یا اجرای برنامه‌ی نصاب آن است و مرحله‌ی آخر، اجرای این برنامه می‌باشد.
بنابراین اولین چالش، یافتن محلی برای دریافت نرم افزار است:
-  این روزها برای بعضی از سکوهای کاری، App Storeهایی وجود دارند که می‌توان از آنجا شروع کرد؛ اما چنین قابلیتی برای تمام سکوهای کاری پیش بینی نشده‌است.
- در لینوکس قابلیت دیگری به نام Package manager وجود دارد که کار یافتن و نصب نرم افزارها را ساده می‌کند؛ اما گاهی از اوقات اطلاعات آن، آنچنان به روز نیست. همچنین اگر بسته‌ای برای توزیع خاصی از لینوکس وجود داشته باشد، الزاما به این معنا نیست که این بسته، قابلیت استفاده‌ی در سایر توزیع‌های لینوکس را نیز به همراه دارد. در ویندوز نیز وضعیت مشخص است! فاقد یک Package manager توکار و استاندارد است. هرچند یک App Store برای آن از طرف مایکروسافت ارائه شده‌است، اما آنچنان محبوبیتی پیدا نکرده‌است.
- و روش متداول دیگری که وجود دارد، مراجعه‌ی مستقیم به سایت اصلی سازنده‌ی نرم افزار است.

- علاوه بر این‌ها داشتن یک سری متادیتا و آمار نیز در مورد نرم افزارها بسیار مفید هستند تا بتوانند در مورد تصمیم به استفاده‌، یا عدم استفاده‌ی از نرم افزار، راهنمای کاربران باشند؛ مانند میزان محبوبیت، تعداد بار دریافت، تعداد مشکلاتی که کاربران با آن داشته‌اند و آخرین باری که نرم افزار به روز شده‌است. اما با توجه به پراکندگی روش‌های دریافت نرم افزار که ذکر شدند، عموما یک چنین آمارهایی را مشاهده نمی‌کنیم.
- چالش دیگر، مشکل سخت اطمینان کردن به روش‌های مختلف توزیع نرم افزارها است. آیا سایتی که این نرم افزار را ارائه می‌دهد، واقعا مرتبط با نویسنده‌ی اصلی آن است؟ همچنین آیا خود نرم افزار مشکلات امنیتی را به همراه ندارد؟ چه کاری را انجام می‌دهد؟
- مشکل بعدی، در دسترس بودن سایت توزیع کننده‌ی نرم افزار است. آیا زمانیکه به برنامه‌ای نیاز داریم، پهنای باند سایت توزیع کننده‌ی آن تمام نشده‌است و می‌توان به آن دسترسی داشت؟
- چالش دیگر، چگونگی پرداخت مبلغی برای دسترسی به نرم افزار است. به نظر تا به اینجا تنها App Storeها موفق شده‌اند روشی یک دست را برای خرید برنامه‌ها و همچنین ارائه‌ی مجوزی برای استفاده‌ی از آن‌ها، ارائه دهند.


چالش‌های پیش روی نصب نرم افزارها

زمانیکه به مرحله‌ی نصب نرم افزار می‌رسیم، هر نرم افزار، روش نصب و تنظیمات آغازین خاص خودش را دارد.
- اولین چالش پس از دریافت نرم افزار، بررسی سازگاری آن با سیستم عامل و پردازنده‌ی فعلی است. شاید این مسایل برای توسعه دهندگان نرم افزارها پیش‌پا افتاده به نظر برسند، اما برای عموم کاربران، چالشی جدی به شما می‌روند.
- پس از مشخص شدن سازگاری یک نرم افزار با سیستم فعلی، قالب ارائه‌ی آن نرم افزار نیز می‌توان مشکل‌زا باشد. بعضی از برنامه‌ها صرفا از طریق سورس کد منتشر می‌شوند. بعضی از آن‌ها توسط یک فایل exe متکی به خود ارائه می‌شوند و بعضی دیگر به همراه یک فایل exe و تعدادی dll به همراه آن‌ها. گاهی از اوقات این برنامه‌ها نیاز به نصب جداگانه‌ی NET Runtime. و یا Java Runtime را برای اجرا دارند و یا وابستگی آن‌ها صرفا به نگارش خاصی از این کتابخانه‌ها و فریم ورک‌های ثالث است. هرچند اگر برنامه‌ای به همراه بسته‌ی نصاب آن باشد، به احتمال زیاد این وابستگی‌ها را نیز نصب می‌کند؛ اما تمام برنامه‌ها اینگونه ارائه نمی‌شوند. به علاوه خیلی‌ها علاقه‌ای به کار با برنامه‌های نصاب ندارند و از ایجاد تغییرات بسیاری که آن‌ها در سیستم ایجاد می‌کنند، خشنود نیستند.
- پس از نصب نرم افزار، مشکل بعدی، نحوه‌ی به روز رسانی آن‌ها است. چگونه باید اینکار انجام شود؟ (تمام مراحل و چالش‌هایی را که تاکنون بررسی کردیم، یکبار دیگر از ابتدا مرور کنید!)

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


چالش‌های پیش روی کار با نرم افزارها

مرحله‌ی بعد، نیاز به مستندات کافی برای کار با برنامه است. این مستندات را از کجا می‌توان تهیه کرد؟ آخرین باری که به روز شده، چه زمانی بوده‌است؟ بسیاری از اوقات بین مستندات تهیه شده و آخرین نگارش نرم افزار، ناسازگاری وجود دارد و به سختی قابل استفاده‌است. آیا نیاز است برنامه را به PATH اضافه کرد؟ آیا نیاز است به صورت سرویس نصب شود؟ اگر بله، چگونه باید این مراحل را انجام داد؟ مجوز کار کردن با آن‌ها چگونه است؟
مشکل مهم دیگری که حین کار با نرم افزارها، در حالت متداول آن‌ها وجود دارد، دسترسی کامل آن‌ها به تمام اجزای سیستم و شبکه است و درون یک sandbox (قرنطینه) امنیتی اجرا نمی‌شوند.
مشکل بعدی، به روز رسانی اجزای ثالث سیستم و یا حتی خود سیستم عامل، مانند به روز رسانی OpenSSL نصب شده و پس از آن، از کار افتادن برنامه‌ای خاص است که وابستگی به نگارشی خاص از این کتابخانه را دارد.


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

خوب، تا اینجا دریافتیم که مدیریت توزیع، نصب و استفاده‌ی از برنامه‌ها، کار ساده‌ای نیست. اما این‌ها چه ارتباطی با Docker دارند؟ در بسیاری از اوقات، زمانیکه صحبت از Docker می‌شود، تصور بسیاری از آن، ارائه‌ی جایگزینی برای ماشین‌های مجازی است. اما ... اینگونه نیست. کانتینرها در مورد نرم افزارها هستند. برای مثال در آینده در مورد ایمیج‌های (Images) کانتینرها بیشتر بحث خواهیم کرد. این ایمیج‌ها در اصل یک بسته‌ی حاوی برنامه‌ها هستند. بنابراین بیشتر شبیه به فایل zip ای است که از یک وب سایت دریافت می‌کنیم (در قسمت یافتن نرم افزار).


یک کانتینر (Container) چیست؟

برای درک بهتر مواردی که تاکنون بحث شدند و همچنین بررسی مفهوم Containers، ابتدا MonogoDB را به صورت معمول نصب می‌کنیم. سپس نحوه‌ی نصب آن‌را درون یک Container بررسی خواهیم کرد. البته هدف اصلی در اینجا، بررسی مفهومی این مراحل و مقایسه‌ی آن‌ها با هم هستند و در قسمت‌های بعدی کار نصب و استفاده‌ی از Docker را قدم به قدم بررسی خواهیم کرد.
 
مراحل نصب محلی MongoDB به صورت متداول:
- ابتدا برای مثال به سایت گوگل مراجعه کرده و mongodb را جستجو می‌کنیم تا بتوانیم به سایت اصلی و محل دریافت بسته‌ی آن، هدایت شویم.
- پس از ورود به سایت mongodb، در بالای صفحه اصلی آن، لینک به صفحه‌ی دریافت بسته‌ی mongodb را می‌توان مشاهده کرد.
- با انتخاب آن، به صفحه‌ی دریافت بسته‌ی mongodb بر اساس سیستم عامل‌های مختلفی هدایت می‌شویم. برای مثال در ویندوز، بسته‌ی msi آن‌را دریافت می‌کنیم.
- به نظر می‌رسد که بسته‌ی نصاب msi آن تمام کارهای لازم برای راه اندازی اولیه‌ی mongodb را انجام می‌دهد. به همین جهت آن‌را اجرا کرده و پس از چندبار انتخاب گزینه‌ی next، نصب آن به پایان می‌رسد.
- پس از پایان نصب، ابتدا به کنسول service.msc ویندوز مراجعه می‌کنیم تا مطمئن شویم که سرویس آن، توسط نصاب msi نصب شده‌است یا خیر؟ و ... خیر! این نصاب، سرویس آن‌را نصب نکرده‌است.
- به همین جهت به مستندات نصب آن در سایت mongodb مراجعه می‌کنیم (لینک Installation instructions در همان صفحه‌ی دریافت بسته‌ی msi وجود دارد). پس از پایان مراحل نصب، عنوان کرده‌است که باید دستور md \data\db را اجرا کنید تا مسیر پیش فرض اطلاعات آن به صورت دستی ایجاد شود. اما ... این مسیر دقیقا به کجا اشاره می‌کند؟ چون شبیه به مسیرهای ویندوزی نیست.
- در ادامه برای آزمایش، به پوشه‌ی program files ویندوز رفته، monogodb نصب شده را یافته و سپس فایل mongod.exe را از طریق خط فرمان اجرا می‌کنیم (برنامه‌ی سرور). اگر این کار را انجام دهیم، این پروسه با نمایش خطای یافت نشدن مسیر c:\data\db، بلافاصله خاتمه پیدا می‌کند. به همین جهت در همین مسیری که در خط فرمان قرار داریم (جائیکه فایل mongod.exe قرار دارد)، دستور md \data\db را اجرا می‌کنیم. اجرای این دستور در این حالت، همان پوشه‌ی c:\data\db را ایجاد می‌کند. نکته‌ای که شاید خیلی‌ها با آن آشنایی نداشته باشند.
- اکنون اگر مجددا فایل mongod.exe را اجرا کنیم، اجرای آن موفقیت آمیز خواهد بود و پیام منتظر دریافت اتصالات بودن از طریق پورت 27017 را نمایش می‌دهد.
- مرحله‌ی بعد، اجرای فایل mongo.exe است تا به این دیتابیس سرور در حال اجرا متصل شویم (برنامه‌ی کلاینت). در اینجا برای مثال می‌توان دستور show dbs را اجرا کرد تا لیست بانک‌های اطلاعاتی آن‌را نمایش دهد.
 

مراحل نصب MongoDB به صورت Container توسط Docker:
- ابتدا برای مثال به سایت گوگل مراجعه کرده و اینبار mongodb docker را جستجو می‌کنیم تا بتوانیم به محل دریافت image آن هدایت شویم. با ورود به آن، در بالای صفحه عنوان شده‌است که official repository است که سبب اطمینان از بسته‌ی ارائه شده‌ی توسط آن می‌شود. بنابراین در اینجا بجای مراجعه به سایت متکی به خود mongodb، به docker hub برای دریافت آن مراجعه کرده‌ایم. در اینجا با جستجوی یک برنامه، متادیتا و اطلاعات آماری بسیاری را نیز می‌توان در مورد برنامه‌های مختلف، مشاهده کرد که در سایت متکی به خود نرم افزارهای مختلف، در دسترس نیستند. همچنین در اینجا اگر بر روی برگه‌ی Tags یک مخزن کلیک کنید، مشاهده می‌کنید که تمام فایل‌های موجود در آن توسط docker hub از لحاظ مشکلات امنیتی پیشتر اسکن شده‌اند و گزارش آن‌ها قابل مشاهده‌است. علاوه بر این‌ها docker hub به همراه یک docker store برای برنامه‌های غیر رایگان نیز هست و این مورد فرآیند کار با نرم افزارهای تجاری را یک دست می‌کند.
- مرحله‌ی بعد، دریافت یک کپی از mongodb از docker hub است. اینبار بجای دریافت مستقیم یک فایل zip یا msi، از دستور docker pull mongo استفاده می‌شود که یک image را در نهایت دریافت می‌کند. این image، حاوی برنامه‌ی مدنظر و تمام وابستگی‌های آن است.
- پس از دریافت image، مرحله‌ی بعد، اجرای mongodb به همراه آن است. در حالت متداول، ابتدا نرم افزار داخل فایل zip یا msi استخراج شده و سپس بر روی سیستم اجرا می‌شوند، اما در اینجا مفهوم معادل نصب نرم افزار دریافت شده‌ی از بسته‌ی zip همراه آن، یک container است. یک container دقیقا مانند یک نرم افزار از پیش نصب شده، عمل می‌کند و معادل اجرای فایل exe مانگو دی بی در اینجا، اجرای container آن است. بنابراین docker، از image دریافت شده، یک container را ایجاد می‌کند که دقیقا معادل یک نرم افزار از پیش نصب شده، رفتار خواهد کرد.
- پس از دریافت image، جهت اجرای آن به عنوان یک container، برای استفاده از نرم افزاری که دریافت کرده‌ایم، تنها یک دستور است که باید با آن آشنا باشیم: docker run mongo. این دستور را در همان صفحه‌ی docker hub مربوطه نیز می‌توانید مشاهده کنید. پس از اجرای این دستور، دقیقا همان خروجی و پیام منتظر دریافت اتصالات بودن از طریق پورت 27017 را مشاهده خواهیم کرد. برای اجرای کلاینت آن نیز دستور docker exec -it 27 mongo را می‌توان اجرا کرد. docker exec کار اجرای چندباره‌ی یک نرم افزار نصب شده را انجام می‌دهد.
این فرآیند در مورد تمام containerها یکی است و به این ترتیب به ازای هر نرم افزار مختلف، شاهد روش نصب متفاوتی نخواهیم بود.
- اجرای دستور docker stop نیز سبب خاتمه‌ی تمام این‌ها می‌شود.


در این تصویر مقایسه‌ای را بین مراحل متداول یافتن، دریافت، نصب و اجرای برنامه‌ها را در دو حالت متداول و همچنین استفاده‌ی از docker، مشاهده می‌کنید.

همچنین نکته‌ی جالبی که در مورد docker وجود دارد این است که اگر به task manager ویندوز مراجعه کنیم:


تمام پروسه‌هایی که با job id مساوی 172 در اینجا اجرا شده‌اند، متعلق به docker بوده و آن‌ها دقیقا مانند یک پروسه‌ی معمولی سیستم عامل جاری، در کنار سایر پروسه‌های موجود، اجرا می‌شوند. بنابراین برنامه‌ای که از طریق docker اجرا می‌شود، هیچ تفاوتی با اجرای متداول آن بر روی سیستم عامل، از طریق روش مراجعه‌ی مستقیم به فایل exe مرتبط و اجرای مستقیم آن ندارد. همانطور که پیش‌تر نیز عنوان شد، containerها در مورد نرم افزارها هستند و نه مجازی سازی و یک container در حال اجرا، حاوی تعدادی برنامه‌ی در حال اجرای بر روی سیستم عامل جاری، در کنار سایر برنامه‌های آن می‌باشد.
البته containers به همراه ایزوله سازی‌های بسیاری اجرا می‌شوند. برای مثال به روز رسانی یک کتابخانه‌ی ثالث بر روی سیستم عامل، سبب از کار افتادن برنامه‌ی اجرای شده‌ی توسط یک container نمی‌شود.


در قسمت بعد، نحوه‌ی نصب Docker را بر روی ویندوز، بررسی می‌کنیم.
مطالب
چک لیست تهیه یک برنامه ASP.NET MVC
خلاصه نکاتی که من در تهیه یک برنامه ASP.NET MVC رعایت می‌کنم:

- استفاده از T4MVC اجباری است. به هیچ عنوان نباید از رشته‌ها برای مشخص سازی نام کنترلرها یا اکشن متدها در قسمت‌های مختلف برنامه استفاده شود.
- تا حد امکان از ViewBag ، ViewData و امثال آن استفاده نشده و به ازای هر View یک مدل متناظر (ViewModel) ایجاد شود.
- فایل پروژه برنامه توسط یک ادیتور متنی ویرایش شده و MvcBuildViews آن به True تنظیم شود.
- مدل‌های متناظر با جداول بانک اطلاعاتی نباید مستقیما در Viewهای برنامه استفاده شوند.
- پوشه Models، از پروژه اصلی حذف شود. یک پروژه class library جدید به نام MyProjectName.Models برای نگهداری ViewModels ایجاد گردد.
- یک پروژه Class library دیگر به نام MyProjectName.DomainClasses برای نگهداری کلاس‌های متناظر با جداول بانک اطلاعاتی ایجاد شود.
- از سیستم minification و bundling، برای یکی سازی اسکریپت‌ها و CSSهای برنامه استفاده شود.
- قسمت custom errors فایل web.config برنامه به نحو صحیحی مقدار دهی شود.
- تمام فرم‌های عمومی برنامه باید دارای AntiForgeryToken باشند.
- تمام فرم‌های عمومی برنامه باید captcha داشته باشند.
- پوشه‌های Content و Scripts از سیستم مسیریابی تعریف شده در Global.asax خارج شوند.
- MvcHandler.DisableMvcResponseHeader = true به Application_Start اضافه شود.
- اگر فقط از Razor به عنوان ViewEngine استفاده می‌شود، در Application_Start، باید سایر ViewEngineهای مورد استفاده، حذف شوند.
- فیلتر پیش فرض مدیریت خطاها حذف و بجای آن از ELMAH استفاده شود.
- در web.config، مقادیر executionTimeout و maxRequestLength مرتبط با httpRuntime تنظیم شوند. همچنین enableVersionHeader آن نیز خاموش گردد.
- استفاده از سشن‌ها کلا باید حذف شود. ماژول توکار آن از قسمت httpModules حذف گردد تا پردازش موازی صفحات فعال گردد. (سشن مربوط است به دوران ASP کلاسیک دهه نود و هیچ نیازی به استفاده از آن در MVC نیست)
- در هیچ کنترلری نباید جزئیات پیاده سازی متدی مشاهده شود. تمام پیاده سازی‌ها باید به لایه سرویس‌های مختلف برنامه منتقل و از طریق تزریق وابستگی‌ها در دسترس باشند.
- اگر نیاز به مشخص سازی آدرسی در سایت است (خصوصا در اسکریپت‌ها) باید از Url.Action استفاده شود و نه رشته‌ها.
- بهتر است بومی سازی برنامه از روز اول آن درنظر گرفته شده و تمام عبارات مورد استفاده در فایل‌های Resource درج شوند.
- برای مدیریت ساده‌تر بسته‌های مورد استفاده (وابستگی‌های برنامه) بهتر است از NuGet استفاده شود.
- از یک ماژول HTTP compression مستقل و با کیفیت استفاده شود (برای سازگاری بهتر با نگارش‌های مختلف IIS).
- برای معرفی HTTP modules و سادگی تعریف و فعال سازی آن‌ها در انواع و اقسام IISها بهتر است از کتابخانه WebActivator استفاده شود.
- امکان دوبار کلیک کردن بر روی تمام دکمه‌ها نباید وجود داشته باشد.
- از هش‌های ترکیبی استفاده شود. مستقیما از MD5 یا SHA1 استفاده نشود.
- با اسکریپت‌های anti IE6,7، این مرورگرها به رحمت ایزدی واصل شوند.
- اگر کاربری JavaScript را در مرورگر خود غیرفعال کرد، نباید بتواند از سایت استفاده کند.
- کلیه تغییرات تنظیمات و محتوای مهم سایت باید برای مدیر سایت بلافاصله ایمیل شوند.
- یک سری کارهای متداول مانند تهیه فایل‌های favicon.ico، apple-touch-icon-XxY.png، crossdomain.xml، robots.txt و sitemap.xml (ترجیحا پویا) فراموش نشود.
- در web.config و در زمان ارائه، compilation debug=false تنظیم شود.
- در تمام قسمت‌هایی که AlllowHtml فعال شده باید از پاکسازی Html دریافتی جهت مقابله با XSS مطمئن شد.
- جهت سهولت طراحی table less از یک فریم ورک CSS ایی استفاده شود.
- در تمام قسمت‌هایی که فایلی آپلود می‌شود باید بررسی شود فایل‌های نا امن (فایل‌های اجرایی ASP.NET) قابل آپلود نباشند.
- حین کار با بانک‌های اطلاعاتی یا از ORM استفاده شود و یا از کوئری‌های پارامتری.
- هر برنامه ASP.NET باید داری یک Application pool مجزا به همراه تنظیمات حافظه مشخصی باشد.
- تمام صفحات باید عنوان داشته باشند. به همین منظور مقدار دهی پیش فرض آن در فایل ViewStart صورت گیرد.
- در صفحه لاگین سایت، autocomplete خاموش شود.
- تمام deleteهای برنامه باید به HttpPost محدود شوند. تمام گزارشات و نمایش اطلاعات غیرعمومی برنامه به HttpGet محدود شوند.
- تعداد رفت و برگشت‌های به بانک اطلاعاتی در یک صفحه توسط پروفایلرها بررسی شده و اطلاعات عمومی پرمصرف کش شوند.
- در هیچکدام از کلاس‌های ASP.NET MVC نباید از HttpContext مستقیما استفاده شود. کلاس پایه جدید آن باید مورد استفاده قرار گیرد یا از Action Result صحیحی استفاده گردد.
- کش کردن فایل‌های استاتیک درنظر گرفته شود.
- تمام درخواست‌های jQuery Ajax باید بررسی شوند. آیا واقعا مرتبط هستند به سایت جاری و آیا واقعا Ajax ایی هستند.


یک نکته:
امکان تهیه قالب‌های سفارشی VS.NET و لحاظ موارد فوق در آن جهت استفاده‌های بعدی نیز وجود دارد:
Create Reusable Project And Item Templates For Your Development Team 
Write Templates for Visual Studio 2010 
Building a Custom Project Wizard in Visual Studio .NET 

مطالب
انتخاب پویای فیلد ها در LINQ

LINQ یک DLS  بر مبنای .NET  می باشد که برای پرس و جو در منابع داده ای مانند پایگاه‌های داده ، فایل‌های XML و یا لیستی از اشیاء درون حافظه کاربرد دارد.

یکی از بزرگترین مزیت‌های آن Syntax  آسان و خوانا آن می‌باشد.

LINQ  از 2 نوع نمادگذاری پشتیبانی می‌کند:

  • Inline LINQ یا query expressions : 
var result = 
    from product in dbContext.Products
    where product.Category.Name == "Toys"
    where product.Price >= 2.50
    select product.Name;
  • Fluent Syntax : 
var result = dbContext.Products
    .Where(p => p.Category.Name == "Toys" && p.Price >= 250)
    .Select(p => p.Name);

در پرس و چو‌های بالا فیلد‌های مورد نیاز در قسمت Select در زمان Compile شناخته شده هستند . اما گاهی ممکن است فیلد‌های مورد نیاز در زمان اجرا مشخص شوند.

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

این مدل را در نظر داشته باشید :

    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Field1 { get; set; }
        public string Field2 { get; set; }
        public string Field3 { get; set; }


        public static IEnumerable<Student> GetStudentSource()
        {
            for (int i = 0; i < 10; i++)
            {
                yield return new Student
                                 {
                                     Id = i,
                                     Name = "Name " + i,
                                     Field1 = "Field1 " + i,
                                     Field2 = "Field2 " + i,
                                     Field3 = "Field3 " + i
                                 };
            }
        }
    }

ستون‌های کلاس Student  را در رابط کاربری برنامه جهت انتخاب به کاربر نمایش می‌دهیم. سپس کاربر یک یا چند ستون را انتخاب می‌کند که قسمت Select  کوئری برنامه باید  بر اساس فیلد‌های مورد نظر کاربر مشخص شود.

یکی از روش هایی که می‌توان از آن بهره برد استفاده از کتاب خانه Dynamic LINQ معرفی شده در اینجا می باشد.

این کتابخانه جهت سهولت در نصب به کمک NuGet در این آدرس قرار دارد.

فرض بر این است که فیلد‌های انتخاب شده توسط کاربر با "," از یکدیگر جدا شده اند. 

    public class Program
    {
        private static void Main(string[] args)
        {
            System.Console.WriteLine("Specify the desired fields : ");
            string fields = System.Console.ReadLine();
            IEnumerable<Student> students = Student.GetStudentSource();
            IQueryable output = students.AsQueryable().Select(string.Format("new({0})", fields));
            foreach (object item in output)
            {
                System.Console.WriteLine(item);
            }
          
            System.Console.ReadKey();
        }
  
    }

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

این روش مزایا و معایب خودش را دارد ، به عنوان مثال خروجی یک لیست از شیء Student  نیست یا این Select  فقط برای روی یک شیء IQueryable  قابل انجام است.

روش دیگری که می‌توان از آن بهره جست استفاده از یک متد کمکی جهت تولید پویای عبارت Lambda  ورودی Select  می باشد :  

    public  class SelectBuilder <T>
    {
        public static Func<T, T> CreateNewStatement(string fields)
        {
            // input parameter "o"
            var xParameter = Expression.Parameter(typeof(T), "o");


            // new statement "new T()"
            var xNew = Expression.New(typeof(T));

            // create initializers
            var bindings = fields.Split(',').Select(o => o.Trim())
                .Select(o =>
                {

                    // property "Field1"
                    var property = typeof(T).GetProperty(o);

                    // original value "o.Field1"
                    var xOriginal = Expression.Property(xParameter, property);

                    // set value "Field1 = o.Field1"
                    return Expression.Bind(property, xOriginal);
                }
            ).ToList();

            // initialization "new T { Field1 = o.Field1, Field2 = o.Field2 }"
            var xInit = Expression.MemberInit(xNew, bindings);

            // expression "o => new T { Field1 = o.Field1, Field2 = o.Field2 }"
            var lambda = Expression.Lambda<Func<T, T>>(xInit, xParameter);

            // compile to Func<T, T>
            return lambda.Compile();
        }
    }
برای استفاده از متد CreateNewStatement باید اینگونه عمل کرد :  
       IEnumerable<Student> result = students.Select(SelectBuilder<Student>.CreateNewStatement("Field1, Field2")).ToList();

            foreach (Student student in result)
            {
                System.Console.WriteLine(student.Field1);
            }
خروجی یک لیست از Student  می باشد.
 نحوه‌ی کارکرد CreateNewStatement :

ابتدا فیلد‌های انتخابی کاربر که با "," جدا شده اند به ورودی پاس داده می‌شود سپس یک statement  خالی ایجاد می‌شود

o=>new Student()
فیلد‌های ورودی از یکدیگر تفکیک می‌شوند و به کمک Reflection پراپرتی معادل فیلد رشته ای در کلاس Student پیدا می‌شود :  
var property = typeof(T).GetProperty(o);
سپس عبارت Select و تولید شیء جدید بر اساس فیلد‌های ورودی تولید می‌شود و برای استفاده Compile  به Func می‌شود. در نهایت Func  تولید شده به Select پاس داده می‌شود و لیستی از Student  بر مبنای فیلد‌های انتخابی تولید می‌شود. 

دریافت مثال : DynamicSelect.zip 
مطالب
قسمت دوم -- نگاهی دقیق تر به اولین پروژه VC++ (درک مفهوم فایلهای سرآیند و فضای نام ، ویژگیهای زبان ++C و برخی قوانین برنامه نویسی در ++C)
در این قسمت نگاهی دقیق‌تر به فایلهای سرآیند ، فضای نام ، ویژگیهای زبان ++C  و برخی قوانین برنامه نویسی ++C  خواهیم داشت و همچنین در مورد  اولین پروژه توضیحات جامع‌تری ارائه میکنیم .

یک برنامه مجموعه ای از دستورات است که توسط کامپیوتر اجرا میگردد ، برنامه نویسان برای نوشتن این دستورات از زبانهای برنامه نویسی استفاده میکنند ، برخی از این زبانها مسقیما قابل فهم توسط کامپیوتر بوده و برخی نیاز به ترجمه دارند . زبانهای برنامه نویسی را میتوان به سه دسته تقسیم نمود :
1 -  زبانهای ماشین
2 - زبانهای اسمبلی
3 - زبانهای سطح بالا

زبانهای ماشین :
  زبانی که مستقیما و بدون نیاز به ترجمه قابل فهم توسط کامپیوتر می‌باشد . هر پردازنده یا processor  زبان خاص خود را دارد !... در نتیجه تنوع زبان ماشین بستگی به انواع پردازنده‌های موجود دارد و اگر دو کامپیوتر دارای پردازنده‌های یکسان نباشتد ، زبان ماشین آنها با یکدیگر متفاوت و غیر قابل فهم برای دیگری می‌باشد . زبان ماشین وابسته به ماشین یا Machine independent  می‌باشد . تمامی دستورات در این زبان توالی از 0 و 1 می‌باشند . برنامه‌های اولیه را با این زبان می‌نوشتند در نتیجه نوشتن برنامه سخت و احتمال خطا داشتن در آن زیاد بود . ار آنجا که نوشتن برنامه به این زبان سخت و فهم برنامه‌های نوشته شده به آن دشوار بود ، برنامه نویسان به فکر استفاده از حروف بجای دستورات زبان ماشین افتادند ( پیدایش زبان اسمبلی )

زبانهای اسمبلی :
به زبانی که دستورات زبان ماشین را با نمادهای حرفی بیان میکند، زبان اسمبلی  (Assembley Language) می‌گویند . چون این زبان مستقیما قابل فهم برای کامپیوتر نیست باید قبل از اجرا آن را به زبان ماشین ترجمه کرد ، به این مترجم اسمبلر گفته میشود . برنامه‌های نوشته شده به این زبان قابل فهم برای برنامه نویس بود اما از آنجا که به ازای هر دستور زبان ماشین یک دستور زبان اسمبلی داشتیم از حجم برنامه‌ها کاسته نشد ! .. بعلاوه چون زبان اسمبلی همانند زبان ماشین از دستورات پایه ای و سطح پایین استفاده میکرد نوشتن برنامه با این زبان هم سخت و مشکل بود . لذا اهل خرد به فکر ابداع نسلی از زبانهای بهتر بودند (پیدایش زبانهای سطح بالا)

زبانهای سطح بالا :
زبانهای سطح بالا قابل فهم بودند و این امکان را داشتند تا چند دستور زبان ماشین یا اسمبلی را بتوان در قالب یک دستور نوشت ( منظور توابع کتابخانه ای در ++C/C) . پس هم فهم ، هم نوشتن برنامه در این زبانها راحت و هم تعداد خطوط کد کمتر شد . این زبانها به زبانهای برنامه نویسی سطح بالا یا High-Level Programming Language  معروفند . البته برنامه نوشته شده در این زبان نیز برای کامپیوتر قابل فهم نبوده و باید به زبان ماشین ترجمه شوند ، این وظیفه بر عهده کامپایلر می‌باشد . اولین زبانهای برنامه نویسی سطح بالا مانند  FORTRAN ، COBOL ، PASCAL و C  می‌باشند . زبان برنامه نویسی ++C تکامل یافته زبان  میباشد .
هر یک از زبانهای برنامه نویسی سطح بالا یک روش برنامه نویسی را پشتیبانی میکند به طور مثال زبان C  و  PASCAL  از روشهای برنامه نویسی  ساخت یافته ای و پیمانه ای و زبانهای مانند ++C و JAVA  از روش برنامه سازی شی گرا یا Object Oriented Programming یا به اختصار (OOP) استفاده میکنند . زبان ++C  چون زبان C را بطور کامل در بر  دارد پس از هر سه روش برنامه نویسی ساخت یافته و پیمانه ای و شی گرا استفاده میکند .

تا اینجا با تاریخچه ای از زبانها و مراحل تکامل آنها آشنا شدیم . حال ویژگیها و دلایل استفاده از زبان  ++C  را مرور میکنیم :

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

تحلیل  اولین پروژه :



در اولین پروژه  کد فوق را بکار بردیم ، حال به شرح دستورات آن می‌پردازیم .
#include <iostream>
دستوراتی که علامت # پیش از آنها قرار میگیرد ، دستورات راهنمای پیش پردازنده هستند . این خط یک دستور پیش پردازنده است که توسط پیش پردازنده و قبل از شروع کامپایل ، پردازش میشود . این کد فایل iostream  را به برنامه اضافه میکند . کتابخانه استاندارد  ++C  به چندین بخش تقسیم شده است و هر بخش فایل سرآیند خود را دارد . دلیل قرار گرفتن این دستور در ابتدای برنامه این است  که ، پیش از استفاده از هر تابع و فراخوانی کردن آن در برنامه ، کامپایلر لازم است اطلاعاتی در مورد آن تابع داشته باشد .  در خط کد بالا فایل سرآیند  iostream  استفاده نمودیم زیرا شامل توابع مربوط به I/O  ( ورودی / خروجی ) می‌باشد .

int  main()
{
 
   return 0;
}
دستور فوق بخشی از هر برنامه ++C  است ، main  تابع اصلی هر برنامه ++C است که شروع برنامه از آنجا آغاز می‌شود . کلمه  int  در ابتدای این خط ، مشخص میکند که تابع main پس از اجرا و به عنوان مقدار برگشتی (;return 0)  یک عدد صحیح باز می‌گرداند .

std::cout<<"Hello world ...\n";
دستور فوق یک رشته را در خروجی استاندارد که معمولا صفحه نمایش می‌باشد ارسال میکند . std  یک فضای نام است . فضای نام محدوده ای است که چند موجودیت در آن تعریف شده است . مثلا موجودیت cout  در فضای نام std در فایل سرآیند iostream تعریف شده است .


در زبان ++C  هر دستور به ; (سیموکالن) ختم میشود .


 

نظرات مطالب
چک لیست ارتقاء به HTTPS مخصوص یک برنامه‌ی ASP.NET MVC 5x
روشی بهتر برای مدیریت محتوای مخلوط (لینک به HTTP و HTTPS در یک صفحه) در مرورگرهای جدید

در مورد محتوای مخلوط و «اصلاح تمام آدرس‌هایی که پیشتر توسط برنامه تولید شده‌اند» در مطلب فوق نکته‌ای عنوان شده‌است. روش دیگر مدیریت آن که نیازی به اصلاح محتوای صفحات سایت را ندارد، استفاده از هدر امنیتی «Content-Security-Policy: upgrade-insecure-requests» است:
<system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Strict-Transport-Security" value="max-age=16070400; includeSubDomains" />
        <add name="Content-Security-Policy" value="upgrade-insecure-requests;" />
      </customHeaders>
</httpProtocol>
کار این هدر ویژه ، ارتقاء خودکار لینک‌های درج شده‌ی در صفحات به نگارش‌های HTTPS آن‌ها است. برای مثال این تصویری است که پیش از اعمال این هدر جهت درخواست آدرس gravatar یک کاربر ارسال شده‌است و اخطار محتوای مخلوط را می‌دهد:


و این تصویری است که پس از اعمال هدر upgrade-insecure-requests قابل مشاهده‌است که دیگر اخطار محتوای مخلوط را به همراه ندارد:
 

نظرات مطالب
EF Code First #7
سلام
مدیریت روابط n-n در کلاسهای Poco به چه صورت است؟ من یک برنامه 2-tier دارم و برای Entity هایم state گرفتم و سمت کلاینت وضعیت entity‌ها رو مدیریت میکنم و سمت سرور اونا رو روی DBContext اعمال میکنم.
دو کلاس Role و Permission دارم که باهم رابطه n-n دارند؛ حال اگه یک Role  چند Permission داشته باشه و بخوام یکی از اونا رو حذف کنم و یا آپدیت کنم بهم پیغام خطا میده(An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single e ...) و داخل inner exception هم مینویسه ()Violation of PRIMARY KEY constraint 'PK_dbo.SecurityRolePermission'. Cannot insert duplicate key in object 'dbo.SecurityRolePermission'
یعنی در حالتهای آپدیت و حذف هم میخواد insert انجام بده (با ef profiler هم چک کردم)البته فکر کنم تا حدودی معلوم باشه چرا اینکار رو میکنه؛ چون مدیریتی روی state اون کلاس واسط(RolePermission) انجام نمیشه، ممکنه بگید چطوری این مشکل رفع میشه؟
مطالب
استفاده وسیع مایکروسافت از Silverlight در پروژه‌های جدید خود

برخلاف محصولات دهه قبل مایکروسافت، در تعداد زیادی از محصولات جدید سرور آن (خصوصا در کنترل پنل‌های تحت وب این محصولات)، استفاده وسیعی از Silverlight مشاهده می‌شود که لیستی از آن‌ها را در ادامه مشاهده خواهید کرد:

Lync Server 2010 (یا همان communication server قدیم) محصولی است جهت مدیریت ارتباطات پیشرفته: (+)



ویندوز Azure که یکی از محصولات استراتژیک مایکروسافت محسوب می‌شود: (+)



Windows Intune جهت بررسی و به روز رسانی وضعیت شبکه، سرورها و کامپیوترهای آن بکار می‌رود: (+)



System Center برای مدیریت سرورهای مایکروسافت کاربرد دارد: (+)



پروژه جدید Crescent از تیم SQL Server جهت data visualization پیشرفته اطلاعات: (+)



در تکمیل مثال‌های فوق می‌توان به Visual Studio LightSwitch نیز اشاره کرد. هدف اصلی این محصول فراهم آوردن امکان تولید برنامه‌های بانک اطلاعاتی مبتنی بر سیلورلایت جهت افرادی با تجربه‌ی کمتر برنامه نویسی می‌باشد: (+)

این نوع محصولات ویژه سرور عموما جهت Windows platform تهیه می‌شوند و زمانیکه بازه‌ی سیع‌تری از کاربران مدنظر باشند همانند Office web apps کمتر از Silverlight استفاده شده است و اینجا شاید این سؤال مطرح شود که چرا Silverlight ؟ در این مورد مطلب مفصلی را اینجا می‌توانید مطالعه کنید: (+)

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

مشاهده یا دانلود کدهای مقاله


تزریق وابستگی چیست؟

تزریق وابستگی (DI) تکنیکی برای دستیابی به اتصال شل بین اشیاء و همکاران اشیاء و وابستگی‌های بین آنها می‌باشد. یک شیء برای انجام وظایف خود، بجای اینکه اشیاء همکار خود را به صورت مستقیم نمونه سازی کند، یا از ارجاعات استاتیک استفاده نماید، می‌تواند از اشیائی که برایش تامین شده‌است، استفاده کند. در اغلب موارد کلاس‌ها، وابستگی‌های خود را از طریق سازنده‌ی خود درخواست می‌کنند، که به آنها اجازه می‌دهد اصل وابستگی صریح را رعایت کنند (Explicit Dependencies Principle). این روش را «تزریق در سازنده» می‌نامند.
از آنجا که در طراحی کلاس‌ها با استفاده از DI، نمونه سازی مستقیم، توسط کلاس‌ها و به صورت Hard-coded انجام نمی‌گیرد، وابستگی بین اشیاء کم شده و پروژه‌ای با اتصالات شل به دست می‌آید. با این کار اصل وابستگی معکوس (Dependency Inversion Principle) رعایت می‌شود. بر اساس این اصل، ماژول‌های سطح بالا نباید به ماژول‌های سطح پایین خود وابسته باشند؛ بلکه هر دو باید به کلاس‌هایی انتزاعی وابسته باشند. اشیاء بجای ارجاع به پیاده سازی‌های خاص کلاس‌های همکار خود، کلاس‌های انتزاعی، معمولاٌ اینترفیس آنها را درخواست می‌کنند و هنگام نمونه سازی از آنها (داخل متد سازنده) کلاس پیاده سازی شده برایشان تامین می‌شود. خارج کردن وابستگی‌‎های مستقیم از کلاس‌ها و تامین پیاده سازی‌های این اینترفیس‌ها به صورت پارامتر‌هایی برای کلاس‌ها، یک مثال از الگوی طراحی استراتژی (Strategy design pattern) می‌باشد.

در حالتیکه کلاس‌ها به تعداد زیادی کلاس وابستگی داشته باشند و برای اجرا شدن، نیاز به تامین وابستگی‌هایشان داشته باشند، بهتر است یک کلاس اختصاصی، برای نمونه سازی این کلاس‌ها با وابستگی‌های مورد نیاز آنها، در سیستم وجود داشته باشد. این کلاس نمونه ساز را کانتینرIoC، یا کانتینر DI یا به طور خلاصه کانتینر می‌نامند ( Inversion of Control (IoC) ). کانتینر در اصل یک کارخانه می‌باشد که وظیفه‌ی تامین نمونه‌هایی از کلاس‌هایی را که از آن درخواست می‌شود، انجام می‌دهد. اگر یک کلاس تعریف شده، وابستگی به کلاس‌های دیگر داشته باشد و کانتینر برای ارائه وابستگی‌های کلاس تعریف شده تنظیم شده باشد، هر موقع نیاز به یک نمونه از این کلاس وجود داشته باشد، به عنوان بخشی از کار نمونه سازی از کلاس مورد نظر، کلاس‌های وابسته‌ی آن نیز ایجاد می‌شوند (همه‌ی کارهای مربوط به نمونه سازی کلاس خاص و کلاس‌های وابسته به آن توسط کانتینر انجام می‌گیرد). به این ترتیب، می‌توان وابستگی‌های بسیار پیچیده و تو در توی موجود در سیستم را بدون نیاز به هیچگونه نمونه سازی hard-code شده، برای کلاس‌ها فراهم کرد. کانتینرها علاوه بر ایجاد اشیاء و وابستگی‌های موجود در آنها، معمولا طول عمر اشیاء در اپلیکیشن را نیز مدیریت می‌کنند.
ASP.NET Core یک کانتینر بسیار ساده را به نام اینترفیس IServiceProvider  ارائه داده است که به صورت پیش فرض از تزریق وابستگی در سازنده‌ی کلاس‌ها پشتیبانی می‌کند و همچنین ASP.NET برخی از سرویس‌های خود را از طریق DI در دسترس قرار داده است. کانتینرASP.NET، یک اشاره‌گر به کلاس‌هایی است که به عنوان سرویس عمل می‌کنند. در ادامه‌ی این مقاله، سرویس‌ها به کلاس‌هایی گفته می‌شود که به وسیله‌ی کانتینر ASP.NET Core مدیریت می‌شوند. شما می‌توانید سرویس ConfigureServices کانتینر را در داخل کلاس Startup پروژه خود پیکربندی کنید.


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

تزریق وابستگی از طریق متد سازنده، مستلزم آن است که سازنده‌ی کلاس مورد نظر عمومی باشد. در غیر این صورت، اپلیکیشن شما استثنای InvalidOperationException  را با پیام زیر نشان می‌دهد:
 A suitable constructor for type 'YourType' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.

تزریق از طریق متد سازنده مستلزم آن است که تنها یک سازنده‌ی مناسب وجود داشته باشد. البته Overload سازنده امکان پذیر است؛ ولی باید تنها یک متد سازنده وجود داشته باشد که آرگومان‌های آن توسط DI قابل ارائه باشند. اگر بیش از یکی وجود داشته باشد، سیستم استثنای InvalidOperationException را با پیام زیر نشان می‌دهد:
 Multiple constructors accepting all given argument types have been found in type 'YourType'. There should only be one applicable constructor.

سازندگان می‌توانند آرگومان‌هایی را از طریق DI دریافت کنند. برای این منظور آرگومان‌های این سازنده‌ها باید مقدار پیش فرضی را داشته باشند. به مثال زیر توجه نمایید:
// throws InvalidOperationException: Unable to resolve service for type 'System.String'...
public CharactersController(ICharacterRepository characterRepository, string title)
{
    _characterRepository = characterRepository;
    _title = title;
}

// runs without error
public CharactersController(ICharacterRepository characterRepository, string title = "Characters")
{
    _characterRepository = characterRepository;
    _title = title;
}


استفاده از سرویس ارائه شده توسط فریم ورک

متد ConfigureServices در کلاس Startup، مسئول تعریف سرویس‌هایی است که سیستم از آن استفاده می‌کند. از جمله‌ی این سرویس‌ها می‌توان به ویژگی‌های پلتفرم مانند EF Core و ASP.NET Core MVC اشاره کرد. IServiceCollection که به ConfigureServices ارائه می‌شود، سرویس‌های زیر را تعریف می‌کند (که البته بستگی به نوع پیکربندی هاست دارد):

  نوع سرویس    طول زندگی 
    Microsoft.AspNetCore.Hosting.IHostingEnvironment  
 Singleton 
    Microsoft.AspNetCore.Hosting.IApplicationLifetime     Singleton 
    Microsoft.AspNetCore.Hosting.IStartup     Singleton 
    Microsoft.AspNetCore.Hosting.Server.IServer     Singleton 
    Microsoft.Extensions.Options.IConfigureOptions     Transient 
    Microsoft.Extensions.ObjectPool.ObjectPoolProvider     Singleton 
    Microsoft.AspNetCore.Hosting.IStartupFilter     Transient 
    System.Diagnostics.DiagnosticListener     Singleton 
    System.Diagnostics.DiagnosticSource     Singleton 
    Microsoft.Extensions.Options.IOptions     Singleton 
    Microsoft.AspNetCore.Http.IHttpContextFactory     Transient 
    Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory     Transient 
    Microsoft.Extensions.Logging.ILogger     Singleton 
    Microsoft.Extensions.Logging.ILoggerFactory  
 Singleton 

در زیر نمونه ای از نحوه‌ی اضافه کردن سرویس‌های مختلف را به کانتینر، با استفاده از متدهای الحاقی مانند AddDbContext، AddIdentity و AddMvc، مشاهده می‌کنید:

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddMvc();

    // Add application services.
    services.AddTransient<IEmailSender, AuthMessageSender>();
    services.AddTransient<ISmsSender, AuthMessageSender>();
}
ویژگی‌ها و میان افزار‌های ارائه شده توسط ASP.NET، مانند MVC، از یک قرارداد، با استفاده از متد الحاقی AddServiceName برای ثبت تمام سرویس‌های مورد نیاز این ویژگی پیروی می‌کنند.


ثبت سرویس‌های اختصاصی

شما می‌توانید سرویس‌های اپلیکیشن خودتان را به ترتیبی که در تکه کد زیر مشاهده می‌کنید، ثبت نمایید. اولین نوع جنریک، نوعی است که از کانتینر درخواست خواهد شد و معمولا به شکل اینترفیس می‌باشد. نوع دوم، نوع پیاده سازی شده‌ای است که به وسیله‌ی کانتینر، نمونه سازی خواهد شد و کانتینر برای درخواست‌های از نوع اول، این نمونه از  تایپ را ارائه خواهد کرد:
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();

نکته:
هر متد الحاقی <services.Add<ServiceName، سرویس‌هایی را اضافه و پیکربندی می‌کند. به عنوان مثال services.AddMvc نیازمندی‌های سرویس MVC را اضافه می‌کند. توصیه می‌شود شما هم با افزودن متدهای الحاقی در فضای نام Microsoft.Extensions.DependencyInjection این قرارداد را رعایت نمائید. این کار باعث کپسوله شدن ثبت گروهی سرویس‌ها می‌شود.
متد AddTransient، برای نگاشت نوع‌های انتزاعی به سرویس‌های واقعی که نیاز به نمونه سازی به ازای هر درخواست دارند، استفاده می‌شود. در اصطلاح، طول عمر سرویس‌ها در اینجا مشخص می‌شوند. در ادامه گزینه‌های دیگری هم برای طول عمر سرویس‌ها تعریف خواهند شد. خیلی مهم است که برای هر یک از سرویس‌های ثبت شده، طول عمر مناسبی را انتخاب نمایید. آیا برای هر کلاس که سرویسی را درخواست می‌کند، باید یک نمونه‌ی جدید ساخته شود؟ آیا فقط یک نمونه در طول یک درخواست وب مورد استفاده قرار می‌گیرد؟ یا باید از یک نمونه‌ی واحد برای طول عمر کل اپلیکیشن استفاده شود؟
در مثال ارائه شده‌ی در این مقاله، یک کنترلر ساده به نام CharactersController وجود دارد که نام کاراکتری را نشان می‌دهد. متد Index، لیست کنونی کاراکترهایی را که در اپلیکیشن ذخیره شده‌اند، نشان می‌دهد. در صورتیکه این لیست خالی باشد، تعدادی به آن اضافه می‌کند. توجه داشته باشید، اگرچه این اپلیکیشن از Entity Framework Core و ClassDataContext برای داده‌های مانا استفاده می‌کند، هیچیکدام از آنها در کنترلر ظاهر نمی‌شوند. در عوض، مکانیزم دسترسی به داده‌های خاص، در پشت یک اینترفیس (ICharacterRepository) مخفی شده است (طبق الگوی طراحی ریپازیتوری). یک نمونه از ICharacterRepository از طریق سازنده درخواست می‌شود و به یک فیلد خصوصی اختصاص داده می‌شود، سپس برای دسترسی به کاراکتر‌ها در صورت لزوم استفاده می‌شود:
public class CharactersController : Controller
{
    private readonly ICharacterRepository _characterRepository;

    public CharactersController(ICharacterRepository characterRepository)
    {
        _characterRepository = characterRepository;
    }

    // GET: /characters/
    public IActionResult Index()
    {
        PopulateCharactersIfNoneExist();
        var characters = _characterRepository.ListAll();

        return View(characters);
    }

    private void PopulateCharactersIfNoneExist()
    {
        if (!_characterRepository.ListAll().Any())
        {
            _characterRepository.Add(new Character("Darth Maul"));
            _characterRepository.Add(new Character("Darth Vader"));
            _characterRepository.Add(new Character("Yoda"));
            _characterRepository.Add(new Character("Mace Windu"));
        }
    }
}

ICharacterRepository دو متد مورد نیاز کنترلر برای کار با نمونه‌های Character را تعریف می‌کند:
using System.Collections.Generic;
using DependencyInjectionSample.Models;

namespace DependencyInjectionSample.Interfaces
{
    public interface ICharacterRepository
    {
        IEnumerable<Character> ListAll();
        void Add(Character character);
    }
}
این اینترفیس با نوع واقعی CharacterRepository پیاده سازی شده است که در زمان اجرا استفاده می‌شود:

using System.Collections.Generic;
using System.Linq;
using DependencyInjectionSample.Interfaces;

namespace DependencyInjectionSample.Models
{
    public class CharacterRepository : ICharacterRepository
    {
        private readonly ApplicationDbContext _dbContext;

        public CharacterRepository(ApplicationDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        public IEnumerable<Character> ListAll()
        {
            return _dbContext.Characters.AsEnumerable();
        }

        public void Add(Character character)
        {
            _dbContext.Characters.Add(character);
            _dbContext.SaveChanges();
        }
    }
}
توجه داشته باشید که CharacterRepository یک ApplicationDbContext را در سازنده‌ی خود درخواست می‌کند. همانطور که مشاهده می‌شود هر وابستگی درخواست شده، به نوبه خود وابستگی‌های دیگری را درخواست می‌کند. تزریق وابستگی‌هایی به شکل زنجیره‌ای، همانند این مثال غیر معمول نیست. کانتینر مسئول resolve (نمونه سازی) همه‌ی وابستگی‌های موجود در گراف وابستگی و بازگرداندن سرویس کاملا resolve شده می‌باشد.

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

در مورد مثال مطرح شده، ICharacterRepository و به نوبه خود ApplicationDbContext باید با سرویس‌های خود در کانتینر ConfigureServices و کلاس Startup ثبت شوند. ApplicationDbContext با فراخوانی متد <AddDbContext<T پیکربندی می‌شود. کد زیر ثبت کردن نوع CharacterRepository را نشان می‌دهد:
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseInMemoryDatabase()
    );

    // Add framework services.
    services.AddMvc();

    // Register application services.
    services.AddScoped<ICharacterRepository, CharacterRepository>();
    services.AddTransient<IOperationTransient, Operation>();
    services.AddScoped<IOperationScoped, Operation>();
    services.AddSingleton<IOperationSingleton, Operation>();
    services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));
    services.AddTransient<OperationService, OperationService>();
}
کانتکست انتیتی فریم ورک، با استفاده از متدهای کمکی که در تکه کد بالا نشان داده شده است، باید با طول عمر Scoped به کانتینر سرویس‌ها افزوده شود. این کار می‌تواند به صورت اتوماتیک انجام گیرد. همه‌ی ریپازیتوری‌هایی که از Entity Framework استفاده می‌کنند، باید از یک طول عمر مشابه استفاده کنند.

هشدار
خطر بزرگی را که باید در نظر گرفت، resolve کردن سرویس Scoped از طول عمر singleton می‌باشد. در صورت انجام این کار، احتمال دارد که سرویس‌ها وارد حالت نادرستی شوند.

سرویس‌هایی که وابستگی‌های دیگری هم دارند، باید آنها را در کانتینر ثبت کنند. اگر سازنده‌ی سرویس نیاز به یک primitive به عنوان ورودی داشته باشد، می‌توان با استفاده از الگوی گزینه‌ها و پیکربندی (options pattern and configuration)، ورودی‌های مناسبی را به سازنده‌ها منتقل کرد.


طول عمر سرویس‌ها و گزینه‌های ثبت

سرویس‌های ASP.NET را می‌توان با طول عمرهای زیر پیکربندی کرد:
Transient: سرویس‌هایی با طول عمر Transient، در هر زمان که درخواست می‌شوند، مجددا ایجاد می‌شوند. این طول عمر برای سرویس‌های سبک و بدون حالت مناسب می‌باشند.
Scoped: سرویس‌هایی با طول عمر Scoped، تنها یکبار در طی هر درخواست ایجاد می‌شوند.
Singleton: سرویس‌هایی با طول عمر Singleton، برای اولین باری که درخواست می‌شوند (یا اگر در ConfigureServices نمونه‌ای را مشخص کرده باشید) ایجاد می‌شوند و درخواست‌های آتی برای این سرویس‌ها از همان نمونه‌ی ایجاد شده استفاده می‌کنند. اگر اپلیکیشن شما درخواست رفتار singleton را داشته باشد، پیشنهاد می‌شود که سرویس کانتینر را برای مدیریت طول عمر سرویس مورد نیاز پیکربندی کنید و خودتان الگوی طراحی singleton را پیاده سازی نکنید.

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

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

using System;

namespace DependencyInjectionSample.Interfaces
{
    public interface IOperation
    {
        Guid OperationId { get; }
    }

    public interface IOperationTransient : IOperation
    {
    }
    public interface IOperationScoped : IOperation
    {
    }
    public interface IOperationSingleton : IOperation
    {
    }
    public interface IOperationSingletonInstance : IOperation
    {
    }
}
ما این اینترفیس‌ها را با استفاده از یک کلاس واحد به نام Operation پیاده سازی کرده‌ایم. سازنده‌ی این کلاس، یک Guid به عنوان ورودی می‌گیرد؛ یا اگر Guid برایش تامین نشد، خودش یک Guid جدید را می‌سازد.
سپس در ConfigureServices، هر نوع با توجه به طول عمر مورد نظر، به کانتینر افزوده می‌شود:
services.AddScoped<ICharacterRepository, CharacterRepository>();
services.AddTransient<IOperationTransient, Operation>();
services.AddScoped<IOperationScoped, Operation>();
services.AddSingleton<IOperationSingleton, Operation>();
services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));
services.AddTransient<OperationService, OperationService>();
توجه داشته باشید که سرویس IOperationSingletonInstance، از یک نمونه‌ی خاص، با شناسه‌ی شناخته شده‌ی Guid.Empty استفاده می‌کند (این Guid فقط شامل اعداد صفر می‌باشد). بنابراین زمانیکه این تایپ مورد استفاده قرار می‌گیرد، کاملا واضح است. تمام این سرویس‌ها وابستگی‌های خود را به صورت پراپرتی نمایش می‌دهند. بنابراین می‌توان آنها را در View نمایش داد.

using DependencyInjectionSample.Interfaces;

namespace DependencyInjectionSample.Services
{
    public class OperationService
    {
        public IOperationTransient TransientOperation { get; }
        public IOperationScoped ScopedOperation { get; }
        public IOperationSingleton SingletonOperation { get; }
        public IOperationSingletonInstance SingletonInstanceOperation { get; }

        public OperationService(IOperationTransient transientOperation,
            IOperationScoped scopedOperation,
            IOperationSingleton singletonOperation,
            IOperationSingletonInstance instanceOperation)
        {
            TransientOperation = transientOperation;
            ScopedOperation = scopedOperation;
            SingletonOperation = singletonOperation;
            SingletonInstanceOperation = instanceOperation;
        }
    }
}
برای نشان دادن طول عمر اشیاء، در بین درخواست‌های جداگانه‌ی یک اپلیکیشن، مثال ذکر شده شامل کنترلر OperationsController می‌باشد که هر کدام از انواع IOperation و همچنین OperationService را درخواست می‌کند. سپس اکشن Index تمام مقادیر OperationId کنترل کننده و سرویس‌ها را نمایش می‌دهد:
using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;
using Microsoft.AspNetCore.Mvc;

namespace DependencyInjectionSample.Controllers
{
    public class OperationsController : Controller
    {
        private readonly OperationService _operationService;
        private readonly IOperationTransient _transientOperation;
        private readonly IOperationScoped _scopedOperation;
        private readonly IOperationSingleton _singletonOperation;
        private readonly IOperationSingletonInstance _singletonInstanceOperation;

        public OperationsController(OperationService operationService,
            IOperationTransient transientOperation,
            IOperationScoped scopedOperation,
            IOperationSingleton singletonOperation,
            IOperationSingletonInstance singletonInstanceOperation)
        {
            _operationService = operationService;
            _transientOperation = transientOperation;
            _scopedOperation = scopedOperation;
            _singletonOperation = singletonOperation;
            _singletonInstanceOperation = singletonInstanceOperation;
        }

        public IActionResult Index()
        {
            // viewbag contains controller-requested services
            ViewBag.Transient = _transientOperation;
            ViewBag.Scoped = _scopedOperation;
            ViewBag.Singleton = _singletonOperation;
            ViewBag.SingletonInstance = _singletonInstanceOperation;

            // operation service has its own requested services
            ViewBag.Service = _operationService;
            return View();
        }
    }
}

حالا دو درخواست جداگانه برای این کنترلر ساخته شده است:



به تفاوت‌های موجود در مقادیر OperationId در یک درخواست و بین درخواستها توجه کنید:
-  OperationId اشیاء Transient همیشه متفاوت می‌باشند. چون یک نمونه جدید برای هر کنترلر و هر سرویس ایجاد شده‌است.
- اشیاء Scoped در یک درخواست، یکسان هستند؛ اما در درخواست‌های مختلف متفاوت می‌باشند.
- اشیاء Singleton برای هر شی‌ء و هر درخواست (صرف نظر از اینکه یک نمونه در ConfigureServices ارائه شده است) یکسان می‌باشند.


درخواست سرویس

در ASP.NET سرویس‌های موجود در یک درخواست HttpContext از طریق مجموعه RequestServices قابل مشاهده می‌باشد.


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


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


طراحی سرویس‌ها برای تزریق وابستگی‌ها

شما باید سرویس‌های خود را طوری طراحی کنید که از تزریق وابستگی‌ها برای ارتباطات خود استفاده نمایند. این کار باعث کاهش استفاده از فراخوانی‌های متدهای استاتیک (متدهای استاتیک، حالت دار می‌باشند و استفاده‌ی زیاد از آنها باعث به وجود آمدن بوی بد کدی به نام static cling، می‌شود) و همچنین از بین رفتن نیاز به نمونه سازی مستقیم کلاس‌های وابسته داخل سرویس‌ها، می‌شود. هر موقع بخواهید بین new کردن یک کلاس، یا درخواست دادن آن از طریق تزریق وابستگی، یکی را انتخاب کنید، این اصطلاح را به یاد بیاورید،  New is Glue. با پیروی از اصول SOLID طراحی شیء گرا، به طور طبیعی کلاس‌های شما تمایل به کوچک بودن، کارا و قابل تست بودن را دارند.
اگر متوجه شدید که کلاس‌های شما تمایل دارند تا تعداد وابستگی‌های زیادی به آنها تزریق شود، چه باید بکنید؟ به طور کلی این مشکل نشانه‌ای است از نقض  Single Responsibility Principle یا SRP است و احتمالا کلاس‌های شما وظایف بیش از اندازه‌ای را دارند. در این گونه موارد تلاش کنید مقداری از وظایف کلاس را به یک کلاس جدید منتقل کنید. در نظر داشته باشید که کلاس‌های کنترلر باید به مسائل UI تمرکز کنند و قوانین کسب و کار و جزئیات دسترسی به داده‌ها باید در کلاس‌هایی جداگانه و مرتبط با خود قرار داشته باشند.
به طور خاص برای دسترسی به داده ، شما می‌توانید DbContext را به کنترلر‌های خود تزریق کنید (با فرض اینکه شما EF را به کانتینر سرویس ConfigureServices اضافه کرده‌اید). بعضی از توسعه دهندگان به جای تزریق مستقیم DbContext از یک اینترفیس ریپازیتوری استفاده می‌نمایند. می‌توانید با استفاده از یک اینترفیس برای کپسوله کردن منطق دسترسی به داده‌ها در یک مکان، تعداد تغییرات مورد نیاز را در صورت تغییر دیتابیس، به حداقل برسانید.


تخریب سرویس ها

سرویس کانتینر برای نوع‌های IDisposable که خودش ایجاد کرده‌است، متد Dispose را فراخوانی خواهد کرد. با این حال، اگر شما خودتان نمونه‌ای را به صورت دستی نمونه سازی و به کانتینر اضافه کرده باشید، سرویس کانتینر آنرا dispose نخواهد کرد.

مثال:
// Services implement IDisposable:
public class Service1 : IDisposable {}
public class Service2 : IDisposable {}
public class Service3 : IDisposable {}

public void ConfigureServices(IServiceCollection services)
{
    // container will create the instance(s) of these types and will dispose them
    services.AddScoped<Service1>();
    services.AddSingleton<Service2>();

    // container did not create instance so it will NOT dispose it
    services.AddSingleton<Service3>(new Service3());
    services.AddSingleton(new Service3());
}

نکته:
در نسخه 1.0، کانتینر برای تمام اشیاء از نوع IDisposable از جمله اشیائی که خودش ایجاد نکرده بود، متد dispose را فراخوانی می‌کرد.


سرویس‌های کانتینر جانشین

کانتینر موجود در net core. به منظور تامین نیازهای اساسی فریم ورک ایجاد شده‌است و تعداد زیادی از اپلیکیشن‌ها از آن استفاده می‌کنند. با این حال، توسعه دهندگان می‌توانند کانتینرهای مورد نظر خود را جایگزین آن کنند. متد ConfigureServices به طور معمول مقدار void را بر می‌گرداند. اما با تغییر امضای آن به نوع بازگشتیIServiceProvider، می‌توان سرویس کانتینر متفاوتی را در اپلیکیشن پیکربندی کرد. سرویس‌های کانتینر IOC مختلفی برای NET. وجود دارند؛ در مثال زیر، Autofac استفاده شده است.
در ابتدا بسته‌های زیر را نصب کنید:
Autofac
Autofac.Extensions.DependencyInjection
سپس کانتینر را در ConfigureServices پیکربندی کنید و  IServiceProvider را به عنوان خروجی بازگردانید:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    // Add other framework services

    // Add Autofac
    var containerBuilder = new ContainerBuilder();
    containerBuilder.RegisterModule<DefaultModule>();
    containerBuilder.Populate(services);
    var container = containerBuilder.Build();
    return new AutofacServiceProvider(container);
}


توصیه ها

هنگام کار با تزریق وابستگی‌ها، توصیه‌های ذیر را در نظر داشته باشید:
- DI برای اشیایی که دارای وابستگی پیچیده هستند، مناسب می‌باشد. کنترلرها، سرویس‌ها، آداپتورها و ریپازیتوری‌ها، نمونه‌هایی از این اشیاء هستند که می‌توانند به DI اضافه شوند.
- از ذخیره‌ی داده‌ها و پیکربندی مستقیم در DI اجتناب کنید. به عنوان مثال، معمولا سبد خرید کاربر نباید به سرویس کانتینر اضافه شود. پیکربندی باید از مدل گزینه‌ها استفاده کند. همچنین از اشیاء "data holder"، که فقط برای دسترسی دادن به اشیاء دیگر ایجاد شده‌اند، نیز اجتناب کنید. در صورت امکان بهتر است شیء واقعی مورد نیاز DI درخواست شود.
- از دسترسی استاتیک به سرویس‌ها اجتناب شود.
- از نمونه سازی مستقیم سرویس‌ها در کد برنامه خود اجتناب کنید.
- از دسترسی استاتیک به HttpContext اجتناب کنید.

توجه
مانند هر توصیه‌ی دیگری، ممکن است شما با شرایطی مواجه شوید که مجبور به نقض هر یک از این توصیه‌ها شوید. اما این موارد استثناء بسیار نادر می‌باشند و رعایت این نکات یک عادت برنامه نویسی خوب محسوب می‌شود.

مرجع: Introduction to Dependency Injection in ASP.NET Core