نظرات مطالب
کار با کوکی‌ها در ASP.NET Core
یک نکته‌ی تکمیلی: ارتقاء به نگارش 3.1 و تغییر تنظیمات کوکی‌ها


مرورگر کروم، از نگارش 80 آن به بعد به همراه یک تغییر غیرسازگار با نگارش‌های قبلی آن است: از این پس تمام کوکی‌های آن در صورتیکه تنظیمات SameSite را نداشته باشند، به صورت SameSite=Lax تفسیر می‌شوند؛ که تغییر امنیتی فوق العاده‌ای است و سبب خواهد شد تا بسیاری از حملات مانند CSRF به طور کامل غیرعملی شوند. اما ... این مورد سبب از کار افتادن برنامه‌هایی خواهد شد که از سرویس‌هایی مانند IdentityServer استفاده می‌کنند. در یک چنین حالتی نیاز خواهید داشت برای رفع این مشکل، SameSite=None را تنظیم کنید.

اما SameSite چیست؟ تنظیم و خاصیت SameSite از سال 2016 به کوکی‌ها اضافه شد تا بتوان توسط آن در برابر حملات CSRF مقاومت کرد؛ مقادیر اولیه‌ی آن نیز Lax و Strict بودند. Lax به این معنا است که کوکی‌ها در حین مرور یک سایت، باید به صورت خودکار به سمت سرور همان سایت ارسال شوند؛ اما در حالت مرور سایت و هدایت از طریق سایت‌های دیگر به سایت ما، فقط درخواست‌های GET رسیده می‌توانند کوکی‌ها را نیز ارسال کنند. حالت Strict آن فقط کوکی‌های تنظیم شده‌ی درون یک سایت را معتبر شمرده و ارسال می‌کند. عدم تنظیم SameSite نیز مشکل خاصی را ایجاد نمی‌کرد. برای مثال اعمال اعتبارسنجی مبتنی بر OpenIdConnect مانند login/logout، نیاز دارند در طی یک درخواست POST، اطلاعاتی را به سایت خارجی درخواست کننده ارسال کنند. برای اینکه این عملیات به درستی صورت گیرد، می‌بایستی تنظیمات SameSite انجام نمی‌شد تا جابجایی کوکی‌ها بین دو دومین مختلف در حالت POST، بدون مشکل صورت می‌گرفت.
با تغییر جدید گوگل، حالت پیش‌فرض SameSite که اجباری نبود، به صورت اجباری به Lax تنظیم شده‌است؛ اما برای حالاتی مانند OpenIdConnect، مقدار None را نیز اضافه کرده‌است. به همین جهت این نوع برنامه‌ها اگر از تنظیم SameSite=None استفاده نکنند، دیگر نمی‌توانند کوکی‌های درخواست‌های POST را بین دومین‌های مختلف جابجا کنند.

مشکل مهم! مقدار None را فقط مرورگر کروم متوجه می‌شود و جزو استاندارد SameSite نیست! در این استاندارد اگر مقدار SameSite تنظیم شود و مرورگر نتواند آن‌را تشخیص دهد (مانند iOS 12)، مقدار Strict را به عنوان مقدار دریافتی تنظیم می‌کند! به همین جهت برنامه‌ی شما باید بر اساس نوع مرورگر تصمیم‌گیری کند که آیا باید SameSite را به خروجی اضافه کند یا خیر.

وضعیت دات نت در این مورد: با به روز رسانی‌های جدید دات نت 4.7.2 و همچنین NET Core 2.1.، مقدار جدید None توسط برنامه (در CookieOptions) قابل تنظیم خواهد بود که سبب تولید SameSite=None می‌شود. به علاوه در NET Core 3.1.،  مقدار SameSite.Unspecified را نیز می‌توان تنظیم کرد که سبب خواهد شد تا خاصیت SameSite اصلا تنظیم نشود (اصلا به درخواست اضافه نشود؛ تا مرورگرهایی که نمی‌توانند مقدار None را تفسیر کنند، به اشتباه آن‌را به Strict تنظیم نکنند).

