پاسخ به بازخورد‌های پروژه‌ها
گزارش برای کاغذ های از پیش طراحی شده
- با open office نمیشه این نوع گزارشات پویا تا این حد رو تهیه کرد. برای تهیه مثلا ساختار سلول‌های پیچیده یا برای نمونه چاپ یک کارت پرسنلی و امثال آن مفید است.
- با PdfReport قابل انجام است. نیاز به کاغذ از پیش آماده ندارد. کل این گزارش را می‌شود با تمام طرح و فونت و عکس و غیره آن در PdfReport تهیه کرد.
و یا ... اگر کاغذ آماده است
الف) اندازه کاغذ را در ساختار صفحه مشخص کنید
ب) نوع قالب گرید را transparent انتخاب کنید (تا دیگر خطوط گرید بر روی کاغذ از پیش آماده شده چاپ نشود).
ج) margin ساختار صفحه رو دقیقا اندازه گیری و اعمال کنید
د) اندازه سلول‌ها را چون ثابت است به همین نحو اندازه گیری کرده و مشخص کنید
بازخوردهای پروژه‌ها
نمایش چندی خطی یک فیلد
با سلام
در گزارشی قصد دارم، تا یک متن نسبتا بلند را که با استفاده از Enviroment.NewLine خط‌های آن تشکیل شده است را نمایش دهم. با استفاده از TextBlock این متن نشان داده می‌شود :

منتهی همانطور که مشاهده می‌کنید فاصله خطوط بسیار کم و متن کمی مبهم شده است.

از Html نیز استفاده کردم ولی مشکلات خاصی داشتم مانند اینکه هر فونتی را قبول نمی‌کرد و غیره، 

<p style='font-family:tahoma;line-height:20px'>فاکتور فروش شماره 4<br />موبایل Samsung یک عدد 5000000</p>

وگرنه با قالب فوق نیز متن فرمت کردیم و نشان دادیم.

آیا راه حلی به غیر از فرمت html در همان حالت معمولی textblock داریم یا خیر؟

مطالب دوره‌ها
مدل سازی داده‌ها در RavenDB
در مطلب جاری، به صورت اختصاصی، مبحث مدل سازی اطلاعات و رسیدن به مدل ذهنی مرسوم در طراحی‌های NoSQL سندگرا را در مقایسه با دنیای Relational، بررسی خواهیم کرد.


تفاوت‌های دوره ما با زمانیکه بانک‌های اطلاعاتی رابطه‌ای پدیدار شدند

- دنیای بانک‌های اطلاعاتی رابطه‌ای برای Write بهینه سازی شده‌اند؛ از این جهت که تاریخچه پیدایش آن‌ها به دهه 70 میلادی بر می‌گردد، زمانیکه برای تهیه سخت دیسک‌ها باید هزینه‌های گزافی پرداخت می‌شد. به همین جهت الگوریتم‌ها و روش‌های بسیاری در آن دوره ابداع شدند تا ذخیره سازی اطلاعات، حجم کمتری را به خود اختصاص دهند. اینجا است که مباحثی مانند Normalization بوجود آمدند تا تضمین شود که داده‌ها تنها یکبار ذخیره شده و دوبار در جاهای مختلفی ذخیره نگردند. جهت اطلاع در سال 1980 میلادی، یک سخت دیسک 10 مگابایتی حدود 4000 دلار قیمت داشته است.
- تفاوت مهم دیگر دوره ما با دهه‌های 70 و 80 میلادی، پدیدار شدن UI و روابط کاربری بسیار پیچیده، در مقایسه با برنامه‌های خط فرمان یا حداکثر فرم‌های بسیار ساده ورود اطلاعات در آن زمان است. برای مثال در دهه 70 میلادی تصور UI ایی مانند صفحه ابتدایی سایت Stack overflow احتمالا به ذهن هم خطور نمی‌کرده است.


تهیه چنین UI ایی نه تنها از لحاظ طراحی، بلکه از لحاظ تامین داده‌ها از جداول مختلف نیز بسیار پیچیده است. برای مثال برای رندر صفحه اول سایت استک اورفلو ابتدا باید تعدادی سؤال از جدول سؤالات واکشی شوند. در اینجا در ذیل هر سؤال نام شخص مرتبط را هم مشاهده می‌کنید. بنابراین اطلاعات نام او، از جدول کاربران نیز باید دریافت گردد. یا در اینجا تعداد رای‌های هر سؤال را نیز مشاهده می‌کنید که به طور قطع اطلاعات آن در جدول دیگری نگه داری می‌شود. در گوشه‌ای از صفحه، برچسب‌های مورد علاقه و در ذیل هر سؤال، برچسب‌های اختصاصی هر مطلب نمایش داده شده‌اند. تگ‌ها نیز در جدولی جداگانه قرار دارند. تمام این قسمت‌های مختلف، نیاز به واکشی و رندر حجم بالایی از اطلاعات را دارند.
- تعداد کاربران برنامه‌ها در دهه‌های 70 و 80 میلادی نیز با دوره ما متفاوت بوده‌اند. اغلب برنامه‌های آن دوران تک کاربره طراحی می‌شدند؛ با بانک‌های اطلاعاتی که صرفا جهت کار بر روی یک سیستم طراحی شده بودند. اما برای نمونه سایت استک اور فلویی که مثال زده شده، توسط هزاران و یا شاید میلیون‌ها نفر مورد استفاده قرار می‌گیرد؛ با توزیع و تقسیم اطلاعات آن بر روی سرورها مختلف.


معرفی مفهوم Unit of change

همین پیچیدگی‌ها سبب شدند تا جهت ساده‌سازی حل اینگونه مسایل، حرکتی به سمت دنیای NoSQL شروع شود. ایده اصلی مدل سازی داده‌ها در اینجا کم کردن تعداد اعمالی است که باید جهت رسیدن به یک نتیجه واحد انجام داد. اگر قرار است یک سؤال به همراه تگ‌ها، اطلاعات کاربر، رای‌ها و غیره واکشی شوند، چرا باید تعداد اعمال قابل توجهی جهت مراجعه به جداول مختلف مرتبط صورت گیرد؟ چرا تمام این اطلاعات را یکجا نداشته باشیم تا بتوان همگی را در طی یک واکشی به دست آورد و به این ترتیب دیگر نیازی نباشد انواع و اقسام JOIN‌ها را به چند ده جدول موجود نوشت؟
اینجا است که مفهومی به نام Unit of change مطرح می‌شود. در هر واحد تغییر، کلیه اطلاعات مورد نیاز برای رندر یک شیء قرار می‌گیرند. برای مثال اگر قرار است با شیء محصول کار کنیم، تمام اطلاعات مورد نیاز آن‌‌را اعم از گروه‌ها، نوع‌ها، رنگ‌ها و غیره را در طی یک سند بانک اطلاعاتی NoSQL سندگرا، ذخیره می‌کنیم.


محدود‌ه‌های تراکنشی یا Transactional boundaries

محدوده‌های تراکنشی در Domain driven design به Aggregate root نیز معروف است. هر محدود تراکنشی حاوی یک Unit of change قرار گرفته داخل یک سند است. ابتدا بررسی می‌کنیم که در یک Read به چه نوع اطلاعاتی نیاز داریم و سپس کل اطلاعات مورد نیاز را بدون نوشتن JOIN ایی از جداول دیگر، داخل یک سند قرار می‌دهیم.
هر محدوده تراکنشی می‌تواند به محدوده تراکنشی دیگری نیز ارجاع داده باشد. برای مثال در RavenDB شماره‌های اسناد، یک سری رشته هستند؛ برخلاف بانک‌های اطلاعاتی رابطه‌ای که بیشتر از اعداد برای مشخص سازی Id استفاده می‌کنند. در این حالت برای ارجاع به یک کاربر فقط کافی است برای مثال مقدار خاصیت کاربر یک سند به "users/1" تنظیم شود. "users/1" نیز یک Id تعریف شده در RavenDB است.
مزیت این روش، سرعت واکشی بسیار بالای دریافت اطلاعات آن است؛ دیگر در اینجا نیازی به JOINهای سنگین به جداول دیگر برای تامین اطلاعات مورد نیاز نیست و همچنین در ساختار‌های پیچیده‌تری مانند ساختارهای تو در تو، دیگر نیازی به تهیه کوئری‌های بازگشتی و استفاده از روش‌های پیچیده مرتبط با آن‌ها نیز وجود ندارد و کلیه اطلاعات مورد نظر، به شکل یک شیء JSON داخل یک سند حاضر و آماده برای واکشی در طی یک Read هستند.
به این ترتیب می‌توان به سیستم‌های مقیاس پذیری رسید. سیستم‌هایی که با بالا رفتن حجم اطلاعات در حین واکشی‌های داده‌های مورد نیاز، کند نبوده و بسیار سریع پاسخ می‌دهند.


Denormalization داده‌ها

