نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 17 - بررسی فریم ورک Logging
یک نکته‌ی تکمیلی: معرفی سیستم Logging در Asp.Net Core

سیستم Logging  در نسخه Core بصورت پیشفرض موجود هست، اما در پروژه‌های غیر Core اگر نیاز به استفاده از آن را داشتید باید اول پکیج Microsoft.Extensions.Logging.Console را نصب کنید. طریقه استفاده ازین سیستم بسیار راحت بوده و کانفیگ خیلی خاصی ندارد؛ اما قبل از استفاده باید با اصطلاحات اولیه آن آشنا بشیم. چیزی که مسئولیت ثبت Log در برنامه‌های Core را دارد Logging Api تعریف شده در خود پلتفرم Asp.Net Core میباشد. Log‌ها طبق این سیستم ذخیره میشوند و توسط Provider‌های مختلف قابل نمایش میباشند. اما Logging Provider چیست؟ ابزاری که با استفاده از آن میتوانیم Log‌های ذخیره شده توسط Logger را مشاهده کنیم و یا بعضا دستور ذخیره Log‌ها را به Logger  ارسال کنیم. در ادامه لیستی از Logging Provider‌های موجود را میبینیم که شامل Debug  , Console , EventSource , EventLog , AzureAppService , Application Insights و... میباشد. Log Provider‌های متنوعی وجود دارند که بعضی از آن‌ها مزایا و معایب خود را دارند و قابل همگام سازی با سیستم Asp.Net Core Logging میباشند مثل Provider‌های Serilog , NLog و دیگر پکیج‌هایی که برای این موضوع وجود دارند. در وهله بعد وقتی صحبت از ذخیره Log‌ها به میان میاید تعاریفی برای درجه اهمیت هر Log و الویت بندی Log‌ها مطابق اهمیتشان وجود دارد که به ما کمک میکند دسته بندی و نظم بهتری در Log‌های برنامه خودمان بیابیم. به این الویت‌ها اصطلاحا Log Level گفته میشود که به شرح زیر میباشد.
◾️الویت 0  Trace
گزارش‌هایی که شامل دقیق‌ترین پیام‌ها هستند. این پیام‌ها ممکن است حاوی داده‌های حساس برنامه باشند. این پیام‌ها به طور پیش فرض غیرفعال هستند و هرگز نباید در محیط تولید فعال شوند.

◾️الویت 1 Debug
اطلاعات مربوط به بررسی لحظه‌ای در حین رفع خطا یا روند Debugging. این گزارش‌ها در درجه اول باید حاوی اطلاعات مفیدی برای رفع اشکال باشند و هیچ ارزش طولانی مدتی ندارند.

◾️الویت 2 Information
گزارش‌هایی که جریان کلی برنامه را ردیابی می‌کنند. این Log‌ها باید چرخه درازمدت داشته باشند.

◾️الویت 3 Warning
گزارش‌هایی که یک رویداد غیر عادی یا غیر منتظره را در جریان برنامه ثبت میکنند؛ اما در غیر این صورت باعث توقف اجرای برنامه نمی‌شوند.

◾️الویت 4 Error
گزارش‌هایی که هنگام توقف جریان اجرای فعلی به دلیل خرابی، ثبت می‌شوند. اینها باید نشان دهنده‌ی یک شکست در فعالیت فعلی باشند، نه یک شکست در کل برنامه.

◾️الویت 5 Critical
اطلاعات غیرقابل بازیابی برنامه یا خرابی سیستم که نیاز به توجه فوری دارند.

برای فعالسازی Logging پیشفرض در Core ابتدا باید در کلاس Program، برنامه Provider مورد استفاده را مشخص کنید. اطلاعات مربوط به Log Level نمایشی برنامه در فایل AppSettingJson قرار دارد که معمولا Log Level نمایشی روی Information قرار دارد و Log هایی با الویت کمتر از Information را نمایش نمیدهد. میتوانید با تغیر در سطح الویت LogLevel اطلاعات مربوط به سطوح پایین‌تر یعنی Trace و  Debug را نیز مشاهده کنید.
روش‌های دیگری نیز برای دسته بندی Log‌ها وجود دارند از جمله Log Category یا Log Event Id که میتوانید به سادگی از آن‌ها استفاده کنید و دسته بندی دلخواه خود را برای Log‌ها در برنامه داشته باشید. 
مطالب
ایجاد سرویس چندلایه‎ی WCF با Entity Framework در قالب پروژه - 1

در این نوشتار که به صورت آموزش تصویری ارائه می‌‏شود؛ یک سرویس WCF در Visual Studio 2013 ایجاد می‌کنم، سپس روش استفاده از آن‏را در یک برنامه ویندوزی آموزش خواهم داد. در اینجا در نظرگرفته شده است که شما افزونه‎ی Resharper را روی ویژوال استودیوی خود نصب دارید. پس در صورتیکه هنوز به سراغ آن نرفته اید درنگ نکنید و واپسین نگارش آن را دانلود کنید.

در این پروژه‌ی ساده در نظر می‎گیریم که دو جدول یکی برای اخبار، شامل عنوان، متن خبر و تاریخ ثبت و دسته بندی و دیگری برای نگهداری دسته‎ها در پایگاه داده داریم و می‏خواهیم سرویس‏های مناسب با این دو جدول را بسازیم. با کد زیر، پایگاه داده‌‏ی dbTest و جدول‌های tblNews و tblCategory در SQL Server 2012 ساخته می‌شود:

USE [master]
GO
/****** Object:  Database [dbMyNews]    Script Date: 2014/01/14 09:46:04 ب.ظ ******/
CREATE DATABASE [dbMyNews]
 CONTAINMENT = NONE
 ON  PRIMARY 
( NAME = N'dbMyNews', FILENAME = N'D:\dbMyNews.mdf' , SIZE = 5120KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )
 LOG ON 
( NAME = N'dbMyNews_log', FILENAME = N'D:\dbMyNews_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)
GO
USE [dbMyNews]
GO
/****** Object:  Table [dbo].[tblCategory]    Script Date: 2014/01/14 09:46:04 ب.ظ ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[tblCategory](
[tblCategoryId] [int] IDENTITY(1,1) NOT NULL,
[CatName] [nvarchar](50) NOT NULL,
[IsDeleted] [bit] NOT NULL,
 CONSTRAINT [PK_tblCategory] PRIMARY KEY CLUSTERED 
(
[tblCategoryId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
/****** Object:  Table [dbo].[tblNews]    Script Date: 2014/01/14 09:46:04 ب.ظ ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[tblNews](
[tblNewsId] [int] IDENTITY(1,1) NOT NULL,
[tblCategoryId] [int] NOT NULL,
[Title] [nvarchar](50) NOT NULL,
[Description] [nvarchar](max) NOT NULL,
[RegDate] [datetime] NOT NULL,
[IsDeleted] [bit] NULL,
 CONSTRAINT [PK_tblNews] PRIMARY KEY CLUSTERED 
(
[tblNewsId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO
ALTER TABLE [dbo].[tblNews]  WITH CHECK ADD  CONSTRAINT [FK_tblNews_tblCategory] FOREIGN KEY([tblCategoryId])
REFERENCES [dbo].[tblCategory] ([tblCategoryId])
GO
ALTER TABLE [dbo].[tblNews] CHECK CONSTRAINT [FK_tblNews_tblCategory]
GO
USE [master]
GO
ALTER DATABASE [dbMyNews] SET  READ_WRITE 
GO

اکنون Visual Studio 2013 را بازکنید سپس روی گزینه New Project کلیک کنید و برابر با نگاره‌ی زیر عمل کنید:

پروژه MyNewsWCFLibrary در راه حل MyNews ساخته می‌شود. این پروژه به صورت پیش‌گزیده دارای یک کلاس به نام Service و یک interface به نام IService است. هر دو را حذف کنید و سپس روی نام پروژه راست‌کلیک کرده، از منوی بازشده گزینه‌ی Add -> New Item را انتخاب کنید. سپس برابر با نگاره‌ی زیر عمل کنید:

در لایه‎‌ی Service Interface کلیه‎ی روال‌های مورد نیاز برای ارتباط با پایگاه داده را می‎سازیم. پیش از آن باید یک Model برای ارتباط با پایگاه داده ساخته باشیم. برای این کار از پنجره Add New Item و از زیرمجموعه Data، گزینه ADO.NET Entity Data Model را انتخاب کنید و به‌سان زیر پیش روید:

در گام پسین روی دکمه New Connection کلیک کنید و رشته‌ی اتصال به پایگاه داده‌ی dbMyNews را بسازید. سپس همانند تنظیمات نگاره‌ی زیر ادامه دهید:

در گام پسین گزینه‌‎ی Entity Framework 6.0 را برگزینید و روی دکمه‎ی Next کلیک کنید.

در پنجره نشان‎ داده شده، جدول‎های مورد نیاز را همانند نگاره‌ی زیر انتخاب کرده و روی دکمه Finish کلیک کنید:

در پایان مدل ما همانند نگاره‌ی زیر خواهد بود.

در بخش پسین درباره‏ی شیوه‏‌ی دست‎کاری کلاس‎های Entity خواهم نوشت.

نظرات مطالب
SignalR
من خودم تخصصی در زمینه node.js ندارم. البته ازش خوشم هم نمیاد :)
اینجوری که خودشون میگن این یه پلتفرمه که از Chrome's JavaScript runtime استفاده میکنه و شونصدتا ویژگی دیگه هم داره! برای استفاده ازش باید برنامشو رو که بهش node میگن دریافت و نصب کنین (حدود 3 مگابایت نسخه 0.6.19). بعد کد سمت سرور کاملا با استفاده از زبان جاوااسکریپت نوشته میشه (عیب) بنابراین کاملا cross platform هستش (مزیت). یعنی با استفاده از جاوااسکریپت میتونین مثلا یه وب سرور بسیار سبک (مزیت) ایجاد کنین که به یکسری درخواستا پاسخ میده. حالا یکسری اومدن برای این پلتفرم ماژولهایی نوشتن مثل socket.io و nowJS که برقراری ارتباطی راحت و آسان با سرور و تبادل داده‌ها رو به عهده میگیره (مزیت).
در مقابل SignalR یه کتابخونه غیرهمزمان (Async) هستش که برا دات نت نوشته شده (مزیت)، مخصوص وب زمان واقعی چندکاربره و شونصدتا ویژگی دیگه هم نداره (مزیت) :). بنابراین سمت سرور از مزایای بیشماری برخورداره، مثل یک IDE قدرتمند (VS) (مزیت2x) و یک کتابخونه کامل (NetFx.) (مزیت2x)
درضمن یه فریمورک دیگه هم برای ASP.NET وجود داره: PokeIn که نسخه تجاری (پولی) هم داره و از WebSocket استفاده میکنه. جایی خوندم که کاراییش خوبه در حد 10 تا 50 هزار کلاینت همزمان(^).
برای SignalR یه اپلیکیشن توسط خود نویسندگانش نوشته شده (SignalR-Crank) که برای آزمایش بار روی سرور استفاده میشه. طبق گفته خودشون تا 100k (صد هزار!) کلاینت رو تونستن بدون هیچ مشکلی راه بندازن (البته با مصرف 5 گیگ رم). نمیدونم این تست چی بوده ولی این عدد خیلی زیاده.
در مورد بحث کارآیی خودم دارم یه تستایی انجام میدم. نمیدونم بتونم آزمایش مشابهی رو بقیه فریمورکها انجام بدم یا نه. اگه نتیجه‌ای حاصل شد اینجا میزارم.
با تشکر
مطالب
افزونه تاریخ فارسی برای outlook 2007

عموما در محیط کاری اگر شبکه ویندوزی و mail server مورد استفاده هم ms exchange باشد، به طور قطع از outlook برای انجام امور روزمره ارسال و دریافت ایمیل استفاده می‌شود.
طبق معمول هم مشکل ما تاریخ فارسی است! یکی از شرکت‌های ایرانی که در این‌باره محصولی را ارائه داده با hook کردن تاریخ ویندوز، هر جایی که تاریخی قرار است نمایش داده شود، آنرا فارسی می‌کند. این محصول دو ایراد دارد: الف) رایگان نیست! ب) این hook بر روی عملکرد سایر برنامه‌ها تاثیرگذار است. برای مثال برنامه‌های دات نت تاریخ قمری را نمایش خواهند داد، بر روی عملکرد و کارآیی کلی سیستم تاثیر منفی دارد و مشکلاتی از این دست.

افزونه زیر بدون دستکاری تاریخ ویندوز، دو کار را در MS outlook 2007 انجام خواهد داد:

الف) اضافه کردن ستون "تاریخ دریافت" شمسی



ب) در متن دریافتی، تمام تاریخ‌های sent موجود را یافته و شمسی می‌کند



دریافت افزونه:
لطفا اینجا کلیک کنید.

تذکر مهم:
نصب دو بسته به روز رسانی سیستم و دات نت فریم ورک را پیش از نصب این افزونه فراموش نکنید.
/Post/14/افزونه-فارسی-به-پارسی-برای-word-2007


در کل برای من کار راه انداز بوده :)

در طی روزهای آتی، سورس کامل و نحوه برنامه نویسی آن‌را بررسی خواهیم کرد.

مطالب دوره‌ها
مراحل Refactoring یک قطعه کد برای اعمال تزریق وابستگی‌ها
برای رسیدن به الگوی معکوس سازی وابستگی‌ها عموما مراحل زیر طی می‌شوند:

الف) در متدهای لایه جاری خود واژه‌های کلیدی new و همچنین کلیه فراخوانی‌های استاتیک را بیابید.
ب) وهله سازی این‌ها را به یک سطح بالاتر (نقطه آغازین برنامه) منتقل کنید. اینکار باید بر اساس اتکای به Abstraction و برای مثال استفاده از اینترفیس‌ها صورت گیرد.
ج) اینکار را آنقدر تکرار کنید تا دیگر در کدهای لایه جاری خود واژه کلیدی new یا فراخوانی متدهای استاتیک مشاهده نشود.
د) در آخر وهله سازی object graphهای مورد نیاز را به یک IoC Container محول کنید.


یک مثال: ابتدا بررسی یک قطعه کد متداول

using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Mvc;

namespace DI06.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            string result = string.Empty;
            using (var client = new WebClient { Encoding = Encoding.UTF8 })
            {
                result = client.DownloadString("https://www.dntips.ir/");
            }
            var match = new Regex(@"(?s)<title>(.+?)</title>", RegexOptions.IgnoreCase).Match(result);
            var title = match.Groups[1].Value.Trim();

            ViewBag.PageTitle = title;
            return View();
        }
    }
}
فرض کنید یک برنامه ASP.NET MVC را به نحو فوق تهیه کرده‌ایم. در کدهای کنترلر آن قصد داریم محتویات Html یک صفحه خاص را دریافت و سپس عنوان آن‌را استخراج کرده و نمایش دهیم.
مشکلات کد فوق:
الف) قرار گرفتن منطق تجاری پیاده سازی کدها مستقیما داخل کدهای یک اکشن متد؛ این مساله در دراز مدت به تکرار شدید کدها منجر خواهد شد که نهایتا قابلیت نگهداری آن‌را کاهش می‌دهند.
ب) در این کد حداقل دو بار واژه کلیدی new ذکر شده است. مورد اول یا new WebClient، از همه مهم‌تر است؛ از این جهت که نوشتن آزمون واحد را برای این کنترلر بسیار مشکل می‌کند. آزمون‌های واحد باید سریع و بدون نیاز به منابع خارجی، قابل اجرا باشند. تعویض آن هم مطابق کدهای تدارک دیده شده کار ساده‌ای نیست.


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

در اصل معکوس سازی وابستگی‌ها عنوان کردیم لایه بالایی سیستم نباید مستقیما به لایه‌های زیرین در حال استفاده از آن، وابسته باشد. این وابستگی باید معکوس شده و همچنین بر اساس Abstraction یا برای مثال استفاده از اینترفیس‌ها صورت گیرد.
به همین منظور یک پروژه دیگر را از نوع Class library، مثلا به نام DI06.Services به Solution جاری اضافه می‌کنیم.
namespace DI06.Services
{
    public interface IWebClientServices
    {
        string FetchUrl(string url);
        string GetWebPageTitle(string url);
    }
}

using System.Net;
using System.Text;
using System.Text.RegularExpressions;

namespace DI06.Services
{
    public class WebClientServices : IWebClientServices
    {
        public string FetchUrl(string url)
        {
            using (var client = new WebClient { Encoding = Encoding.UTF8 })
            {
                return client.DownloadString(url);
            }
        }

        public string GetWebPageTitle(string url)
        {
            var html = FetchUrl(url);
            var match = new Regex(@"(?s)<title>(.+?)</title>", RegexOptions.IgnoreCase).Match(html);
            return match.Groups[1].Value.Trim();
        }
    }
}
در این لایه، سرویس WebClient ایی را تدارک دیده‌ایم. این سرویس می‌تواند محتوای Html یک آدرس را برگرداند و یا عنوان آن آدرس خاص را استخراج نماید.
هنوز کار معکوس سازی وابستگی‌ها رخ نداده است. صرفا اندکی تمیزکاری و انتقال پیاده سازی منطق تجاری به یک سری کلاس‌هایی با قابلیت استفاده مجدد صورت گرفته است. به این ترتیب اگر باگی در این کدها وجود داشته باشد و همچنین از آن در چندین نقطه برنامه استفاده شده باشد، اصلاح این کلاس مرکزی، به یکباره تمامی قسمت‌های مختلف برنامه را تحت تاثیر مثبت قرار داده و از تکرار کدها و فراموشی احتمالی بهبود قسمت‌های مشابه جلوگیری می‌کند.
کار معکوس سازی وابستگی‌ها در یک لایه بالاتر صورت خواهد گرفت:
using System.Web.Mvc;
using DI06.Services;

namespace DI06.Controllers
{
    public class HomeController : Controller
    {
        readonly IWebClientServices _webClientServices;
        public HomeController(IWebClientServices webClientServices)
        {
            _webClientServices = webClientServices;
        }

        public ActionResult Index()
        {
            ViewBag.PageTitle = _webClientServices.GetWebPageTitle("https://www.dntips.ir/");
            return View();
        }
    }
}
اینبار کنترلر Home را توسط تزریق وابستگی‌ها در سازنده کنترلر، بازنویسی کرده‌ایم. کد نهایی بسیار تمیزتر از حالت قبل است. دیگر پیاده سازی متد GetWebPageTitle مستقیما داخل یک اکشن متد قرار نگرفته است. همچنین این کنترلر اصلا نمی‌داند که قرار است از کدام پیاده سازی اینترفیس IWebClientServices استفاده کند. اگر در تنظیمات اولیه IoC Container مورد استفاده، کلاس WebClientServices ذکر شده باشد، از آن استفاده خواهد کرد؛ یا اگر حتی کلاس WebClientServicesFake نیز معرفی گردد (جهت انجام آزمون غیر وابسته به new WebClient)، باز هم بدون کوچکترین تغییری در کدهای آن قابل استفاده خواهد بود.

در مورد نحوه تنظیمات اولیه یک IoC Container و یا پیشنیازهای ASP.NET MVC جهت آماده شدن برای تزریق خودکار وابستگی‌ها در سازنده کنترلرها، پیشتر مطالبی را در این سری مطالعه کرده‌اید؛ در اینجا نیز اصول مورد استفاده یکی است و تفاوتی نمی‌کند.
نظرات مطالب
مهارت‌های تزریق وابستگی‌ها در برنامه‌های NET Core. - قسمت ششم - دخالت در مراحل وهله سازی اشیاء توسط IoC Container
زمانیکه از serviceProvider.GetRequiredService استفاده می‌شود، تمام وابستگی‌های سرویس درخواستی را هم تا n سطح تامین می‌کند. فقط تنظیمات اولیه‌ی یافتن سرویس‌ها را انجام دهید، مابقی آن خودکار است. در تزریق وابستگی‌ها بجای استفاده‌ی از واژه‌ی کلیدی new، یا از تزریق وهله‌ی مدنظر آن، در سازنده‌ی یک کلاس استفاده می‌شود و یا از الگوی Service Locator (یا همان serviceProvider.GetRequiredService در اینجا).
مطالب
مهارت‌های تزریق وابستگی‌ها در برنامه‌های NET Core. - قسمت سوم - رهاسازی منابع سرویس‌های IDisposable
یکی از پرکاربردترین اینترفیس‌های NET.، اینترفیس IDisposable است. عموما کلاس‌هایی که ارجاعی را به منابع غیر مدیریت شده مانند فایل‌ها و سوکت‌ها داشته باشند، این اینترفیس را پیاده سازی می‌کنند. garbage collector به صورت خودکار حافظه‌ی اشیاء مدیریت شده یا دات نتی را رها می‌کند؛ اما چیزی را در مورد منابع غیر مدیریت شده نمی‌داند. به همین جهت پیاده سازی اینترفیس IDisposable روشی را جهت پاکسازی این منابع به garbage collector معرفی می‌کند.


رفتار IoC Container توکار ASP.NET Core با سرویس‌های IDisposable

ASP.NET Core به همراه یک IoC Container توکار ارائه می‌شود و اگر سرویسی با طول عمرTransient و یا Scoped به آن معرفی شود و همچنین این سرویس اینترفیس IDisposable را نیز پیاده سازی کند، کار dispose خودکار آن در پایان درخواست جاری صورت می‌گیرد و نیازی به تنظیمات اضافه‌تری ندارد. در اینجا سرویس‌هایی با طول عمر Singleton نیز در پایان کار برنامه، زمانیکه خود ServiceProvider به پایان کارش می‌رسد، dispose خواهند شد.
البته این مورد یک شرط را نیز به همراه دارد: کار وهله سازی سرویس‌های درخواستی باید توسط خود این IoC Container مدیریت شود تا در پایان کار بداند چگونه آن‌ها را Dispose کند.

یک مثال: بررسی Dispose شدن خودکار یک سرویس IDisposable
namespace CoreIocServices
{
    public interface IMyDisposableService
    {
        void Run();
    }

    public class MyDisposableService : IMyDisposableService, IDisposable
    {
        private readonly ILogger<MyDisposableService> _logger;

        public MyDisposableService(ILogger<MyDisposableService> logger)
        {
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _logger.LogInformation("+ {0} was created", this.GetType().Name);
        }

        public void Run()
        {
            _logger.LogInformation("Running MyDisposableService!");
        }

        public void Dispose()
        {
            _logger.LogInformation("- {0} was disposed!", this.GetType().Name);
        }
    }
}
سرویس ساده‌ی فوق، اینترفیس IDisposable را پیاده سازی می‌کند و با استفاده از ILogger، پیام‌هایی را در زمان ایجاد و Dipose آن در پنجره کنسول و یا دیباگ نمایش خواهد داد.
اگر این سرویس را به یک برنامه‌ی ASP.NET Core معرفی کنیم:
namespace CoreIocSample02
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IMyDisposableService, MyDisposableService>();
و سپس به نحو متداولی از آن در یک کنترلر استفاده کنیم:
namespace CoreIocSample02.Controllers
{
    public class HomeController : Controller
    {
        private readonly IMyDisposableService _myDisposableService;

        public HomeController(IMyDisposableService myDisposableService)
        {
            _myDisposableService = myDisposableService;
        }

        public IActionResult Index()
        {
            _myDisposableService.Run();
            return View();
        }
در اینجا منظور از نحو‌ه‌ی متداول، همان تزریق در سازنده‌ی کلاس و درخواست وهله‌ای از این سرویس از IoC Container است؛ بجای ایجاد مستقیم آن.
در ادامه با اجرای برنامه، اگر به لاگ‌های آن دقت کنیم، این خروجی قابل مشاهده خواهد بود:
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Route matched with {action = "Index", controller = "Home"}. Executing action CoreIocSample02.Controllers.HomeController.Index (CoreIocSample02)
info: CoreIocServices.MyDisposableService[0]
      + MyDisposableService was created
.
.
.  
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
      Executed endpoint 'CoreIocSample02.Controllers.HomeController.Index (CoreIocSample02)'
info: CoreIocServices.MyDisposableService[0]
      - MyDisposableService was disposed!
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 1316.4719ms 200 text/html; charset=utf-8
در ابتدای اجرای درخواست، پیام MyDisposableService was created ظاهر شده‌است (پیام صادر شده‌ی از سازنده‌ی سرویس) و جائیکه پیام Executed endpoint یا پایان درخواست جاری لاگ شده، بلافاصله پیام MyDisposableService was disposed نیز مشاهده می‌شود که از متد Dispose سرویس درخواستی صادر شده‌است.
بنابراین IoC Container، به صورت خودکار، کار Dispose این سرویس IDisposable را نیز انجام داده‌است.


Dispose خودکار وهله‌هایی که توسط IoC Container ایجاد نشده‌اند

اگر ایجاد اشیاء از نوع IDisposable را خودتان و خارج از دید IoC Container توکار ASP.NET Core انجام می‌دهید، از مزیت پاکسازی خودکار منابع توسط آن‌ها در پایان درخواست محروم خواهید شد، اما ... برای رفع این مشکل نیز متد context.Response.RegisterForDispose پیش بینی شده‌است. اگر شیءای از نوع IDisposable را توسط این متد به ASP.NET Core معرفی کنید، در پایان درخواست به صورت خودکار Dispose خواهد شد.
یک مثال: فرض کنید یک StreamWriter را داخل یک میان‌افزار ایجاد کرده‌اید، اما آن‌را Dispose نکرده‌اید:
namespace CoreIocSample02
{
    public class Startup
    {
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.Use(async (context, next) =>
            {
                var writer = File.CreateText(Path.GetTempFileName());
                context.Response.RegisterForDispose(writer);
                context.Items["filewriter"] = writer;
                await writer.WriteLineAsync("some important information");
                await writer.FlushAsync();
                await next();
            });
در این مثال، شیء writer به context.Items انتساب داده شده‌است تا در سایر قسمت‌های pipeline جاری نیز قابل دسترسی باشد. یعنی از آن می‌توان داخل یک اکشن متد نیز استفاده کرد. نکته‌ی مهم آن، معرفی این شیء به متد context.Response.RegisterForDispose است که سبب خواهد شد پس از پایان کار درخواست، به صورت خودکار writer را Dispose کند.
اکنون در ادامه، در اکشن متد WriteLog یک کنترلر دلخواه، کار ثبت وقایع با دریافت این writer از HttpContext.Items قابل انجام است؛ چون هنوز طول عمر درخواست جاری پایان نیافته و شیء writer به صورت خودکار Dispose نشده‌است:
namespace CoreIocSample02.Controllers
{
    public class HomeController : Controller
    {
        public async Task<IActionResult> WriteLog()
        {
            var writer = HttpContext.Items["filewriter"] as StreamWriter;
            if (writer != null)
            {
                await writer.WriteLineAsync("more important information");
                await writer.FlushAsync();
            }
            return View();
        }
 
روش صحیح Dispose اشیایی با طول عمر Scoped، در خارج از طول عمر یک درخواست ASP.NET Core

زمانیکه به صورت متداولی از سیستم تزریق وابستگی‌های ASP.NET Core استفاده می‌کنیم، به ازای هر درخواست HTTP رسیده، یک Scope از نوع IServiceScopeFactory ایجاد می‌شود و با پایان درخواست، این Scope نیز Dispose خواهد شد. به این ترتیب هر سرویس ایجاد شده‌ی درون این Scope نیز Dispose می‌شود؛ کاری شبیه به عملیات زیر:
using(var scope = serviceProvider.CreateScope())
{
   var provider = scope.ServiceProvider;
   var resolvedService = provider.GetRequiredService(someType);
   // Use resolvedService...
}
در این بین سرویس‌های Singleton به هیچ Scope ای منتسب نمی‌شوند و طول عمر آن‌ها توسط root container مدیریت می‌شود و زمانیکه این ServiceProvider یا root container به پایان کار خودش برسد، با dispose شدن آن، سرویس‌های Singleton آن نیز dispose خواهند شد.

مشکل! اگر از سرویس فرضی IOperationScoped با طول عمر Scoped در متدهای مختلف کلاس آغازین برنامه استفاده کنیم (مانند DbContext برنامه)، طول عمری را که دریافت خواهیم کرد singleton خواهد بود و نه Scoped؛ چون درون یک scopeFactory.CreateScope ایجاد شده‌ی به صورت خودکار توسط یک درخواست قرار نداریم. بنابراین هر درخواست وهله‌ای از سرویس IOperationScoped با طول عمر Scoped، تنها همان وهله‌ی ابتدایی آن‌را باز می‌گرداند و singleton رفتار می‌کند؛ چون scope ایی ایجاد و تخریب نشده‌است.
در یک چنین مواردی، برای اطمینان حاصل کردن از dispose شدن سرویس در پایان کار، نیاز است مراحل ایجاد scope و dispose آن‌را به صورت دستی به نحو ذیل مدیریت کنیم:
public void Configure(IApplicationBuilder app, 
                      ILoggerFactory loggerFactory, 
                      IServiceScopeFactory scopeFactory) 
{
   using (var scope = scopeFactory.CreateScope()) 
   { 
     var initializer = scope.ServiceProvider.GetService<IOperationScoped>(); 
     initializer.SeedAsync().Wait(); 
   } 
}


Dispose کردن سرویس‌های IDisposable در برنامه‌های Console

اگر همین سرویس IMyDisposableService را در مثال برنامه‌ی کنسول قسمت اول استفاده کنیم:
var myDisposableService = serviceProvider.GetService<IMyDisposableService>();
myDisposableService.Run();
در پایان کار برنامه، شاهد پیام MyDisposableService was disposed نخواهیم بود. به همین جهت در اینجا نیز می‌توانیم شبیه به کاری که در ASP.NET Core در پشت صحنه رخ می‌دهد، عمل کنیم:
در برنامه‌ی کنسول، کار ایجاد serviceProvider را خودمان انجام دادیم:
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
var serviceProvider = serviceCollection.BuildServiceProvider();
متد BuildServiceProvider خروجی از نوع کلاس ServiceProvider را دارد؛ با این امضاء:
namespace Microsoft.Extensions.DependencyInjection
{
    public sealed class ServiceProvider : IServiceProvider, IDisposable, IServiceProviderEngineCallback
    {
        public void Dispose();
        public object GetService(Type serviceType);
    }
}
همانطور که مشاهده کنید، کلاس ServiceProvider نیز اینترفیس IDisposable را پیاده سازی می‌کند. بنابراین برای آزاد سازی صحیح منابع وابسته‌ی به آن، باید متد Dispose آن‌را نیز فراخوانی کرد:
namespace CoreIocSample01
{
    class Program
    {
        static void Main(string[] args)
        {
            var serviceCollection = new ServiceCollection();
            ConfigureServices(serviceCollection);
            using (var serviceProvider = serviceCollection.BuildServiceProvider())
            {
                var myDisposableService = serviceProvider.GetService<IMyDisposableService>();
                myDisposableService.Run();

                var testService = serviceProvider.GetService<ITestService>();
                testService.Run();
            }
        }
در اینجا serviceProvider را داخل یک using statement قرار داده‌ایم. اینبار اگر برنامه را اجرا کنیم، پس از پایان کار برنامه، پیام MyDisposableService was disposed نیز ظاهر می‌شود. ServiceProvider ایجاد شده یا همان root container، در زمان Dispose، تمام اشیایی را هم که توسط آن مدیریت شده‌اند و نیاز به Dispose دارند، Dispose می‌کند.

کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: CoreDependencyInjectionSamples-02.zip
مطالب دوره‌ها
مثال - نمایش درصد پیشرفت عملیات توسط SignalR
برنامه‌های وب در سناریوهای بسیاری نیاز دارند تا درصد پیشرفت عملیاتی را به کاربران گزارش دهند. نمونه ساده آن، گزارش درصد پیشرفت میزان دریافت یک فایل است و یا اعلام درصد انجام یک عملیات طولانی از سمت سرور به کاربر. در ادامه قصد داریم این موضوع را توسط SignalR پیاده سازی کنیم.


نکته‌ای در مورد نگارش‌های مختلف SignalR
اگر برنامه شما قرار است دات نت 4 را پشتیبانی کند، آخرین نگارش SignalR که با آن سازگار است، نگارش 1.1.3 می‌باشد. بنابراین اگر دستور ذیل را اجرا کنید:
 PM> Install-Package Microsoft.AspNet.SignalR
SignalR 2 را نصب می‌کند که با دات نت 4 و نیم به بعد سازگار است.
اگر دستور ذیل را اجرا کنید، SiganlR 1.x را نصب می‌کند که با دات نت 4 به بعد سازگار است:
 PM> Install-Package Microsoft.AspNet.SignalR -Version 1.1.3
پیش فرض این مطلب نیز استفاده از نگارش 1.1.3 می‌باشد تا بازه بیشتری از وب سرورها را شامل شود.
با اینکار Microsoft.AspNet.SignalR.JS نیز به صورت خودکار نصب می‌گردد و به این ترتیب کلاینت جاوا اسکریپتی SiganlR نیز در برنامه قابل استفاده خواهد بود.


تنظیمات فایل Global.asax.cs

سطر فراخوانی متد RouteTable.Routes.MapHubs باید در ابتدای متد Application_Start فایل Global.asax.cs قرار گیرد (پیش از هر تنظیم دیگری). تفاوتی هم نمی‌کند که برنامه وب فرم است یا MVC. به این ترتیب مسیریابی‌های SignalR تنظیم شده و مسیر http://localhost/signalr/hubs قابل استفاده خواهد بود.


تنظیمات اسکریپت‌های سمت کلاینت مورد نیاز

پس از نصب بسته SignalR، سه اسکریپت ذیل باید به ابتدای صفحه وب اضافه شوند تا کلاینت‌های جاوا اسکریپتی SignalR بتوانند با سرور ارتباط برقرار کنند:
 <script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script>
<script src="Scripts/jquery.signalR-1.1.3.min.js" type="text/javascript"></script>
<script src="signalr/hubs" type="text/javascript"></script>
این تنظیمات نیز برای هر دو نوع برنامه‌های وب فرم و MVC یکسان است.


تعریف کلاس Hub برنامه

using Microsoft.AspNet.SignalR;

namespace WebFormsSample03.Common
{
    public class ProgressHub : Hub
    {
        /// <summary>
        /// این متد استاتیک تعریف شده تا در برنامه به صورت مستقیم قابل استفاده باشد
        /// یا می‌شد اصلا این متد تعریف نشود و از همان دریافت زمینه هاب در کنترلر استفاده گردد
        /// </summary>        
        public static void UpdateProgressBar(int value, string connectionId)
        {
            var ctx = GlobalHost.ConnectionManager.GetHubContext<ProgressHub>();
            ctx.Clients.Client(connectionId).updateProgressBar(value); //فراخوانی یک متد در سمت کلاینت
        }
    }
}
متدی که در کلاس هاب برنامه تعریف شده، از نوع استاتیک است. از این جهت که می‌خواهیم این متد را در خارج از این هاب و در یک کنترلر Web API فراخوانی کنیم. زمانیکه متدی به صورت استاتیک تعریف می‌شود، ارتباط آن با وهله جاری کلاس یا this قطع خواهد شد. به همین جهت نیاز است تا از طریق متد GlobalHost.ConnectionManager.GetHubContext مجددا به context کلاس هاب دسترسی پیدا کنیم.
البته تعریف این متد در اینجا ضروری نبود. حتی می‌شد بدنه کلاس هاب را خالی تعریف کرد و متد GetHubContext را مستقیما داخل یک کنترلر فراخوانی نمود.
متد UpdateProgressBar، مقدار value را به تنها یک کلاینت که Id آن مساوی connectionId دریافتی است، ارسال می‌کند. این کلاینت باید یک callback جاوا اسکریپتی را جهت تامین متد پویای updateProgressBar تدارک ببیند.


کلاس Web API کنترلر دریافت فایل‌ها

فرقی نمی‌کند که برنامه شما از نوع وب فرم است یا MVC. امکانات Web API در هر دو نوع پروژه، قابل دسترسی است (همان ایده یک ASP.NET واحد).
بنابراین نیاز است یک کنترلر وب API جدید را به پروژه اضافه کرده و محتوای آن را به شکل ذیل تغییر دهیم:
using System.Threading;
using System.Web.Http;
using WebFormsSample03.Common;

namespace WebFormsSample03
{
    public class DownloadRequest
    {
        public string Url { set; get; }
        public string ConnectionId { set; get; }
    }

    public class DownloaderController : ApiController
    {
        public void Post([FromBody]DownloadRequest data)
        {
            //todo: start downloading the data.Url ....

            ProgressHub.UpdateProgressBar(10, data.ConnectionId);
            Thread.Sleep(2000);

            ProgressHub.UpdateProgressBar(40, data.ConnectionId);
            Thread.Sleep(3000);

            ProgressHub.UpdateProgressBar(64, data.ConnectionId);
            Thread.Sleep(2000);

            ProgressHub.UpdateProgressBar(77, data.ConnectionId);
            Thread.Sleep(2000);

            ProgressHub.UpdateProgressBar(92, data.ConnectionId);
            Thread.Sleep(3000);

            ProgressHub.UpdateProgressBar(99, data.ConnectionId);
            Thread.Sleep(2000);

            ProgressHub.UpdateProgressBar(100, data.ConnectionId);
        }
    }
}
اگر برنامه شما وب فرم است، باید تنظیمات مسیریابی ذیل را نیز به آن افزود. در برنامه‌های MVC4 این تنظیم به صورت پیش فرض وجود دارد:
using System;
using System.Web.Http;
using System.Web.Routing;

namespace WebFormsSample03
{
    public class Global : System.Web.HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            // Register the default hubs route: ~/signalr
            RouteTable.Routes.MapHubs();

            RouteTable.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}
کاری که در این کنترلر انجام شده، شبیه سازی یک عملیات طولانی توسط متد Thread.Sleep است. همچنین این کنترلر، id کلاینت درخواست کننده یک url را نیز دریافت می‌کند. بنابراین می‌توان به نحو بهینه‌ای، تنها نتایج پیشرفت عملیات را به این کلاینت ارسال کرد و نه به سایر کلاینت‌ها.
همچنین در اینجا با توجه به مسیریابی تعریف شده، باید اطلاعات را به آدرس api/Downloader از نوع Post ارسال کرد.


تعریف کلاینت متصل به Hub

در سمت سرور، متد پویای updateProgressBar فراخوانی شده است. اکنون باید این متد را در سمت کلاینت پیاده سازی کنیم:
    <form id="form1" runat="server">
    <div>
    <input id="txtUrl" value="http://www.site.com/file.rar" type="text" />
        <input id="send" type="button" value="start download ..." />
        <br />
        <div id="bar" style="border: #000 1px solid; width:300px;"></div>
    </div>
    </form>
    <script type="text/javascript">
        $(function () {
            $.connection.hub.logging = true; //اطلاعات بیشتری را در جاوا اسکریپت کنسول مرورگر لاگ می‌کند
            var progressHub = $.connection.progressHub; //این نام مستعار پیشتر توسط ویژگی نام هاب تنظیم شده است
            progressHub.client.updateProgressBar = function (value) {
                //متدی که در اینجا تعریف شده دقیقا مطابق نام متد پویایی است که در هاب تعریف شده است
                //به این ترتیب سرور می‌تواند کلاینت را فراخوانی کند
                $("#bar").html(GaugeBar.generate(value));
            };
            $.connection.hub.start() // فاز اولیه ارتباط را آغاز می‌کند
            .done(function () {
                $("#send").click(function () {
                    $("#send").attr('disabled', 'disabled');
                    var myClientId = $.connection.hub.id;
                    // اکنون اتصال برقرار است به سرور
                    $.ajax({
                        type: "POST",
                        contentType: "application/json",
                        url: "/api/Downloader",
                        data: JSON.stringify({ Url: $("#txtUrl").val(), ConnectionId: myClientId })
                    }).success(function () {
                        $("#send").removeAttr('disabled');
                    }).fail(function () {
                        //                    
                    });
                });
            });
        });
    </script>
بر روی این فرم، یک جعبه متنی که Url را دریافت می‌کند و یک دکمه‌ی آغاز کار دریافت این Url، وجود دارد.
در ابتدای کار صفحه، اتصال به progressHub برقرار می‌شود. اگر دقت کنید، نام این هاب با حروف کوچک در اینجا (در سمت کلاینت) آغاز می‌گردد.
سپس با تعریف یک callback به نام progressHub.client.updateProgressBar، پیام‌های دریافتی از طرف سرور را به یک افزونه progress bar جی‌کوئری، برای نمایش ارسال می‌کند.
کار اتصال به رویداد کلیک دکمه‌ی آغاز دریافت فایل، در متد done باید انجام شود. این callback زمانی فراخوانی می‌گردد که کار اتصال به سرور با موفقیت صورت گرفته باشد.
سپس در ادامه توسط jQuery Ajax، اطلاعات Url و همچنین Id کلاینت را به مسیر api/Downloader یا همان web api controller ارسال می‌کنیم.



کدهای کامل این مثال را از اینجا نیز می‌توانید دریافت نمائید:
  WebFormsSample03.zip
بازخوردهای دوره
شروع به کار با RavenDB

با سلام و احترام

بیان کردید "البته لازم به ذکر است که RavenDB در 4 حالت برنامه کنسول (همین سرور فوق)، نصب به عنوان یک سرویس ویندوز NT، هاست شدن در IIS و حالت مدفون شده یا Embedded قابل استفاده است.
 " خواهش میکنم راجع به هر مورد توضیح دهید.

نظرات مطالب
EF Code First #1
سیلورلایت به صورت مستقیم با هیچ نوع ORM ایی کار نمی‌کند (چون یک فناوری سمت کاربر است). اما شما در سمت سرور می‌تونید به کمک یک WCF سرویس و مشتقات مشابه آن با EF یا NH و غیره کار کنید و سپس نتیجه را در یک برنامه سیلورلایت مصرف کنید.