به صورت خلاصه: اگر برنامه‌ی شما از OpenIdConnect و یا IdentityServer استفاده نمی‌کند، هیچ تنظیمی را تغییر ندهید! تنظیم پیش‌فرض SameSite=Lax گوگل سبب می‌شود تا عملا حملات CSRF دیگر بر روی سایت شما قابل اجرا نباشد. اما اگر از IdentityServer استفاده می‌کنید، این تنظیم پیش‌فرض، سبب از کار افتادن امکان ارسال کوکی‌های حالت POST، به سایت‌های خارجی می‌شود و برنامه و سیستم شما از کار خواهد افتاد.
بازخوردهای پروژه‌ها
مدیریت پروژه
قبل از هر چیز سپاس گذارم از وب سایت بسیار قوی شما 
صحبتم را با یک مثال پیش می‌برم 
فرض کنیم یک پروژه کوچک مثل فروشگاه اینترنتی یا برنامه مدیرت املاک را یک تیم 4 نفره می‌خواهند انجام دهند پروژه باید چگونه تقسیم بندی بشه . حالا تقسیم بندی شد هر کسی در سیستم خودش داره قسمت مورد نظر را توسعه میده پایان کار چگونه باید پروژه اسمبل بشه و مباحثی در مورد مدیرت پروژه به صورت کاربردی مثل مطالب سایتتون البته بازم تکرار می‌کنم من مباحثی مانند Scrum و Uml نیاز ندارم . مدیرت پروژه تیمی ونحوه کار هر یک از اعضا - و نهایتا چگونگی اسمبل کردن پروژه در یک vs . با تشکر 
مطالب
Ef 6 و Ngen : شروعی سریعتر برای برنامه های مبتنی بر Entity Framework
تولید کد Native زمانی اتفاق می‌افتد که کامپایلر JIT، کد اسمبلی‌های MSIL را به کدهای Native در ماشین محلی کامپایل می‌کند و این عمل بلافاصله قبل از اجرای متد برای اولین بار اتفاق می‌افتد. این کد به صورت موقتی بوده و در حافظه‌ای که برای پردازش در نظر گرفته شده ذخیره می‌شود و در پایان هر پردازش توسط سیستم عامل ویرایش می‌شود. کد Native به ازای هر بار شروع یک پردازش تولید می‌شود. ابزار Native Image Generator یا همان Ngen اقدام به تولید کد Native با استفاده از کامپایلر JIT نموده و آن را در هارد دیسک ذخیره می‌نماید. زمانیکه برنامه نیازمند یک اسمبلی CLR است، به جای بارگذاری خود اسمبلی، ایمیج کد Native آن بارگذاری می‌شود. به این نکته نیز توجه داشته باشید که CLR اطلاعاتی در مورد اینکه کدام اسمبلی، ایمیج کد Native است و این ایمیج در کجا و در چه زمانی تهیه شده است، دارد. کد Native باعث بهبود استفاده از حافظه می‌شود، زمانیکه یک اسمبلی بین پروسس‌ها به اشتراک گذاشته شده‌است. تا قبل از EF6 کتابخانه‌های هسته‌ای EF در زمان اجرا جزئی از دات نت فریمورک بودند و تولید کد Native آنها به صورت اتوماتیک انجام می‌شد. اما از نسخه 6، تمامی این کتابخانه‌ها در داخل پکیج Nuget آن ترکیب شده‌اند . پس برای تولید کد Native مربوط به فایل EntityFramework.dll نیازمند ابزار Ngen هستیم.
1- ابتدا یک برنامه‌ی ساده کنسول ویندوز ساخته و از Package Manager Console دستور Install-package entityframework را اجرا نموده تا پکیج Ef به برنامه اضافه گردد.
using System;
using System.Data.Entity;

namespace UsingNgen
{

    public class NgenDbContex : DbContext
    { }

    class Program
    {
        static void Main()
        {
            var nGenCtx = new NgenDbContex();
            Console.WriteLine("Press a key to exit...");
            Console.ReadKey();
        }
    }


}
حال کد ساده بالا را به برنامه اضافه می‌کنیم و برنامه را Build میکنیم.
2- برای ثبت جزئیات اجرای برنامه از ابزار Windows Performance Recorder که جزئی از ویندوز می‌باشد، استفاده می‌کنیم. کافیست عبارت WPR را در نوار جستجوی ویندوز تایپ کنید تا این ابزار در دسترس قرار گیرد. 



برای ضبط جزئیات، روی دکمه‌ی Start کلیک کنید و به محل ذخیره‌ی فایل اجرایی حاصل از Build ویژوال استودیو رفته و آن را اجرا کنید. بعد از اتمام اجرا، جزئیات را ذخیره نمایید.

بعد از ذخیره فایل، در پنجره بالا دکمه‌ای به نام Open in WPA ظاهر می‌شود. WPA مخفف Windows Performance Analyzer می‌باشد. آن را کلیک کنید تا محیط آنالایزر باز شود.

حال در سمت چپ این پنجره انواع آنالایزرها را مشاهده می‌کنید. روی آنالایزر Computation کلیک کنید و از زیرمجموعه‌ی آن، CPU Usage را انتخاب کنید. آمار مربوط به برنامه خودمان را در تصویر بالا مشاهده می‌کنید. کل برنامه 164 میلی ثانیه زمان برده و فایل Clr.dll حدود 47 میلی ثانیه و یک فایل clrjit.dll نیز برای تولید کد JIT وجود دارد. حال برای تسریع در عمل شروع، از تکنیک Ngen به صورت زیر استفاده می‌کنیم.

3- دوباره به نوار جستجوی ویندوز رفته و ابزار Developer Command Prompt for VsXXXX را با امتیاز دسترسی از نوع Admin اجرا کنید. XXXX نسخه‌ی ویژوال استودیو می‌باشد.