اینجا است که احتمالا ذهن رابطه‌ای تربیت شده‌ی شما شروع به واکنش می‌کند! برای مثال اگر نام یک محصول تغییر کرد، چطور؟ اگر آدرس یک مشتری نیاز به ویرایش داشت، چطور؟ چگونه یکپارچگی اطلاعاتی که اکنون به ازای هر سند پراکنده شده‌است، مدیریت می‌شود؟
زمانیکه به این نوع سؤالات رسیده‌ایم، یعنی Denormalization رخ داده است. در اینجا سندهایی را داریم که کلیه اطلاعات مورد نیاز خود را یکجا دارند. به این مساله از منظر نگاه به داده‌ها در طی زمان نیز می‌توان پرداخت. به این معنا که صحیح است که آدرس مشتری خاصی امروز تغییر کرده است، اما زمانیکه سندی برای او در سال قبل صادر شده است، واقعا آدرس آن مشتری که سفارشی برایش ارسال شده، دقیقا همان چیزی بوده است که در سند مرتبط، ثبت شده و موجود می‌باشد. بنابراین سند قبلی با اطلاعات قبلی مشتری در سیستم موجود خواهد بود و اگر سند جدیدی صادر شد، این سند بدیهی است که از اطلاعات امروز مشتری استفاده می‌کند.


ملاحظات اندازه‌های داده‌ها

زمانیکه سند‌ها بسیار بزرگ می‌شوند چه رخ خواهد داد؟ از لحاظ اندازه داده‌ها سه نوع سند را می‌توان متصور بود:
الف) سندهای محدود، مانند اغلب اطلاعاتی که تعداد فیلدهای مشخصی دارند با تعداد اشیاء مشخصی.
ب) سندهای نامحدود اما با محدودیت طبیعی. برای مثال اطلاعات فرزندان یک شخص را درنظر بگیرید. هرچند این اطلاعات نامحدود هستند، اما به صورت طبیعی می‌توان فرض کرد که سقف بالایی آن عموما به 20 نمی‌رسد!
ج) سندهای نامحدود، مانند سندهایی که آرایه‌ای از اطلاعات را ذخیره می‌کنند. برای مثال در یک سایت فروشگاه، اطلاعات فروش یک گروه از اجناس خاص را درنظر بگیرید که عموما نامحدود است. اینجا است که باید به اندازه اسناد نیز دقت داشت. برای مدیریت این مساله حداقل از دو روش استفاده می‌شود:
- محدود کردن تعداد اشیاء. برای مثال در هر سند حداکثر 100 اطلاعات فروش یک محصول بیشتر ثبت نشود. زمانیکه به این حد رسیدیم، یک سند جدید ایجاد شده و Id سند قبلی مثلا "products/1" در سند دوم ذکر خواهد شد.
- محدود کردن تعداد اطلاعات ذخیره شده بر اساس زمان
RavenDB برای مدیریت این مساله، مفهوم Includes را معرفی کرده است. در اینجا با استفاده از متد الحاقی Include، کار زنجیر کردن سندهای مرتبط صورت خواهد گرفت.



یک مثال عملی: مدل سازی داده‌های یک بلاگ در RavenDB

پس از این بحث مقدماتی که جهت معرفی ذهنیت مدل سازی داده‌ها در دنیای غیر رابطه‌ای NoSQL ضروری بود، در ادامه قصد داریم مدل‌های داده‌های یک بلاگ را سازگار با ساختار بانک اطلاعاتی NoSQL سندگرای RavenDB طراحی کنیم.
در یک بلاگ، تعدادی مطلب، نظر، برچسب (گروه‌های مطالب) و امثال آن وجود دارند. اگر بخواهیم این اطلاعات را به صورت رابطه‌ای مدل کنیم، به ازای هر کدام از این موجودیت‌ها یک جدول نیاز خواهد بود و برای رندر صفحه اصلی بلاگ، چندین و چند کوئری برای نمایش اطلاعات مطالب، نویسنده(ها)، برچسب‌ها و غیره باید به بانک اطلاعاتی ارسال گردد، که تعدادی از آن‌ها مستقیما بر روی یک جدول اجرا می‌شوند و تعدادی دیگر نیاز به JOIN دارند.
مشکلاتی که روش رابطه‌ای دارد:
- تعداد اعمالی که باید برای نمایش صفحه اول سایت صورت گیرد، بسیار زیاد است و این مساله با تعداد بالای کاربران از دید مقیاس پذیری سیستم مشکل ساز است.
- داده‌های مرتبط در جداول مختلفی پراکنده‌اند.
- این سیستم برای Write بهینه سازی شده است و نه برای Read. (همان بحث گران بودن سخت دیسک‌ها در دهه‌های قبل که در ابتدای بحث به آن اشاره شد)

مدل سازی سازگار با دنیای NoSQL یک بلاگ

در اینجا چند کلاس مقدماتی را مشاهده می‌کنید که تعریف آن‌ها به همین نحو صحیح است و نیاز به جزئیات و یا روابط بیشتری ندارند.
namespace RavenDBSample01.BlogModels
{
    public class BlogConfig
    {
        public string Id { set; get; }
        public string Title { set; get; }
        public string Description { set; get; }
        // ... more items here
    }

    public class User
    {
        public string Id { set; get; }
        public string FullName { set; get; }
        public string Email { set; get; }
        // ... more items here
    }
}
اما کلاس مطالب بلاگ را به چه صورتی طراحی کنیم؟ هر مطلب، دارای تعدادی نظر خواهد بود. اینجا است که بحث unit of change مطرح می‌شود و درج اطلاعاتی که در طی یک read نیاز است از بانک اطلاعاتی جهت رندر UI واکشی شوند. به این ترتیب به این نتیجه می‌رسیم که بهتر است کلیه کامنت‌های یک مطلب را داخل همان شیء مطلب مرتبط قرار دهیم. از این جهت که یک نظر، خارج از یک مطلب بلاگ دارای مفهوم نیست.
اما این طراحی نیز یک مشکل دارد. درست است که ساختار یک صفحه مطلب، از مطالب وبلاگ به همین نحوی است که توضیح داده شد؛ اما در صفحه اول سایت، هیچگاه کامنت‌های مطالب درج نمی‌شوند. بنابراین نیازی نیست تا تمام کامنت‌ها را داخل یک مطلب ذخیره کرد. به این ترتیب برای نمایش صفحه اول سایت، حجم کمتری از اطلاعات واکشی خواهند شد.
    public class Post
    {
        public string Id { set; get; }
        public string Title { set; get; }
        public string Body { set; get; }

        public ICollection<string> Tags { set; get; }

        public string AuthorId { set; get; }

        public string PostCommentsId { set; get; }
        public int CommentsCount { set; get; }
    }

    public class Comment
    {
        public string Id { set; get; }
        public string Body { set; get; }
        public string AuthorName { set; get; }
        public DateTime CreatedAt { set; get; }
    }

    public class PostComments
    {
        public List<Comment> Comments { set; get; }
        public string LastCommentId { set; get; }
    }
در اینجا ساختار Post و Commentهای بلاگ را مشاهده می‌کنید. جایی که ذخیره سازی اصلی کامنت‌ها صورت می‌گیرد در شیء PostComments است. یعنی PostCommentsId شیء Post به یک وهله از شیء PostComments که حاوی کلیه کامنت‌های آن مطلب است، اشاره می‌کند.
به این ترتیب برای نمایش صفحه اول سایت، فقط یک کوئری صادر می‌شود. برای نمایش یک مطلب و کلیه کامنت‌های متناظر با آن دو کوئری صادر خواهند شد.

بنابراین همانطور که مشاهده می‌کنید، در دنیای NoSQL، طراحی مدل‌های داده‌ای بر اساس «سناریوهای Read» صورت می‌گیرد و نه صرفا طراحی یک مدل رابطه‌ای بهینه سازی شده برای حالت Write.

سورس کامل ASP.NET MVC این بلاگ‌را که «راکن بلاگ» نام دارد، از GitHub نویسندگان اصلی RavenDB می‌توانید دریافت کنید.
مطالب
معرفی کتابخانه PdfReport
مدتی هست که در حال تهیه کتابخانه‌ی گزارش سازی مبتنی بر iTextSharp هستم و عمده‌ی استفاده از آن برای من تاکنون، تهیه گزارشات باکیفیت PDF فارسی تحت وب بوده؛ هر چند این کتابخانه وابستگی خاصی به فناوری مورد استفاده ندارد و با WinForms، WPF، مشتقات ASP.NET ، سرویس‌های WCF و کلا هرجایی که دات نت فریم ورک کامل در دسترس باشد، قابل استفاده است. همچنین به منبع داده خاصی گره نخورده است و حتی می‌توانید از یک anonymously typed list عدم متصل به بانک اطلاعاتی خاصی نیز به عنوان منبع داده آن استفاده کنید.
کتابخانه PdfReport به عمد جهت دات نت 3.5 تهیه شده است تا بازه وسیعی از سیستم عامل‌ها را پوشش دهد.
این کتابخانه علاوه بر تبدیل اطلاعات شما به گزارشات مبتنی بر PDF، امکان تهیه خروجی خودکار اکسل (2007 به بعد) را نیز دارد. فایل خروجی آن، به صورت پیوست درون فایل PDF تهیه شده قرار می‌گیرد و جزئی از آن می‌شود.
بدیهی است اینبار با کتابخانه گزارش سازی روبرو هستید که با راست به چپ مشکلی ندارد!
کتابخانه PdfReport بر پایه کتابخانه‌های معروف سورس باز iTextSharp و EPPlus تهیه شده است. حداقل مزیت استفاده از آن، صرفه جویی در وقت شما جهت آموختن ریزه کاری‌های مرتبط با هر کدام از کتابخانه‌های یاده شده است. برای نمونه جهت فراگیری کار با iTextSharp نیاز است یک کتاب 600 صفحه‌ای به نام iText in action را مطالعه و تمرین کنید. این مورد منهای مسایل و نکات متعدد مرتبط با زبان فارسی است که در این کتاب به آن‌ها اشاره‌ای نشده است.
برای تهیه آن، مشکلات متداولی که کاربران مدام در انجمن‌های برنامه نویسی مطرح می‌کنند و ابزارهای موجود عاجز از ارائه راه حلی ساده برای حل آن‌ها هستند، مد نظر بوده و امید است نگارش یک این کتابخانه بتواند بسیاری از این دردها را کاهش دهد.
کار با این کتابخانه صرفا با کدنویسی میسر است (code first) و همین مساله انعطاف پذیری قابل توجهی را ایجاد کرده که در طی روزهای آینده با جزئیات بیشتر آن آشنا خواهید شد.