حال به محل ذخیره فایل اجرایی برنامه رفته و دستور Ngen Install EntityFramework.dll را تایپ کنید تا یک ایمیج کد Native از entityframework.dll ساخته شود. دوباره ابزار Windows Performance Recorder را لود کرده و روی دکمه Start کلیک کنید و فایل اجرایی برنامه را اجرا نمایید. پس از اتمام عملیات ثبت جزئیات، آن را در Windows Performance Analyzer باز نمایید.

همانطور که مشاهده می‌کنید کل برنامه ما 89 میلی ثانیه زمان برده و Clr.dll 29 ثانیه و به جای clrjit.dll فایل EntityFramework به صورت native تولید شده است.

نظرات مطالب
تولید SiteMap استاندارد و ایجاد یک ActionResult اختصاصی برای Return کردن SiteMap تولید شده
با سلام؛ چند سوال داشتم:
1- به نظرتون اگر تعداد صفحات وب سایت زیاد باشه این کار (ساخت sitemap به ازای هر درخواست) رو سرور فشار ایجاد نمی‌کنه؟
2 -همان طور که می‌دونید حداکثر می‌شود 50000 هزار لینک در یک sitemap  قرار داد و به ازای مقادیر بیشتر باید چندین sitemap ایجاد کرد. 
برای این سناریو چطور می‌شه رفتار کرد؟
3- و سوال آخر اینکه چرا آقای نصیری لینک‌های محدودی را در sitemap  سایت قرار داده اند؟ دلیل خاصی داشته است؟
نظرات مطالب
CheckBoxList در ASP.NET MVC
- روش شما هم خوبه. یعنی نهایتا همان SelectListItem بحث شده در مطلب فوق رو ایجاد و استفاده کردید. مطلبی که عنوان کردم در مورد قسمت اصلی پیاده سازی checkbox list بود.
- ضمن اینکه در کدهای شما به نظر می‌رسه از الگوی واحد کار استفاده نشده. شما در هر متدی یک new دارید. یعنی یک اتصال به بانک اطلاعاتی. این مورد در برنامه‌های وب می‌تونه سرعت کار و بار بانک اطلاعاتی رو (بسته به تعداد کاربر) به شدت بالا ببره.
کار الگوی واحد کار، مدیریت یک Context به ازای تمام متدهای درگیر است. یک کانکشن و یک تراکنش به ازای n متد و n کلاسی که برای انجام یک کار مورد نیاز هستند.
در این مورد قبلا مفصل توضیح دادم .


نظرات مطالب
ASP.NET MVC #13
برخلاف وب فرم‌ها شما در MVC محدود به یک فرم در صفحه نیستید. این مساله سبب میشه که بتوان اعتبار سنجی یک مدل را به ازای هر فرم تعریف شده در صفحه به صورت جداگانه انجام داد. به این ترتیب بجای اینکه کل گرید را در یک فرم تعریف کنید، هر سطر آن باید در یک فرم قرار گیرد یا قسمتی که قرار است به سرور ارسال شود باید در یک فرم قرار گیرد. به این صورت اعتبار سنجی از راه دور توضیح داده شده در بالا بدون مشکل کار خواهد کرد چون الان به ازای هر قسمتی که قرار است ویرایش شود یک فرم دارید و اطلاعات مدل متناظر با آن فرم به یک action method ارسال خواهد شد. مابقی مسایل یکی است و فرقی نمی‌کند.
مطالب
پیاده سازی ماژولار Autofac
یکی از مشکلاتی که در برخی از طراحی‌هایی که تا کنون دیده‌ام وجود دارد، عدم استفاده از قابلیت ماژولار نویسی تنظیمات Autofac  و عدم استفاده از Interfaceها برای ارتباط بین قسمت‌های مختلف سیستم است. به این صورت که تمام تنظیمات مربوط به Autofac را در بالاترین لایه سمت سرور خود یعنی Service یا Web انجام می‌دهند که باعث می‌شود این لایه به تمامی لایه‌های پایین خود از جمله DataAccess دسترسی مستقیم داشته باشد. در یک سیستم بزرگ به دلایل بسیار از جمله حساسیت داده‌ها، مدیریت درست بر روی قسمت‌های مختلف و یکبار نویسی هر قسمت، بهتر است تمام تغییرات از فیلتر Business و بصورت مدیریت شده بر روی داده‌های ما صورت بپذیرد. در این نوع سیستم ما نمی‌توانیم دسترسی مستقیمی را به لایه DataAccess به تمام توسعه دهندگان بدهیم و امیدوار باشیم که کسی از آن استفاده نکند. برای رفع این مشکل می‌توانیم تنظیمات قسمت Autofac را در هر لایه بصورت جداگانه انجام دهیم. به اینصورت که لایه‌های پایین‌تر اطلاعات خود را تنها در اختیار لایه‌هایی که باید به آنها دسترسی داشته باشند، قرار می‌دهند و در نهایت با تجمیع این اطلاعات در بالاترین لایه می‌توانیم بصورت کنترل شدهی از آنها استفاده کنیم. دسترسی هر قسمت نیز تنها می‌تواند از طریق Interface هایی که در اختیار سایر قسمت‌ها گذاشته می‌شود صورت بپذیرد. به این صورت می‌توان از طریق Interfaceها دسترسی‌های کنترل شده‌ای را در اختیار سایر قسمتهایی که بصورت مستقیم یا غیر مستقیم به لایه مربوطه دسترسی دارند، قرار دهیم. در اینجا نکته دیگری را که باید در نظر بگیرید این است که هدف اصلی DI، حذف وابستگی‌های کامل و تبدیل آنها به وابستگی‌های محدود است و این عمل از طریق Interface‌ها صورت می‌پذیرد. پس قاعدتا نباید بی دلیل بصورت مستقیم از کلاسها استفاده شود، یا آنها را در اختیار سایر لایه‌ها قرار بدهیم. تمام ارتباطات می‌توانند از طریق Interface‌ها بصورت کاملا کنترل شده انجام بپذیرند.



طراحی لایه DataAccess

در این نوع طراحی، هر لایه تنها از طریق Interfaceها به لایه بالاتر از خودش سرویس می‌دهد و در مواردی که ما در نظر بگیریم، می‌توانیم به لایه‌های دیگر نیز دسترسی‌های غیر مستقیم و کنترل شده‌ای را بدهیم. بطور مثال هر کلاس Repository می‌تواند به‌صورت Internal تعریف شود؛ پس تنها در لایه DataAccess در دسترس است. برای دسترسی سایر لایه‌ها به Repository‌ها، هر Repository می‌تواند از یک IRepository (این Interface دسترسی خواندنی نوشتنی به کلاس Repository دارد) که در لایه DataAccess بصورت public تعریف شده و تنها در لایه Business قابل دسترس است و یک IRepositoryReadOnly (این Interface دسترسی فقط خواندنی به کلاس Repository دارد) که در قسمت Common تعریف شده، ارث ببرد. به این صورت همان طور که در شکل بالا نیز می‌بینید، دسترسی‌هایی که از بیرون به لایه DataAccess صورت می‌گیرد، به دو قسمت تقسیم می‌شوند: اول دسترسی کامل به IRepository که تنها از طریق Business صورت می‌پذیرد و دوم دسترسی از طریق IRepositoryReadOnly که می‌تواند از هر جایی در سیستم که به قسمت Common دسترسی دارد صورت بپذیرد (البته استفاده از IRepositoryReadOnly به سیاست‌هایی که شما در نظر می‌گیرید بستگی دارد). با این کار مطمئن می‌شوید که تغییرات تنها می‌توانند از طریق Business صورت بپذیرند. همچنین حتی در صورتیکه نیاز بدانید، یک دسترسی فقط خواندنی را نیز به توسعه دهندگان سایر قسمت‌ها داده‌اید.

حال با توجه به توضیحات فوق، تنظیمات مربوط به Autofac را در لایه DataAccess انجام می‌دهیم.


طراحی تنظیمات Autofac در لایه DataAccess

public class DataAccessSetupDependency : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            base.Load(builder);
            builder.RegisterType<EFContext>().As<IDbContext>();
            builder.RegisterType<UnitOfWork>().As<IUnitOfWork>();
            builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
             .Where(x => x.Namespace.EndsWith("Repositories"))
             .AsImplementedInterfaces();
        }
    }
همانطور که می‌بینید در این قسمت تنها تنظیمات وابستگی‌های لایه DataAccess در ماژول DataAccessSetupDependency انجام شده‌است.


طراحی لایه Business

در لایه Business نیز دسترسی‌های خود را تنها از طریق Interface هایی که کلاسهای Business از آنها ارث برده‌اند و آنها را پیاده سازی کرده‌اند، به قسمت Web می‌دهید.

طراحی تنظیمات Autofac در لایه Business

public class BusinessSetupDependency : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            base.Load(builder);

            builder.RegisterAssemblyModules(typeof(SqlServerDataAccess.SetupDependencies.DataAccessSetupDependency).Assembly);

            //builder.RegisterAssemblyModules(typeof(CassandraDataAccess.SetupDependecies.SetupDependency).Assembly);

            builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
           .Where(x => x.Namespace.EndsWith("Business.Core"))
           .AsImplementedInterfaces();

            builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
            .Where(x => x.Namespace.EndsWith("Business.Businesses"))
            .AsImplementedInterfaces();
        }
    }
 همانطور که می‌بینید در ماژول BusinessSetupDependency ابتدا تنظیمات وابستگی‌های موجود در لایه DataAccess بدست آمده و در ادامه سایر تنظیمات موجود در لایه Business انجام شده‌اند.


طراحی لایه Web

جمع بندی تمام تنظیمات Autofac در لایه Web