اما چرا PDF؟

استفاده از قالب PDF برای تهیه گزارشات، این مزایا را به همراه دارد:
- دقیقا همان چیزی که مشاهده می‌شود، در هر مکانی قابل چاپ است. با همان کیفیت، همان اندازه صفحه، همان فونت و غیره. به این ترتیب به صفحه بندی بسیار مناسب و بهینه‌ای می‌توان رسید و مشکلات گزارشات HTML ایی وب را ندارد.
- امکان استفاد از فونت‌های شکیل‌تر در آن بدون مشکل و بدون نیاز به نصب بر روی کامپیوتری میسر است؛ چون فونت را می‌توان در فایل PDF نیز قرار داد.
- این فایل در تمام سیستم عامل‌ها پشتیبانی می‌شود. خصوصا اینکه فایل نهایی در تمام کامپیوترها و در انواع و اقسام سیستم عامل‌ها به یک شکل و اندازه نمایش داده خواهد شد. برای مثال اینطور نیست که در ویندوز XP ،‌اعداد آن فارسی نمایش داده شوند و در ویندوز 7 با تنظیمات محلی خاصی، دیگر اینطور نباشد. حتی تحت لینوکس هم اعداد آن فارسی نمایش داده خواهد شد چون فونت مخصوص بکار رفته، در خود فایل PDF قابل قرار دادن است.
- برنامه معروف و رایگان Adobe reader برای خواندن و مشاهده آن کفایت می‌کند و البته کلاینت یکبار باید این برنامه را نصب کند. همچنین از این نوع برنامه‌های رایگان برای مشاهده فایل‌های PDF زیاد است.
- تمام صفحات گزارش را در یک فایل می‌توان داشت و به یکباره تمام آن نیز به سادگی قابل چاپ است. این مشکلی است که با گزارشات تحت وب وجود دارد که نمی‌شود مثلا یک گزارش 100 صفحه‌ای را به یکباره به چاپگر ارسال کرد. به همین جهت عموما کاربران درخواست می‌دهند تا کل گزارش را در یک صفحه HTML نمایش دهید تا ما راحت آن‌را چاپ کنیم یا راحت از آن خروجی بگیریم. اما زمانیکه فایل PDF تهیه می‌شود این مشکلات وجود نخواهد داشت و جهت Print بسیار بهینه سازی شده است. تا حدی که الان فرمت برگزیده تهیه کتاب‌های الکترونیکی نیز PDF است. مثلا سایت معروف آمازون امکان فروش نسخه PDF کتاب‌ها را هم پیش بینی کرده است.
-امکان صفحه بندی دقیق به همراه مشخص سازی landscape یا portrait بودن صفحه نهایی میسر است. چیزی که در گزارشات صفحات وب آنچنان معنایی ندارد.
- امکان رمزنگاری اطلاعات در آن پیش بینی شده است. همچنین می‌توان به فایل‌های PDF امضای دیجیتال نیز اضافه کرد. به این ترتیب هرگونه تغییری در محتوای فایل توسط برنامه‌های PDF خوان معتبر گزارش داده شده و می‌توان از صحت اطلاعات ارائه شده توسط آن اطمینان حاصل کرد.
- از فشرده سازی مطالب، فایل‌ها و تصاویر قرار داده شده در آن پشتیبانی می‌کند.
- از گرافیک برداری پشتیبانی می‌کند.


مجوز استفاده از این کتابخانه:
کار من مبتنی بر LGPL است. به این معنا که به صورت باینری (فایل dll) در هر نوع پروژه‌ای قابل استفاده است.
اما ... PdfReport از دو کتابخانه دیگر نیز استفاده می‌کند:
- کتابخانه iTextSharp که دارای مجوز AGPL است. این مجوز رایگان نیست.
- کتابخانه EPPlus برای تولید فایل‌های اکسل با کیفیت. مجوز استفاده از این کتابخانه LGPL است و تا زمانیکه به صورت باینری از آن استفاده می‌کنید، محدودیتی را برای شما ایجاد نخواهد کرد.


کتابخانه PdfReport به صورت سورس باز در CodePlex قرار گرفته ؛ اما جهت پرسیدن سؤالات، پیشنهادات، ارائه بهبود و غیره می‌توانید (و بهتر است) از قسمت مدیریت پروژه مرتبط در سایت جاری نیز استفاده کنید.


نحوه تهیه اولین گزارش، با کتابخانه PdfReport

الف) یک پروژه Class library جدید را شروع کنید. از این جهت که گزارشات PdfReport در انواع و اقسام پروژه‌های VS.NET قابل استفاده است، می‌توان از این پروژه Class library به عنوان کلاس‌های پایه قابل استفاده در انواع و اقسام پروژه‌های مختلف، بدون نیاز به تغییری در کدهای آن استفاده کرد.

ب) آخرین نگارش فایل‌های مرتبط با PdfReport را از اینجا دریافت کنید و سپس ارجاعاتی را به اسمبلی‌های موجود در بسته آن به پروژه خود اضافه نمائید (ارجاعاتی به PdfReport، iTextSharp و EPPlus). فایل XML راهنمای کتابخانه نیز به همراه بسته آن می‌باشد که در حین استفاده از متدها و خواص PdfReport کمک بزرگی خواهد بود.

ج) کلاس‌های زیر را به آن اضافه کنید:
using System.Web;
using System.Windows.Forms;

namespace PdfReportSamples
{
    public static class AppPath
    {
        public static string ApplicationPath
        {
            get
            {
                if (isInWeb)
                    return HttpRuntime.AppDomainAppPath;

                return Application.StartupPath;
            }
        }

        private static bool isInWeb
        {
            get
            {
                return HttpContext.Current != null;
            }
        }
    }
}
از این کلاس برای مشخص سازی محل ذخیره سازی فایل‌های نهایی PDF تولیدی استفاده خواهیم کرد.
همانطور که مشاهده می‌کنید ارجاعاتی را به System.Windows.Forms.dll و System.Web.dll نیاز دارد.

در ادامه کلاس User را جهت ساخت یک منبع داده درون حافظه‌ای تعریف خواهیم کرد:
using System;

namespace PdfReportSamples.IList
{
    public class User
    {
        public int Id { set; get; }
        public string Name { set; get; }
        public string LastName { set; get; }
        public long Balance { set; get; }
        public DateTime RegisterDate { set; get; }
    }
}
اکنون کلاس اصلی گزارش ما به صورت زیر خواهد بود:
using System;
using System.Collections.Generic;
using PdfRpt.Core.Contracts;
using PdfRpt.FluentInterface;

namespace PdfReportSamples.IList
{
    public class IListPdfReport
    {
        public IPdfReportData CreatePdfReport()
        {
            return new PdfReport().DocumentPreferences(doc =>
            {
                doc.RunDirection(PdfRunDirection.RightToLeft);
                doc.Orientation(PageOrientation.Portrait);
                doc.PageSize(PdfPageSize.A4);
                doc.DocumentMetadata(new DocumentMetadata { Author = "Vahid", Application = "PdfRpt", Keywords = "Test", Subject = "Test Rpt", Title = "Test" });
            })
            .DefaultFonts(fonts =>
            {
                fonts.Path(Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\tahoma.ttf",
                                  Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\verdana.ttf");
            })
            .PagesFooter(footer =>
            {
                footer.DefaultFooter(DateTime.Now.ToString("MM/dd/yyyy"));
            })
            .PagesHeader(header =>
            {
                header.DefaultHeader(defaultHeader =>
                {
                    defaultHeader.ImagePath(AppPath.ApplicationPath + "\\Images\\01.png");
                    defaultHeader.Message("گزارش جدید ما");
                });
            })
            .MainTableTemplate(template =>
            {
                template.BasicTemplate(BasicTemplate.ClassicTemplate);
            })
            .MainTablePreferences(table =>
            {
                table.ColumnsWidthsType(TableColumnWidthType.Relative);
                table.NumberOfDataRowsPerPage(5);
            })
            .MainTableDataSource(dataSource =>
            {
                var listOfRows = new List<User>();
                for (int i = 0; i < 200; i++)
                {
                    listOfRows.Add(new User { Id = i, LastName = "نام خانوادگی " + i, Name = "نام " + i, Balance = i + 1000 });
                }
                dataSource.StronglyTypedList<User>(listOfRows);
            })
            .MainTableSummarySettings(summarySettings =>
            {
                summarySettings.OverallSummarySettings("جمع کل");
                summarySettings.PerviousPageSummarySettings("نقل از صفحه قبل");
                summarySettings.PageSummarySettings("جمع صفحه");
            })
            .MainTableColumns(columns =>
            {
                columns.AddColumn(column =>
                {
                    column.PropertyName("rowNo");
                    column.IsRowNumber(true);
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(0);
                    column.Width(1);
                    column.HeaderCell("#");
                });

                columns.AddColumn(column =>
                {
                    column.PropertyName<User>(x => x.Id);
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(1);
                    column.Width(2);
                    column.HeaderCell("شماره");
                });

                columns.AddColumn(column =>
                {
                    column.PropertyName<User>(x => x.Name);
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(2);
                    column.Width(3);
                    column.HeaderCell("نام");
                });

                columns.AddColumn(column =>
                {
                    column.PropertyName<User>(x => x.LastName);
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(3);
                    column.Width(3);
                    column.HeaderCell("نام خانوادگی");
                });

                columns.AddColumn(column =>
                {
                    column.PropertyName<User>(x => x.Balance);
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(4);
                    column.Width(2);
                    column.HeaderCell("موجودی");
                    column.ColumnItemsTemplate(template =>
                    {
                        template.TextBlock();
                        template.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj));
                    });
                    column.AggregateFunction(aggregateFunction =>
                    {
                        aggregateFunction.NumericAggregateFunction(AggregateFunction.Sum);
                        aggregateFunction.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj));
                    });
                });

            })
            .MainTableEvents(events =>
            {
                events.DataSourceIsEmpty(message: "رکوردی یافت نشد.");
            })
            .Export(export =>
            {
                export.ToExcel();
                export.ToCsv();
                export.ToXml();
            })
            .Generate(data => data.AsPdfFile(AppPath.ApplicationPath + "\\Pdf\\RptIListSample.pdf"));
        }
    }
}
و برای استفاده از آن:
var rpt = new IListPdfReport().CreatePdfReport();
// rpt.FileName


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