public class WebSetupDependency : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            base.Load(builder);
            builder.RegisterAssemblyModules(typeof(Business.SetupDependencies.BusinessSetupDependency).Assembly);
            builder.RegisterControllers(Assembly.GetExecutingAssembly());
            //سایر تنظیمات
            var container = builder.Build();
            CommonDependencyResolver.SetContainer(container);
            DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

        }
    }
 public class AutofacConfig
    {
        public static void SetupContainer()
        {
             var builder = new ContainerBuilder();
            builder.RegisterAssemblyModules(Assembly.GetExecutingAssembly());
            var container = builder.Build();
            CommonDependencyResolver.SetContainer(container);
            DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
        }
    }
ماژول WebSetupDependency در واقع جمع بندی از تمام وابستگی‌های لایه‌های موجود در سیستم است. بصورتیکه ابتدا تنظیمات وابستگی‌های لایه Business که خود شامل تنظیمات وابستگی‌های لایه DataAccess نیز هست، بدست می‌آیند و سپس لایه Web تنظیمات مرتبط با خودش را بر روی آنها اعمال می‌کند.

همچنین کلاس AutofacConfig مسئول جمع بندی تمام ماژول‌ها و ایجاد Container آنهاست و سپس این Container را در DependencyResolver  ثبت می‌کند. نکته‌ای را که باید در اینجا در نظر بگیرید، CommonDependencyResolver است که مسئول ثبت Container در قسمت Common است. به این صورت دیگر تنها لایه‌ای که به Container دسترسی دارد، لایه Web نیست. در واقع با ثبت Container در قسمت Common شما دسترسی کنترل شده‌ای را از Container به سایر لایه‌های سیستم  داده‌اید و در این حالت در صورتیکه لایه‌های دیگر مانند DataAccess یا Business به Container نیاز پیدا کنند، می‌توانند از طریق CommonDependencyResolver این دسترسی را داشته باشند.


جمع بندی

با طراحی ماژولار تنظیمات Autofac و استفاده از Interface‌ها برای ارتباط با دیگر قسمتها، دیگر نیازی نیست دسترسی‌های بی‌موردی از یک لایه را  به سایر قسمت‌ها داد و دیگر نیازی نیست شما نگران این باشید که ممکن است یکی  از توسعه دهندگان به‌دلایلی مانند کم تجربگی، کاری را خارج از زیرساختی که شما یا گروه پیاده سازی زیرساخت، پیاده سازی کرده‌اید انجام دهد. همه چیز آنطور که شما می‌خواهید و برنامه ریزی کرده‌اید، انجام می‌شود.
مطالب
ساخت گزارش با استفاده از FastReport & Linq
یک روش ساده جهت ساخت گزارش به کمک FastReport استفاده از منبع داده ایجاد شده توسط Linq است. بعنوان نمونه در اینجا اطلاعات داخل یک فایل متنی (List.txt) ذخیره شده است. با استفاده از دستورات زبان Linq اطلاعات فایل متنی استخراج و داخل Query قرار گرفته است. یک نمونه از Report ایجاد و با استفاده از report.RegisterData منبع داده را به FastReport معرفی می‌کنیم. ابتدا از report.Design جهت طراحی گزارش استفاده و سپس با report.Load گزارش ساخته شده (در اینجا با نام List.frx ذخیره شده) را بارگذاری و توسط report.Show نمایش میدهیم 
محتوای فایل نمونه List.txt 


افزودن اسمبلی‌های مورد نیاز به مجموع Referencesها 

کد استفاده شده جهت طراحی گزارش 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using FastReport;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string[] List = System.IO.File.ReadAllLines("List.TXT");
            var Query = from list in List
                        let items = list.Split(',')
                        select new
                        {
                            Id = Convert.ToInt32(items[0]),
                            FName = items[1],
                            LName = items[2]
                        };

            using (Report report = new Report())
            {
                report.RegisterData(Query.ToList(), "myQuery");
                report.Design();
            }
        }
    }
}

نحوه شناسایی منبع و فیلدها در FastReport 

نمایش گزارش ذخیره شده در List.frx با استفاده از کد زیر

 report.Load("List.frx");

 report.Show();

خروجی گزارش ساخته شده

سورس برنامه نمونه

Linq_FastReport-sample.rar

مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت هفتم - سرویس‌ها و تزریق وابستگی‌ها
یک سرویس در AngularJS 2.0، کلاسی است با هدفی محدود و مشخص. این سرویس‌ها مستقل از کامپوننتی خاص هستند و هدف آن‌ها، به اشتراک گذاشتن اطلاعات و یا منطقی بین کامپوننت‌های مختلف می‌باشد. همچنین از آن‌ها برای کپسوله سازی تعاملات خارجی، مانند دسترسی به داده‌ها نیز استفاده می‌شود.


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

فرض کنید کلاس سرویسی، به نحو ذیل تعریف شده‌است:
 export class MyService {}
این کلاس، خارج از کلاس متناظر با یک کامپوننت قرار داد. بنابراین برای استفاده‌ی از آن، می‌توان آن‌را به صورت مستقیم، داخل کلاسی که به آن نیاز دارد، وهله سازی/نمونه سازی نمود و استفاده کرد:
 let svc = new MyService();
هر چند این روش کار می‌کند، اما نمونه‌ی ایجاد شده، سطح دسترسی محلی، در این کلاس دارد و در خارج آن قابل دسترسی نیست. بنابراین نمی‌توان از آن برای به اشتراک گذاشتن اطلاعات و منابع، بین کامپوننت‌های مختلف استفاده کرد.
همچنین در این حالت، mocking این سرویس برای نوشتن unit tests نیز مشکل می‌باشد.

راه بهتر و توصیه شده‌ی در اینجا، ثبت و معرفی این سرویس‌ها به AngularJS 2.0 است. سپس AngularJS 2.0 به ازای هر کلاس سرویس معرفی شده‌ی به آن، یک وهله/نمونه را ایجاد می‌کند. بنابراین طول عمر سرویس‌های ایجاد شده‌ی در این حالت، singleton است (یکبار ایجاد شده و تا پایان طول عمر برنامه زنده نگه داشته می‌شوند).
پس از آن می‌توان از تزریق کننده‌های توکار AngularJS 2.0، جهت تزریق وهله‌های این سرویس‌ها استفاده کرد.
اکنون اگر کلاسی، نیاز به این سرویس داشته باشد، نیاز خود را به صورت یک وابستگی تعریف شده‌ی در سازنده‌ی کلاس اعلام می‌کند:
 constructor(private _myService: MyService){}
در این حالت زمانیکه کلاس کامپوننت، برای اولین بار وهله سازی می‌شود، سرویس مورد نیاز آن نیز توسط تزریق کننده‌ی توکار AngularJS 2.0، در اختیارش قرار می‌گیرد.
به این فرآیند اصطلاحا dependency injection و یا تزریق وابستگی‌ها می‌گویند. در فرآیند تزریق وابستگی‌ها، یک کلاس، وهله‌های کلاس‌های دیگر مورد نیاز خودش را بجای وهله سازی مستقیم، از یک تزریق کننده دریافت می‌کند. بنابراین بجای نوشتن newها در کلاس جاری، آن‌ها را به صورت وابستگی‌هایی در سازنده‌ی کلاس تعریف می‌کنیم تا توسط AngularJS 2.0 تامین شوند.

با توجه به اینکه طول عمر این وابستگی‌ها singleton است و این طول عمر توسط AngularJS 2.0 مدیریت می‌شود، اطلاعات وهله‌های سرویس‌های مختلف و تغییرات صورت گرفته‌ی در آن‌ها، بین تمام کامپوننت‌ها به صورت یکسانی به اشتراک گذاشته می‌شوند.
به علاوه اکنون امکان mocking سرویس‌ها با توجه به عدم وهله سازی آن‌ها در داخل کلاس‌ها به صورت مستقیم، ساده‌تر از قبل میسر است.


مراحل ساخت یک سرویس در AngularJS 2.0

ساخت یک سرویس در AngularJS 2.0، با ایجاد یک کلاس جدید شروع می‌شود. سپس متادیتای آن افزوده شده و در آخر موارد مورد نیاز آن import خواهند شد. با این موارد پیشتر در حین ساختن یک کامپوننت جدید و یا یک Pipe جدید آشنا شده‌اید و این طراحی یک دست را در سراسر AngularJS 2.0 می‌توان مشاهده کرد.
اولین سرویس خود را با افزودن فایل جدید product.service.ts به پوشه‌ی app\products آغاز می‌کنیم؛ با این محتوا:
import { Injectable } from 'angular2/core';
import { IProduct } from './product';
 
@Injectable()
export class ProductService {
 
    getProducts(): IProduct[] {
        return [
            {
                "productId": 2,
                "productName": "Garden Cart",
                "productCode": "GDN-0023",
                "releaseDate": "March 18, 2016",
                "description": "15 gallon capacity rolling garden cart",
                "price": 32.99,
                "starRating": 4.2,
                "imageUrl": "app/assets/images/garden_cart.png"
            },
            {
                "productId": 5,
                "productName": "Hammer",
                "productCode": "TBX-0048",
                "releaseDate": "May 21, 2016",
                "description": "Curved claw steel hammer",
                "price": 8.9,
                "starRating": 4.8,
                "imageUrl": "app/assets/images/rejon_Hammer.png"
            }
        ];
    }
}
نام کلاس سرویس نیز pascal case است و بهتر است به کلمه‌ی Service ختم شود.
همانند سایر ماژول‌های تعریف شده‌، در اینجا نیز باید کلاس تعریف شده export شود تا در قسمت‌های دیگر قابل استفاده و دسترسی گردد.
سپس در این سرویس، یک متد برای بازگشت لیست محصولات ایجاد شده‌است.
در ادامه یک decorator جدید به نام ()Injectable@  به بالای این کلاس اضافه شده‌است. این متادیتا است که مشخص می‌کند کلاس جاری، یک سرویس AngularJS 2.0 است.
البته باید دقت داشت که این مزین کننده تنها زمانی نیاز است حتما قید شود که کلاس تعریف شده، دارای وابستگی‌های تزریق شده‌ای باشد. اما توصیه شده‌است که بهتر است هر کلاس سرویسی (حتی اگر دارای وابستگی‌های تزریق شده‌ای هم نبود) به این decorator ویژه، مزین شود تا بتوان طراحی یک دستی را در سراسر برنامه شاهد بود.
در آخر هم موارد مورد نیاز، import می‌شوند. برای مثال Injectable در ماژول angular2/core تعریف شده‌است.

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