توضیحات بیشتر:

- در قسمت  DocumentPreferences، جهت راست به چپ (PdfRunDirection)، اندازه صفحه (PdfPageSize)، جهت صفحه (PageOrientation) و امثال آن تنظیم می‌شوند.
- سپس نیاز است قلم‌های مورد استفاده در گزارش مشخص شوند. در متد DefaultFonts باید دو  قلم را معرفی کنید. قلم اول، قلم پیش فرض خواهد بود و قلم دوم برای رفع نواقص قلم اول مورد استفاده قرار می‌گیرد. برای مثال اگر قلم اول فاقد حروف انگلیسی است، به صورت خودکار به قلم دوم رجوع خواهد شد.
- در ادامه در متد PagesFooter، تاریخ درج شده در پایین تمام صفحات مشخص می‌شود. در مورد ساخت Footer سفارشی در قسمت‌های بعدی بحث خواهد شد.
- در متد PagesHeader، متن و تصویر قرار گرفته در Header تمام صفحات گزارش قابل تنظیم است. این مورد نیز قابل سفارشی سازی است که در قسمت‌های بعد به آن خواهیم پرداخت.
- توسط MainTableTemplate، قالب ظاهری ردیف‌های گزارش مشخص می‌شود. یک سری قالب پیش فرض در کتابخانه PdfReport موجود است که توسط متد BasicTemplate آن قابل دسترسی است. در مورد نحوه تعریف قالب‌‌های سفارشی به مرور در قسمت‌های بعد، بحث خواهد شد.
- در قسمت MainTablePreferences تنظیمات جدول اصلی گزارش تعیین می‌شود. برای مثال چه تعداد ردیف در صفحه نمایش داده شود. اگر این مورد را تنظیم نکنید، به صورت خودکار محاسبه خواهد شد. نحوه تعیین عرض ستون‌های گزارش به کمک متد ColumnsWidthsType مشخص می‌شود که در اینجا حالت نسبی درنظر گرفته شده است.
- منبع داده مورد استفاده توسط متد MainTableDataSource مشخص می‌شود که در اینجا یک لیست جنریک تعیین شده و سپس توسط متد StronglyTypedList در اختیار گزارش ساز جاری قرار می‌گیرد. تعدادی منبع داده پیش فرض در PdfReport وجود دارند که هر کدام را در قسمت‌های بعدی بررسی خواهیم کرد. همچنین امکان تعریف منابع داده سفارشی نیز وجود دارد.
- با کمک متد MainTableSummarySettings، برچسب‌های جمع‌های پایین صفحات مشخص می‌شود.
- در قسمت MainTableColumns، ستون‌هایی را که علاقمندیم در گزارش ظاهر شوند، قید می‌کنیم. هر ستون باید با یک فیلد یا خاصیت منبع داده متناظر باشد. همچنین همانطور که مشاهده می‌کنید امکان تعیین Visibility، عرض و غیره آن نیز مهیا است (قابلیت ساخت گزارشاتی که به انتخاب کاربر، ستون‌های آن ظاهر یا مخفی شوند). در اینجا توسط callbackهایی که در متد ColumnItemsTemplate قابل دسترسی هستند، می‌توان اطلاعات را پیش از نمایش فرمت کرد. برای مثال سه رقم جدا کننده به اعداد اضافه کرد (برای نمونه در خاصیت موجودی فوق) و یا توسط متد AggregateFunction، می‌توان متد تجمعی مناسبی را جهت ستون جاری مشخص کرد.
- توسط متد MainTableEvents به بسیاری از رخدادهای داخلی PdfReport دسترسی خواهیم یافت. برای مثال اگر در اینجا رکوردی موجود نباشد، رخداد DataSourceIsEmpty صادر خواهد شد.
- به کمک متد Export، خروجی‌های دلخواه مورد نظر را می‌توان مشخص کرد. تعدادی خروجی، مانند اکسل، XML و CSV در این کتابخانه موجود است. امکان سفارشی سازی آن‌ها نیز پیش بینی شده است.
- و نهایتا توسط متد Generate مشخص خواهیم کرد که فایل گزارش کجا ذخیره شود.

 لطفا برای طرح مشکلات و سؤالات خود در رابطه با کتابخانه PdfReport از این قسمت سایت استفاده کنید.
راهنماهای پروژه‌ها
لیست مثال‌های همراه با سورس کد PdfReport
برای دریافت یکجای تمامی مثال‌های ذیل به این آدرس مراجعه کرده و بر روی لینک دانلود کلیک نمائید. همچنین توضیحات بیشتر و تکمیلی این مثال‌ها، در برچسب PdfReport سایت، قابل پیگیری و مطالعه هستند.

AccountingBalanceColumn/
چگونه باید از مقدار مانده ردیف قبلی در محاسبات ردیف جاری استفاده کرد (چیزی شبیه به گزارشات دفتر کل حسابداری).

AcroFormTemplate/
چگونه می‌توان از قالب‌های سفارشی تهیه شده توسط Open office در PdfReport استفاده کرد. اگر در یک سلول قرار است قالب پیچیده‌ای را نمایش دهید، یکی از روش‌های انجام کار استفاده از قالب‌های AcroForm است.

AdHocColumns/
چگونه تولید ستون‌های گزارشات را پویا کنیم (بدون نیاز به تعریف جزئیات آن‌ها). برای مثال اگر هربار کوئری متفاوتی را ارسال می‌کنید یا از منابع داده مختلفی با تعداد ستون‌های متغیر در گزارش نهایی استفاده می‌شود، می‌توانید با حذف قسمت تعاریف ستون‌ها، این نوع گزارشات پویا را تهیه نمائید.

AnnotationField/
نمایشی از قالب سلول سفارشی AnnotationField. Annotationها اشیایی خاص در فایل‌های PDF هستند که امکان نوشتن توضیحات طولانی را فراهم می‌کنند و نهایتا به شکل یک آیکون در گزارش ظاهر خواهند شد.

Barcodes/
مثالی در مورد نحوه تولید انواع بارکدهای مختلف مانند barcode 128 و barcode 39

CalculatedFields/
چگونه بر اساس فیلدهای موجود یک گزارش، ستون محاسبه شده جدیدی را تولید کنیم. همچنین مواردی مانند فرمت کردن عدد نمایش داده شده و اضافه کردن جمع به یک ستون نیز در این گزارش لحاظ شده است.

CharacterMap/
گزارشی شبیه به برنامه معروف character map ویندوز. در این گزارش نوع جدول به TableType.HorizontalStackPanel تنظیم شده است. به این ترتیب رکوردهای تولید شده به صورت افقی و پی در پی نمایش داده خواهند شد.

ChartImage/
نحوه قرار دادن نمودارهای MS Chart را در گزارشات، در این مثال مشاهده خواهید کرد.

CsvToPdf/
چگونه رکوردهای یک فایل CSV را تبدیل به فایل PDF کنیم؟ این مثال در حقیقت نحوه استفاده مستقیم از نتایج کوئری‌های LINQ را بیان می‌کند.

CustomCellTemplate/
چگونه یک قالب سلول سفارشی را تعریف کنیم. یک سری قالب پیش فرض مانند تصویر، متن و غیره در PdfReport به ازای هر سلول قابل تعریف است. اگر این موارد نیاز کاری شما را برآورده نمی‌کنند، می‌توانید آن‌ها را سفارشی سازی کنید.

CustomHeaderFooter/
چگونه هدر و فوتر گزارشات را سفارشی سازی کنیم؟

CustomPriceNumber/
چگونه یک قالب سلول سفارشی را جهت نمایش ویژه عدد مبلغ هر ردیف به شکل یک جدول پر شده از اعداد ایجاد کنیم.

DataAnnotations/
چگونه تعاریف خواص ستون‌ها را به کمک data annotations انجام داده و اینکار را ساده‌تر نمائیم. با استفاده از data annotations نیز می‌توان قسمت تعاریف ستون‌ها را کاملا حذف کرد.

DbImage/
چگونه تصاویر ذخیره شده در بانک اطلاعاتی را در گزارشات نمایش دهیم.

DigitalSignature/
چگونه امضای دیجیتال را به گزارشات PDF خود اضافه نمائیم.

DuplicateColumns/
چگونه از ستون‌هایی هم نام، استفاده کنیم. برای مثال اگر از دو جدول کوئری می‌گیرید و دو فیلد به نام‌های name اما با معانی و مقادیری متفاوت تعریف شده‌اند، چگونه باید ایندکس آن‌ها را جهت تمایز بهتر معرفی کرد.

DynamicCompile/
چگونه سورس یک گزارش PdfReport را به صورت پویا از یک فایل متنی ساده خوانده و کامپایل کنیم.

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

EmailInMemoryPdf/
چگونه یک فایل Pdf تولید شده را به صورت خودکار به مقصدی خاص ایمیل کنیم.

Events/
چگونه می‌توان دقیقا پیش و پس از یک گزارش، تعاریف و عناصر دلخواه خود را اضافه کنیم؟

ExcelToPdf/
چگونه یک فایل اکسل را تبدیل به گزارش PdfReport کنیم؟

ExpensesCrosstab/
مثالی دیگر از نحوه تولید گزارشات Crosstab.

ExtraHeadingCells/
چگونه گزارشاتی را تولید کنیم که هدر آن‌ها بیش از یک ردیف است.

Grouping/
نحوه گروه بندی اطلاعات را در این گزارش بررسی خواهیم کرد.

HexDump/
یک گزارش ویژه از منبع داده‌ای anonymously typed.

HtmlCellTemplate/
چگونه می‌توان از Html جهت ساده سازی تعریف سلول‌های پیچیده که بیش از یک مقدار را نمایش می‌دهند استفاده کرد.

HtmlHeader/
چگونه می‌توان از Html برای ساده سازی هدر گزارش استفاده کرد.

HtmlHeaderRtl/
نسخه راست به چپ و فارسی مثال قبل.

IList/
چگونه می‌توان از لیست‌های جنریک گزارش تهیه کرد.

ImageFilePath/
چگونه می‌توان از تصاویر ذخیره شده در فایل سیستم، گزارش گرفت.

InjectCustomRows/
چگونه می‌توان ردیفی سفارشی را در بین ردیف‌های دریافت شده از بانک اطلاعاتی قرار داد.

InlineProviders/
چگونه می‌توان تعاریف سفارشی سلول‌ها را در همان محل تعریف گزارش به نحوی ساده‌تر تعریف کرد.

InMemory/
نحوه تولید فایل‌های PDF درون حافظه‌ای، مناسب جهت برنامه‌های وب ASP.NET (بدون نیاز به ذخیره فایل بر روی سرور)

MailingLabel/
چگونه گزارش‌های معروف برچسب‌های چاپی را توسط PdfReport تولید کنیم.

MasterDetails/
چگونه از روابط one-to-many بین دو جدول گزارش گیری کنیم؟

MergePdfFiles/
چگونه از چند منبع داده مختلف استفاده کرده و نهایتا گزارشات حاصل را یکی و تبدیل به یک فایل PDF کنیم. برای نمونه استفاده از سه جدول مختلف با هدرها و سرستون‌های متفاوت و سپس تولید یک گزارش یکپارچه از این سه، در قالب یک فایل نهایی. به علاوه در این مثال نحوه بازنویسی فوتر موجود نیز نمایش داده شده است (توسط WriterCustomizer آن).

MonthCalendar/
چگونه در گزارشات، تقویم میلادی را نمایش دهیم.

PdfA/
چگونه خروجی PDF حاصل را بر اساس استاندارد PdfA که مخصوص آرشیو و نگهداری است، تولید کنیم.

PersianFontsListToPdf/
چگونه از لیست قلم‌های نصب شده در سیستم گزارش Pdf تهیه کنیم.

PersianMonthCalendar/
بررسی نحوه نمایش تقویم شمسی، در گزارشات.

PersianRtl/
بررسی امکانات فارسی توکار کتابخانه PdfReport؛ مانند تهیه گزارشات راست به چپ، تاریخ شمسی، عدد به حروف و غیره.

ProgressReport/
چگونه درصد پیشرفت یک عملیات را در سلول‌ها نمایش دهیم. همچنین نحوه ایجاد گزارشات چند ستونی، برای صرفه جویی در میزان کاغذ مصرفی چاپ گزارشات را نیز در این گزارش مشاهده خواهید نمود.

QuestionsAcroForm/
مثالی در مورد نحوه استفاده از قالب‌های PDF تولید شده توسط Open office برای تولید برگه سؤالات امتحانی

QuestionsForm/
مثالی در مورد نحوه طراحی برگه سؤالات امتحانی توسط سفارشی سازی سلول‌ها در PdfReport

SQLiteDataReader/
چگونه از یک بانک اطلاعاتی SQLite گزارش تهیه کنیم.

StackedProperties/
چگونه در یک گزارش، در یک سلول بیش از یک فیلد را نمایش دهیم.

Tax/
چگونه یک گزارش فاکتور فروش طراحی کنیم.

WorkedHours/
چگونه گزارش حضور و غیاب پرسنل را تهیه کنیم.

WrapGroupsInColumns/
چگونه گزارشات چندستونی را تولید کنیم.

XmlToPdf/
چگونه داده‌های یک فایل XML را تبدیل به گزارش کنیم.

ZapfDingbatsSymbols/
چگونه از قلم مخصوص Symbols شرکت Adobe برای نمایش اشکال مختلف می‌توان استفاده کرد. 
مطالب
تفاوت سیستم‌های یکپارچه با میکروسرویس‌ها (Monolithic vs Microservices architecture)

 معماری میکرو سرویس یا یکپارچه؟

برای درک میکروسرویس‌ها، باید بدانیم کاربرد سیستم‌های یکپارچه چیست و چه چیزی باعث شد در زمان‌های اخیر از برنامه‌های یکپارچه به میکروسرویس‌ها حرکت کنیم.


 سیستم‌های یکپارچه ( Monolithic applications  )

اگر تمام عملکردهای یک پروژه در یک بخش واحد وجود داشته باشند، آن برنامه به عنوان یک برنامه‌ی یکپارچه شناخته می‌شود. ما برنامه‌ی خود را در لایه‌های مختلفی مانند Presentation ، Service ، UI  طراحی می‌کنیم و سپس آن بخش از کدهای نوشته شده را به عنوان یک فایل خروجی به کار می‌گیریم. این چیزی نیست جز یک برنامه‌ی یکپارچه، که در آن " mono " یک پایگاه کد منفرد حاوی تمام عملکردهای مورد نیاز را نشان می‌دهد.


چرا اصلا به سمت میکروسرویس‌ها برویم؟

خب برای جواب به این سوال بهتر است معایب سیستم‌های یکپارچه را مرور کنیم:

  1. مدیریت دشوار بخاطر گسترش برنامه در گذشت زمان
  2. برای تغییری کوچک، کل برنامه را دوباره باید منتشر ( publish ) کنیم
  3. با تغییر و آپدیت برنامه، زمان انتشار افزایش می‌یابد.
  4. درک دشوار برای توسعه دهنده‌های جدید هر پروژه
  5. برای تقسیم ترافیک روی قسمت‌های مختلف برنامه، باید نمونه‌های کل برنامه را در چندین سرور منتشر کنیم که بسیار ناکارآمد و باعث استفاده‌ی بیهوده از منابع میشود
  6. اگر از فناوری یا تکنولوژی‌های جدید استفاده کنیم، برای عملکردی خاص، چه از نظر هزینه و چه از نظر زمان، بر کل برنامه تاثیر گذار است
  7. و در نهایت وجود یک باگ در هر ماژول میتواند کل برنامه را مختل کند.

و اما مزایای سیستم‌های یکپارچه:

  1. توسعه‌ی آن نسبت به میکروسرویس‌ها ساده است.
  2. انتشار آن آسان‌تر است؛ زیرا فقط یک خروجی، مستقر شده‌است.
  3. در مقایسه با معماری میکروسرویس‌ها، توسعه‌ی آن نسبتا آسان‌تر و ساده‌تر است.
  4. مشکلات تأخیر و امنیت شبکه در مقایسه با معماری میکروسرویس‌ها نسبتاً کمتر است.
  5. توسعه دهندگان نیازی به یادگیری برنامه‌های مختلف ندارند؛ آنها می‌توانند تمرکز خود را بر روی یک برنامه حفظ کنند.


میکروسرویس ها

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