ثبت و معرفی سرویس جدید ProductService به AngularJS 2.0 Injector

مرحله‌ی اول استفاده از سرویس‌های تعریف شده، ثبت و معرفی آن‌ها به AngularJS 2.0 Injector است. سپس این Injector است که تک وهله‌ی سرویس ثبت شده‌ی در آن‌را در اختیار هر کامپوننتی که آن‌را درخواست کند، قرار می‌دهد.
مرحله‌ی ثبت این سرویس، معرفی نام این کلاس، به خاصیتی آرایه‌ای، به نام providers است که یکی از خواص decorator ویژه‌ی Component است. بدیهی است هر کامپوننتی که در برنامه وجود داشته باشد، توانایی ثبت این سرویس را نیز دارد؛ اما باید از کدامیک استفاده کرد؟
اگر سرویس خود را در کامپوننت لیست محصولات رجیستر کنیم، تک وهله‌ی این سرویس تنها در این کامپوننت و زیر کامپوننت‌های آن در دسترس خواهند بود و اگر این سرویس را در بیش از یک کامپوننت ثبت کنیم، آنگاه دیگر هدف اصلی طول عمر singleton یک سرویس مفهومی نداشته و برنامه هم اکنون دارای چندین وهله از سرویس تعریف شده‌ی ما می‌گردد و دیگر نمی‌توان اطلاعات یکسانی را بین کامپوننت‌ها به اشتراک گذاشت.
بنابراین توصیه شده‌است که از خاصیت providers کامپوننت‌های غیر ریشه‌ای، صرفنظر کرده و سرویس‌های خود را تنها در بالاترین سطح کامپوننت‌های تعریف شده، یعنی در فایل app.component.ts ثبت و معرفی کنید. به این ترتیب تک وهله‌ی ایجاد شده‌ی در اینجا، در این کامپوننت ریشه‌ای و تمام زیر کامپوننت‌های آن (یعنی تمام کامپوننت‌های دیگر برنامه) به صورت یکسانی در دسترس قرار می‌گیرد.
به همین جهت فایل app.component.ts را گشوده و تغییرات ذیل را به آن اعمال کنید:
import { Component } from 'angular2/core';
import { ProductListComponent } from './products/product-list.component';
import { ProductService } from './products/product.service';
 
@Component({
    selector: 'pm-app',
    template:`
    <div><h1>{{pageTitle}}</h1>
        <pm-products></pm-products>
    </div>
    `,
    directives: [ProductListComponent],
    providers: [ProductService]
})
export class AppComponent {
    pageTitle: string = "DNT AngularJS 2.0 APP";
}
در اینجا دو تغییر جدید صورت گرفته‌اند:
الف) خاصیت providers که آرایه‌ای از سرویس‌ها را قبول می‌کند، با ProductService مقدار دهی شده‌است.
ب) در ابتدای فایل، ProductService، از ماژول آن import گردیده‌است.


تزریق سرویس‌ها به کامپوننت‌ها

تا اینجا یک سرویس جدید را ایجاد کردیم و سپس آن‌را به AngularJS 2.0 Injector معرفی نمودیم. اکنون نوبت به استفاده و تزریق آن، به کلاسی است که به این وابستگی نیاز دارد. در TypeScript، تزریق وابستگی‌ها در سازنده‌ی یک کلاس صورت می‌گیرند. هر کلاس، دارای متد سازنده‌ای است که در زمان وهله سازی آن، اجرا می‌شود. اگر نیاز به تزریق وابستگی‌ها باشد، تعریف این سازنده به صورت صریح، ضروری است. باید دقت داشت که هدف اصلی از متد سازنده، آغاز و مقدار دهی متغیرها و وابستگی‌های مورد نیاز یک کلاس است و باید تا حد امکان از منطق‌های طولانی عاری باشد.
در ادامه فایل product-list.component.ts را گشوده و سپس سازنده‌ی ذیل را به آن اضافه کنید:
import { ProductService } from './product.service';
export class ProductListComponent implements OnInit {
    pageTitle: string = 'Product List';
    imageWidth: number = 50;
    imageMargin: number = 2;
    showImage: boolean = false;
    listFilter: string = 'cart';
 
    constructor(private _productService: ProductService) {
    }
سازنده‌ی کلاس عموما پس از لیست خواص آن کلاس تعریف می‌شود و پیش از تعاریف سایر متدهای آن.
روش خلاصه شده‌ای که در اینجا جهت تعریف سازنده‌ی کلاس و متغیر تعریف شده‌ی در آن بکار گرفته شده، معادل قطعه کد متداول ذیل است و هر دو حالت ذکر شده، در TypeScript یکی می‌باشند:
private _productService: ProductService;
constructor(productService: ProductService) {
   _productService = productService;
}
در اینجا سرویس مورد نیاز را به صورت یک متغیر private در سازنده‌ی کلاس ذکر می‌کنیم (مرسوم است متغیرهای private با _ شروع شوند). همچنین این سرویس باید در لیست import ابتدای ماژول جاری نیز ذکر شود.
این وابستگی در اولین باری که کلاس کامپوننت، توسط AngularJS 2.0 وهله سازی می‌شود، از لیست providers ثبت شده‌ی در کامپوننت ریشه‌ی سایت، تامین خواهد شد.
اکنون نوبت به استفاده‌ی از این سرویس تزریق شده‌است. به همین جهت ابتدا لیست عناصر آرایه‌ی خاصیت products را حذف می‌کنیم (برای اینکه قرار است این سرویس، کار تامین اطلاعات را انجام دهد و نه کلاس کامپوننت).
 products: IProduct[];
خوب، در ادامه، کدهای مقدار دهی آرایه‌ی products را از سرویس دریافتی، در کجا قرار دهیم؟ شاید عنوان کنید که در همین متد سازنده‌ی کلاس نیز می‌توان این‌کار را انجام داد.
 this.products = _productService.getProducts();
هر چند در مثال جاری که از یک آرایه‌ی از پیش تعریف شده، برای این مقصود استفاده می‌شود، این مقدار دهی مشکلی را ایجاد نخواهد کرد، اما در قسمت بعدی که می‌خواهیم آن‌را از سرور دریافت کنیم، فراخوانی متد getProducts، اندکی زمانبر خواهد بود. بنابراین رویه‌ی کلی این است که کدهای زمانبر، نباید در سازنده‌ی یک کلاس قرار گیرند؛ چون سبب تاخیر در بارگذاری تمام قسمت‌های آن می‌شوند.
به همین جهت روش صحیح انجام این مقدار دهی، با پیاده سازی life cycle hook ویژه‌ای به نام OnInit است که در قسمت پنجم آن‌را معرفی کردیم:
export class ProductListComponent implements OnInit {
products: IProduct[];

constructor(private _productService: ProductService) {
}

ngOnInit(): void {
    //console.log('In OnInit');
    this.products = this._productService.getProducts();
}
هر نوع عملیات آغازین مقدار دهی متغیرها و خواص کامپوننت‌ها باید در ngOnInit مربوط به هوک OnInit انجام شود که نمونه‌ای از آن‌را در کدهای فوق ملاحظه می‌کنید.
در اینجا اکنون خاصیت products عاری است از ذکر صریح عناصر تشکیل دهنده‌ی آن. سپس وابستگی مورد نیاز، در سازنده‌ی کلاس تزریق شده‌است و در آخر، در رویداد چرخه‌ی حیات ngOnInit، با استفاده از این وابستگی تزریقی، لیست محصولات دریافت و به خاصیت عمومی products نسبت داده شده‌است.

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


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


خلاصه‌ی بحث
فرآیند کلی تعریف یک سرویس AngularJS 2.0، تفاوتی با ساخت یک کامپوننت یا Pipe سفارشی ندارد. پس از تعریف کلاسی که نام آن ختم شده‌ی به Service است، آن‌را مزین به ()Injectable@ می‌کنیم. سپس این سرویس را در بالاترین سطح کامپوننت‌های موجود یا همان کامپوننت ریشه‌ی سایت، ثبت و معرفی می‌کنیم؛ تا تنها یک وهله از آن توسط AngularJS 2.0 Injector ایجاد شده و در اختیار تمام کامپوننت‌های برنامه قرار گیرد. البته اگر این سرویس تنها در یک کامپوننت استفاده می‌شود و قصد به اشتراک گذاری اطلاعات آن‌را نداریم، می‌توان سطح سلسله مراتب دسترسی به آن‌را نیز کاهش داد. برای مثال این سرویس را در لیست providers همان کامپوننت ویژه، ثبت و معرفی کرد. به این ترتیب تنها این کامپوننت خاص و فرزندان آن دسترسی به امکانات سرویس مدنظر را می‌یابند و نه تمام کامپوننت‌های دیگر تعریف شده‌ی در برنامه.
در ادامه هر کلاسی که به این سرویس نیاز دارد (با توجه به سلسه مراتب دسترسی ذکر شده)، تنها کافی است در سازنده‌ی خود، این وابستگی را اعلام کند تا توسط AngularJS 2.0 Injector تامین گردد.