معماری میکروسرویس تأثیر بسزایی در رابطه‌ی بین برنامه و پایگاه داده دارد. بجای اشتراک گذاری یک پایگاه داده با سایر میکروسرویس‌ها، هر میکروسرویس، پایگاه داده خاص خود را دارد که اغلب منجر به تکثیر برخی از داده‌ها می‌شود، اما اگر می‌خواهید از این معماری بهره مند شوید، داشتن یک پایگاه داده در هر میکروسرویس، ضروری است؛ زیرا اتصال ضعیف را تضمین می‌کند. مزیت دیگر داشتن یک پایگاه داده‌ی مجزا برای هر میکروسرویس این است که هر میکروسرویس می‌تواند از نوع پایگاه داده‌ای که برای نیازهای خود مناسب‌تر است، استفاده کند. هر سرویس یک ماژول را ارائه می‌دهد، به طوری که خدمات مختلف را می‌توان به زبان‌های برنامه نویسی مختلف نوشت. الگوهای زیادی در معماری میکروسرویس دخیل هستند مانند discovery و registry service ، Caching ، ارتباط API ، امنیت و غیره.



اصول میکروسرویس‌ها:

تک مسئولیتی: یکی از اصولی است که به عنوان بخشی از الگوی طراحی SOLID تعریف شده است. بیان می‌کند که یک Unit ، یا یک کلاس، یک متد یا یک میکروسرویس باید تنها یک مسئولیت را داشته باشد. هر میکروسرویس باید یک مسئولیت داشته باشد و یک عملکرد واحد را ارائه دهد. شما همچنین می‌توانید بگویید تعداد میکروسرویس‌هایی که باید توسعه دهید، برابر با تعداد عملکردهای مورد نیاز شما است. پایگاه داده نیز غیرمتمرکز است و به طور کلی، هر میکروسرویس، پایگاه داده خاص خود را دارد.

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

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

مزایای میکروسرویس ها:

  1. مدیریت آن آسان است زیرا نسبتا کوچکتر است.
  2. اگر در یکی از میکروسرویس‌ها، بروزرسانی وجود داشته باشد، باید فقط آن میکروسرویس را مجدداً منتشر کنیم.
  3. میکروسرویس‌ها مستقل هستند و از این رو به طور مستقل منتشر می‌شوند. زمان راه اندازی و انتشار آنها نسبتاً کمتر است.
  4. برای یک توسعه‌دهنده جدید بسیار آسان است که وارد پروژه شود، زیرا او باید فقط یک میکروسرویس خاص را که عملکردی را که روی آن کار می‌کند، درک کند و نه کل سیستم را.
  5. اگر یک میکروسرویس خاص به دلیل استفاده بیش از حد کاربران از آن عملکرد، با بار زیادی مواجه است، ما باید فقط آن میکروسرویس را تنظیم کنیم. از این رو، معماری میکروسرویس از مقیاس بندی افقی پشتیبانی می‌کند.
  6. هر میکروسرویس بر اساس نیازهای تجاری می‌تواند از فناوری‌های مختلفی استفاده کند.
  7. اگر یک میکروسرویس خاص به دلیل برخی باگ‌ها از کار بیفتد، بر روی سایر میکروسرویس‌ها تأثیر نمی‌گذارد و کل سیستم دست نخورده باقی می‌ماند و به ارائه سایر عملکردها به کاربران ادامه می‌دهد.

معایب میکروسرویس ها:

  1. پیچیده است و پیچیدگی آن با افزایش تعداد ریز سرویس‌ها افزایش می‌یابد.
  2. نیاز به نیروهای متخصص
  3. استقرار مستقل میکروسرویس‌ها پیچیده‌است.
  4. میکروسرویس‌ها از نظر استفاده از شبکه پرهزینه هستند؛ زیرا نیاز به تعامل با یکدیگر دارند و همه این تماس‌های راه دور منجر به تأخیر شبکه می‌شود.
  5. امنیت کمتر به دلیل ارتباط بین سرویس‌ها
  6. اشکال زدایی دشوار است

مطالب
چرا باید از Git Hooks استفاده کنیم؟ معرفی Husky.Net
قبل از شروع این مقاله بهتر است ابتدا یک خاطره‌ی کوچک را تعریف کنم. مدتی پیش بود که برای سایت داکیومنتیشن یکی از پروژه‌های Open-Source سعی داشتم از vuepress که یک static site generator هست استفاده کنم. متاسفانه نسخه‌ی بتایی که استفاده میکردم یک فیچر مورد نیازم را نداشت و مجبور شدم خودم به‌آن این فیچر را اضافه کنم. سوروس را گرفتم، فیچر اضافه شد و ماجرا از اینجا شروع می‌شود ...

  • commit lint : اول اجازه نداد که کامیت را انجام دهم! با این توضیح که متن کامیت شما باید فرمت کامیت‌های این پروژه را رعایت کند. اگر به تصویر زیر دقت کنید، کامیت‌ها در این پروژه بسیار منظم هستند و هر کسی با هر متنی که دلش خواست کامیتی انجام نمی‌دهد. با یک بررسی کوچک متوجه شدم چطور باید فرمت این پروژه را رعایت کنم و مشکل رفع شد.



  • eslint : مرحله بعدی فایل‌هایی که تغییر داده بودم و یا ایجاد کرده بودم، با ابزار eslint به صورت خودکار بررسی شد. (eslint مشابه analyzer‌های دات نت است که برای بررسی مشکلات موجود در سورس کد استفاده می‌شود). در تصویر زیر یک مثال از خطای ایجاد شده‌ی توسط eslint را مشاهده می‌کنید. در این مرحله من با خطایی مواجه نشدم؛ ولی متوجه شدم کدی که نوشتم، مشکل خاصی ندارد. 



  • prettier  : مرحله سوم کد نوشته شده‌ی من توسط دستورات فرمتی که این پروژه رعایت می‌کند مانند style کد نویسی، استفاده از space یا tab، فاصله فرو رفتگی (indent) خطوط و غیره ... به صورت خودکار فرمت شد و کامیت من انجام شد. 

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

تا اینجا با یکسری مزایای پایه‌ی داشتن چنین سیستمی آشنا شدیم ولی چطور این اتفاق رخ می‌دهد!؟ پاسخ: با استفاده از Git Hooks . البته ما صرفا محدود به انجام همین کارها نیستیم. هر کاری که در ترمینال سیستم قابل انجام باشد، از طریق Git hooks هم قابل انجام است. مثلا یکی دیگر از استفاده‌های رایج از git hooks میتواند اجرای Unit-Test‌ها قبل از push باشد.
در پروژه‌ی vuepress، تعداد زیادی ابزار به صورت هماهنگ نصب شده و کار میکنند که در نهایت با استفاده از Hook‌های Git، اجرا میشوند (مثلا از هوک pre-commit برای اجرای یکسری فرمان قبل از هر کامیت). با سه مورد از این ابزارها، در ابتدای این مقاله آشنا شدیم. ولی کلید اصلی تمام این‌ها، دو ابزار  husky  و  lint-staged هستند. husky ساخت و استفاده از git hook‌ها را آسان میکند و lint-staged وظیفه دارد تا فایل‌های stage شده‌ی در گیت را لیست کند و به سایر ابزارها برای اجرا ارسال نماید. 

شاید بپرسید که تمام اینها از Nodejs و npm استفاده میکنند، چطور میتوانیم از این امکانات در دات نت استفاده کنیم!   

معرفی Husky.Net 
این کتابخانه ترکیبی از ابزارهای husky و lint-staged و چند ابزار دیگر برای برنامه نویسان دات نت است. با استفاده از این ابزار به راحتی میتوانید از هوک‌های Git برای اجرای دستورات خود یا سایر ابزارها استفاده کنید. husky.net یک task-runner داخلی دارد که تعریف و ایجاد تسک‌های قابل اجرا توسط هوک‌ها را آسان میکند.

نصب husky.net
# local installation (recommended)
cd <Your project root directory>
dotnet new tool-manifest
dotnet tool install Husky

# global installation
dotnet tool install --global Husky

نکته نهایی
من در این مقاله قصد داشتم فقط دلیل نیاز به چنین ابزاری را شرح دهم؛ توضیح اینکه husky.net چه امکاناتی دارد و چطور میتوان از آن استفاده کرد، خارج از حوصله‌ی این مقاله است. اگر شد در آینده مقاله‌ای برای آموزش این ابزار تهیه میکنم؛ فعلا برای کسب اطلاعات بیشتر میتوانید به صفحه‌ی گیت هاب این ابزار مراجعه کنید و دلیل دیگر اینکه فکر میکنم الان زمان مناسبی برای آموزش نیست؛ چون این پروژه بسیار جوان است و به تازگی نوشته شده و احتمال اینکه فیچر‌های آن به زودی تغییر کند هم وجود دارد. همینطور از علاقمندان به مشارکت در پروژه‌های open-source دعوت می‌کنم که به بهبود این ابزار و یا تهیه‌ی ابزارهایی که در حال حاضر مشابهی در دات نت ندارند، کمک کنند. به طور مثال برای فرمت کد و آنالایزر، dotnet-format  مایکروسافت وجود دارد که بسیار کاربردیست؛ ولی ما ابزاری مثل commit lint هنوز در دات نت نداریم. فعلا در صورت نیاز باید برای استفاده از آن همچنان node و npm را نصب کنیم.
مطالب
ارتقاء به ASP.NET Core 2.0 - معرفی بسته‌ی Microsoft.AspNetCore.All
یکی از مهم‌ترین تغییرات ASP.NET Core 2.0، نسبت به نگارش‌های قبلی آن، ارائه‌ی یک «متا پکیج» جدید به نام Microsoft.AspNetCore.All است. این بسته به همراه تمام وابستگی‌های مورد نیاز جهت توسعه‌ی برنامه‌های ASP.NET Core 2.0 است؛ این «تمام» شامل تمام بسته‌های Razor، بسته‌های MVC، بسته‌های EF Core و غیره است. به این ترتیب به روز رسانی بسته‌های وابسته‌ی به هم، بسیار ساده خواهد شد و همچنین به فایل‌های csproj بسیار خلوت و قابل مدیریتی، خواهیم رسید.


بسته‌ی Microsoft.AspNetCore.All فقط مخصوص پروژه‌های netcoreapp2.0 است

این «متا پکیج» تنها در پروژه‌هایی که TargetFramework آن‌ها به netcoreapp2.0 تنظیم شده‌است، قابل استفاده می‌باشد:
 <TargetFramework>netcoreapp2.0</TargetFramework>
اگر TargetFramework تنظیمی netstandard2.0 باشد، قادر به استفاده‌ی از آن نخواهید بود. در این حالت باید مانند سابق، تک تک بسته‌های مورد نیاز را در فایل csproj به صورت جداگانه‌ای تعریف کنید.


نحوه‌ی به روز رسانی پروژه‌ها جهت استفاده‌ی از Microsoft.AspNetCore.All

پیش از ناقص کردن برنامه و حذف بسته‌های نیوگتی که نباید از فایل csproj حذف شوند، ابتدا باید لیستی را که توسط «متا پکیج» Microsoft.AspNetCore.All ارائه می‌شود، بررسی کرد. این لیست را پس از نصب SDK جدید، در آدرس ذیل می‌توانید مشاهده کنید:
 C:\Program Files\dotnet\store\x64\netcoreapp2.0


روش دیگر یافتن این لیست، مراجعه‌ی به سایت نیوگت و بررسی قسمت dependencies آدرس https://www.nuget.org/packages/Microsoft.AspNetCore.All است:


سپس به فایل csproj ایی که دارای TargetFramework مساوی netcoreapp2.0 است مراجعه کرده و هر کدام از بسته‌هایی را که در این لیست قرار دارند ... حذف کنید. در آخر بجای تمام این مداخل حذف شده، یک مدخل کلی ذیل را تعریف کنید:
<ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
</ItemGroup>


سؤال: آیا استفاده‌ی از بسته‌ی Microsoft.AspNetCore.All، ارائه‌ی نهایی برنامه را حجیم نمی‌کند؟

اگر به مسیر dotnet\store ایی که پیشتر عنوان شد مراجعه کنید، در اینجا بیش از 180 بسته را خواهید یافت. در این حالت شاید به نظر برسد که حجم نهایی قابل توزیع برنامه‌های ASP.NET Core با استفاده از تک مدخل Microsoft.AspNetCore.All بسیار بالا خواهد رفت. اما ... خیر.
NET Core 2.0. به همراه ویژگی جدیدی است به نام Runtime store. هدف از آن، پیش نصب بسته‌ها بر روی سیستم جاری، در یک مکان مرکزی است تا دیگر در حین توزیع نهایی برنامه، نیازی به توزیع مجدد آن‌ها نباشد. به همین جهت، به آن می‌توان شبیه به مفهوم پیشین Global assembly cache یا GAC مخصوص NET Core. نگاه کرد. به علاوه تمام این بسته‌ها ngen شده و سرعت آغاز و اجرای برنامه‌ها را بهبود می‌بخشند.
زمانیکه SDK جدید NET Core 2.0. را نصب می‌کنید، تمام بسته‌های مورد نیاز آن، در مسیر مرکزی C:\Program Files\dotnet\store نصب می‌شوند. بنابراین سیستمی که به همراه این SDK باشد، حتما حاوی تمام وابستگی‌های ذکر شده‌ی در متاپکیج Microsoft.AspNetCore.All نیز خواهد بود و در این حالت نیازی به توزیع مجدد آن‌ها نیست.
پس از آن مهم‌ترین تفاوتی را که مشاهده خواهید کرد، کاهش حجم نهایی برنامه‌های ASP.NET Core 2.0 نسبت به نگارش‌های 1x است. برای آزمایش، یک برنامه‌ی ASP.NET Core 1.x و سپس یک برنامه‌ی ساده‌ی ASP.NET Core 2.x را publish کنید.
این تصویر، پوشه‌ی نهایی قابل توزیع یک برنامه‌ی ASP.NET Core 1.x را پس از publish نمایش می‌دهد:

و این تصویر، پوشه‌ی نهایی قابل توزیع یک برنامه‌ی ASP.NET Core 2.x را پس از publish نمایش می‌دهد:

در این حالت پوشه‌ی نهایی نگارش 1x شامل 94 آیتم و پوشه‌ی نهایی نگارش 2x شامل 13 آیتم است. یعنی حجم نهایی را که باید ارائه داد، به شدت کاهش یافته‌است.


بالا رفتن کارآیی تازه واردان به دنیای ASP.NET Core با متاپکیج جدید Microsoft.AspNetCore.All

یکی از مشکلاتی که به همراه کار با ASP.NET Core 1.x وجود دارد، مشخص نبودن محل قرارگیری ویژگی‌های جدید و بسته‌های مرتبط با آن‌ها است. همچنین به ازای هر ویژگی جدید باید یک بسته‌ی نیوگت جدید را نصب کرد و عموما یافتن این‌ها و یا دانستن وجود آن‌ها، کار دشواری می‌باشد.
اما زمانیکه متابسته‌ی Microsoft.AspNetCore.All به قسمت ارجاعات پروژه اضافه می‌شود، در آغاز کار برنامه، سیستم IntelliSense آن‌ها را پردازش کرده و بلافاصله در اختیار برنامه نویس قرار می‌گیرند. این قابلیت حتی در VSCode نیز همانند Visual Studio کار می‌کند و توسعه دهنده‌ها بلافاصله IntelliSense بسیار کاملی را از قابلیت‌های موجود در اختیار خواهند داشت؛ به همراه ویژگی‌های تکمیلی دیگری مانند افزودن و یا اصلاح ساده‌تر فضاهای نام مرتبط.
مطالب
ساخت کتابخانه‌های عمومی جاوا اسکریپتی توسط Angular CLI
این روزها ساخت کتابخانه‌های مدرن جاوا اسکریپتی که نیازهای انواع و اقسام توسعه دهندگان آن‌ها را پوشش دهند، مشکل است. این کتابخانه‌ها باید دارای بسته‌های مختلفی با پشتیبانی از ES5 و ES2015 به بعد باشند؛ همچنین ارائه‌ی متادیتای مخصوص TypeScript را نیز پشتیبانی کنند. به علاوه مباحث کارآیی، minification و tree shaking (حذف کدهای مرده) را نیز مدنظر داشته باشید.
پیشتر مطلبی را در مورد ساخت کتابخانه‌های مخصوص Angular را به کمک Angular CLI، در این سایت مطالعه کرده بودید. در این مطلب فرض بر این است که شما توسعه دهنده‌ی Angular «نیستید»، اما قصد دارید با استفاده از ابزار Angular CLI، کتابخانه‌ی جاوا اسکریپتی عمومی بسیار مدرنی را با پشتیبانی از تمام موارد یاد شده، تولید کنید.


ساخت قالب آغازین کتابخانه به کمک Angular CLI

برای تبدیل کتابخانه‌های جاوا اسکریپتی خود به قالب مدرنی که در مقدمه عنوان شد، نیاز به ابزاری جهت خودکارسازی فرآیند‌های آن داریم و این ویژگی‌ها مدتی است که به ابزار Angular CLI اضافه شده‌اند و همانطور که عنوان شد، مخاطب این مطلب، توسعه دهندگان عمومی JavaScript است و نه صرفا توسعه دهندگان Angular. به همین جهت نیاز است ابتدا این ابزار را نصب کرد:
npm install -g @angular/cli
برای اجرای دستور فوق در خط فرمان، ابتدا باید آخرین نگارش nodejs را نیز نصب کرده باشید.
پس از نصب Angular CLI، از آن جهت ساخت قالب تولید کتابخانه‌های TypeScript ای استفاده می‌کنیم:
 ng new my-math-app
این دستور یک قالب پروژه‌ی آغازین Angular را ایجاد کرده و همچنین وابستگی‌های npm آن‌را نیز نصب می‌کند (بنابراین نیاز است به اینترنت نیز متصل باشید). البته ممکن است در حین اجرای این دستور سؤالاتی مبنی بر ایجاد مسیریابی و یا انتخاب بین css و sass نیز پرسیده شود. این موارد برای کار ما در اینجا مهم نیستند و هر پاسخی را که مایل بودید، ارائه دهید. در این مطلب ما کاری به این قالب نخواهیم داشت. فقط هدف ما افزودن یک کتابخانه‌ی جدید به آن است.
بنابراین پس از اجرای دستور فوق، از طریق خط فرمان به پوشه‌ی my-math-app وارد شده و سپس دستور زیر را اجرا کنید:
 ng generate library ts-math-example
این دستور، قالب آغازین یک کتابخانه‌ی جدید TypeScript ای را به پروژه‌ی Angular ما با نام ts-math-example اضافه می‌کند. اکنون می‌توانیم از این قالب جهت توسعه‌ی کتابخانه‌ی مدرن جاوا اسکریپتی خود استفاده کنیم.


تکمیل کتابخانه‌ی جاوا اسکریپتی

اکنون که به لطف Angular CLI، قالب آغازین ساخت یک کتابخانه‌ی TypeScript ای را داریم، می‌توانیم شروع به تکمیل آن کنیم. برای این منظور به پوشه‌ی my-math-app\projects\ts-math-example\src\lib مراجعه کرده و تمام فایل‌های پیش‌فرض آن‌را حذف کنید. این‌ها قالب‌های ساخت کتابخانه‌های Angularای هستند که ما در اینجا کاری به آن‌ها نداریم:


همچنین می‌توان به فایل my-math-app\projects\ts-math-example\package.json نیز مراجعه کرد (فایل package.json پروژه‌ی کتابخانه) و قسمت peerDependencies آن را که به Angular اشاره می‌کند نیز حذف نمود.

سپس یک فایل خالی math.ts را به پوشه‌ی یاد شده اضافه می‌کنیم:


با این محتوا:
export function add(num1: number, num2: number) {
    return num1 + num2;
}
کتابخانه‌ی ما کار ساده‌ی جمع زدن اعداد را انجام می‌دهد.

در ادامه نیاز است این ماژول را به فایل my-math-app\projects\ts-math-example\src\public-api.ts معرفی کرد تا به عنوان API قابل دسترسی کتابخانه، در دسترس قرار گیرد:
/*
* Public API Surface of ts-math-example
*/
export * from './lib/math';
هر فایلی که قرار است توسط کتابخانه‌ی ما در معرض دید عموم قرارگیرد، باید در فایل public_api.ts عمومی شود.

در حین توسعه‌ی کتابخانه خود،‌جهت اطمینان از صحت کامپایل برنامه، دستور ng build ts-math-example --watch را در پوشه‌ی my-math-app صادر کنید. کار آن کامپایل مداوم پروژه‌ی کتابخانه بر اساس تغییرات داده شده‌است. حاصل این کامپایل نیز در پوشه‌ی my-math-app\dist\ts-math-example قرار می‌گیرد:


این همان خروجی مدرنی است که در ابتدای بحث از آن صحبت کردیم و شامل کتابخانه‌های ES5 و ES2015 به بعد و همچنین ارائه‌ی متادیتای مخصوص TypeScript نیز هست.


کامپایل و انتشار نهایی کتابخانه

پس از تکمیل کتابخانه‌ی خود، اکنون می‌توانیم آن‌را به سایت npm، برای استفاده‌ی سایرین ارسال کنیم. برای این منظور باید مراحل زیر طی شوند:
ابتدا فایل package.json واقع در ریشه‌ی پوشه‌ی ts-math-example را جهت تعریف اطلاعات این کتابخانه، تکمیل کنید. سپس دستورات زیر را در ریشه‌ی پروژه‌ی اصلی صادر کنید:
ng build ts-math-example --prod
cd dist/ts-math-example
npm publish
دستور اول کتابخانه را در حالت production تولید می‌کند که حداکثر بهینه سازی‌ها را به همراه دارد.
با دستور دوم به پوشه‌ی خروجی کتابخانه وارد شده و دستور سوم، آن‌را به سایت npm ارسال می‌کند.

استفاده کننده‌ی از کتابخانه‌ی ما (این استفاده کننده می‌تواند هر نوع پروژه‌ی جاوا اسکریپتی اعم از Angular ،React ،Vue ،ES6 ،TypeScript و غیره باشد) ابتدا با دستور npm install ts-math-example --save آن‌را نصب و به پروژه‌ی خود اضافه کرده و سپس به نحو زیر می‌تواند از آن استفاده کند:
 import { add } from '@myuser/ts-math-example';
مطالب
نمایش در حال بارگذاری بودن صفحه در برنامه‌های Angular
پیشتر در مورد HTTP Client جدید Angular، مطلب «ارتقاء به HTTP Client در Angular 4.3» را بررسی کردیم. یکی دیگر از قابلیت‌های HttpClient که روش اصلی دسترسی به اطلاعات از راه دور در Angular 5 نیز می‌باشد، امکان تعریف Interceptors سفارشی برای آن است. به کمک Interceptors می‌توان به تمامی درخواست‌های HTTP ارسالی و پاسخ‌های دریافتی از سرور گوش فرا داد و در این بین منطق ویژه‌ای را پیاده سازی کرد؛ مانندcaching ،logging و غیره. در مطلب جاری قصد داریم از این قابلیت برای نمایش یک loading bar مشخص کننده‌ی شروع و پایان تمامی درخواست‌های HTTP استفاده کنیم.


دریافت و نصب پیشنیازها

برای نمایش loading bar در بالای صفحه، از کامپوننت ng2-slim-loading-bar استفاده خواهیم کرد. دمویی از آن‌را در اینجا می‌توانید مشاهده کنید.
برای نصب آن، ابتدا دستور ذیل را در ریشه‌ی پروژه اجرا کنید:
 npm install ng2-slim-loading-bar --save
پس از آن فایل angular-cli.json. را گشوده و فایل css آن‌را تعریف نمائید:
"styles": [
   "../node_modules/ng2-slim-loading-bar/style.css",
   "styles.css"
],
در ادامه به فایل app.module.ts مراجعه کرد و ماژول آن‌را نیز به نحو ذیل به برنامه معرفی کنید:
import {SlimLoadingBarModule} from 'ng2-slim-loading-bar';

@NgModule({
imports: [
     //...
     SlimLoadingBarModule.forRoot()
  ]
})
export class AppModule {
}

پس از این مقدمات، هرجایی که نیاز به استفاده‌ی از آن با کدنویسی باشد، ابتدا سرویس SlimLoadingBarService آن‌را به سازنده‌ی کلاس کامپوننت مدنظر تزریق می‌کنیم و سپس از متدهای start ،stop و complete آن می‌توان استفاده کرد.
 import {SlimLoadingBarService} from 'ng2-slim-loading-bar';
همچنین برای نمایش این کامپوننت باید selector آن را در فایل app.component.html در بالای صفحه اضافه کرد:
  <ng2-slim-loading-bar></ng2-slim-loading-bar>


تدارک یک LoaderInterceptor برای استفاده از ng2-slim-loading-bar

در ادامه می‌خواهیم هر زمانیکه در سراسر برنامه، درخواست HTTP ایی شروع شد، این کامپوننت نمایش داده شود و در پایان درخواست و یا درصورت بروز خطایی، پایان یابد و مخفی شود. برای این منظور یک Interceptor جدید را به صورت ذیل به پوشه‌ی Core برنامه اضافه می‌کنیم:
 ng g s core/interceptors/LoaderInterceptor --spec false
با این کدها:
import { Injectable } from "@angular/core";
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from "@angular/common/http";
import { Observable } from "rxjs/Observable";
import "rxjs/add/operator/do";
import { SlimLoadingBarService } from "ng2-slim-loading-bar";


@Injectable()
export class LoaderInterceptorService implements HttpInterceptor {

  constructor(private loadingBar: SlimLoadingBarService) {
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // start our loader here
    this.loadingBar.start();
    return next.handle(req).do(
      (event: HttpEvent<any>) => {
        if (event instanceof HttpResponse) {
          this.loadingBar.complete();
        }
      },
      (err: any) => {
        this.loadingBar.complete();
      });
  }
}
توضیحات:
- برای پیاده سازی یک interceptor جدید، نیاز است کلاس سرویسی را که HttpInterceptor را پیاده سازی می‌کند، ایجاد کنیم. برای تکمیل این پیاده سازی نیاز است متد intercept را با امضایی که مشاهده می‌کنید، تعریف کنیم:
 intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
- در اینجا پارامتر req به خود درخواست Http اشاره می‌کند.
- کار پارامتر next تنظیم و بازگشت یک HttpEvent observable توسط متد handle آن است.

برای نمونه در اینجا ابتدا سرویس SlimLoadingBarService به سازنده‌ی کلاس interceptor‌  تزریق شده‌است. سپس توسط آن می‌توان به متدهای start و complete این کامپوننت دسترسی یافت. برای مثال در ابتدای کار گوش فرادادن به درخواست جاری، متد start فراخوانی شده‌است و سپس زمانیکه پاسخی از سرور دریافت شده و یا خطایی رخ‌داده، متد complete آن فراخوانی شده‌است.


ثبت و معرفی LoaderInterceptorService به سیستم

برای معرفی interceptor تهیه شده، به فایل app.module.ts مراجعه کرد و قسمت providers آن‌را به نحو ذیل تکمیل می‌کنیم: 
 @NgModule({
  providers: [
   { provide: HTTP_INTERCEPTORS, useClass: LoaderInterceptorService, multi: true }
  ]
})
export class AppModule {}


آزمایش برنامه

اکنون اگر قسمت‌های مختلف برنامه را که با HttpClient جدید کار می‌کنند بررسی کنید، متوجه خواهید شد که با شروع هر درخواست، loading bar قرمزی در بالای صفحه ظاهر می‌شود و در پایان درخواست، به صورت خودکار مخفی می‌گردد. نکته‌ی مهم این روش عدم نیاز به تغییری در قسمت‌های مختلف برنامه است. این interceptor سراسری است و به صورت یکسانی بر روی کل برنامه تاثیر می‌گذارد.