اشتراک‌ها
کنفرانس توسعه دهندگان Build مایکروسافت یک هفته پس از انتشار نهایی ویندوز 8 برگزار می شود.
کنفرانس Build که از سال قبل جای کنفرانس PDC مایکروسافت را گرفته است، امسال یک هفته پس از انتشار نهایی ویندوز 8 به مدت سه روز از سی ام اکتبر تا دوم نوامبر برگزار خواهد شد.
کنفرانس توسعه دهندگان Build مایکروسافت یک هفته پس از انتشار نهایی ویندوز 8 برگزار می شود.
نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 19 - بومی سازی
- در نگارش‌های جدید ASP.NET Core، بسته‌ی بومی سازی آن، خطاهای یافت نشدن کلیدها را توسط ILogger آن لاگ می‌کند (و هیچ استثنایی را مشاهده نخواهید کرد). به همین جهت لاگ کردن را در برنامه‌ی خود فعال کنید و خروجی آن‌را جهت یافتن عباراتی مانند search for بررسی کنید (دقیقا عنوان می‌کند که کجاها را برای یافتن معادل‌ها بررسی کرده‌است و چیزی یافت نشده).
- برای مثال اگر از قالب پیش‌فرض ASP.NET Core 2.1 استفاده می‌کنید، لاگ کردن در کنسول و همچنین دیباگ آن فعال است (بدون نیاز به تنظیم اضافه‌تری). در اینجا برنامه را در حالت dotnet watch run اجرا کنید (این دستور را در ریشه‌ی پروژه اجرا کنید) و سپس در کنسول جاری، عبارات لاگ شده را بررسی کنید. یا پنجره‌ی debug در ویژوال استودیو نیز همینکار را انجام می‌دهد.
بررسی لاگ‌های برنامه جزو الزامات کار با برنامه‌های NET Core. است. در اینجا کمتر استثناءها را مشاهده می‌کنید و چون یک فریم ورک توکار Logging را به همراه دارد، همه‌جا از آن برای گزارش مشکلات، در پشت صحنه استفاده می‌شود.
نظرات مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت اول - نصب پیشنیازها

برای استفاده از Angular CLI در پروژه های MVC  یا کلا استفاده از ویژوال استودیو باید اینو در نظر گرفت که دیگه کار IDE نگهداری فایلهای پروژه هست و حتی برای Run گرفتن از پروژه باید از کامندهای CLI استفاده کنید، من مثالی رو در گیت هاب دارم میتونید اونو بگیرید و بعد کامند لاین رو از روت پروژه باز کنید و کامندهای CLI رو برای دریافت ماجول‌های Angular CLI  و خروجی گرفتن اجرا کنید و کار توسعه رو ادامه بدید :

https://github.com/mehdipayervand/MVCSampleNG2

بعد از دریافت پروژه کامندهای زیر توی روت پروژه اجرا کنید:(کمی زمانبر هست تقزیبا 150 مگابایت)

npm install

ng build

و اگر خطایی مشاهده نکردید:

ng serve

حالا آدرس زیر رو توی مرورگرتون باز کنید و ادامه کارتون ...

http://localhost:4200/

اشتراک‌ها
اضافه شدن قابلیت Package Registry به Github

Today, we’re excited to introduce GitHub Package Registry, a package management service that makes it easy to publish public or private packages next to your source code.

GitHub Package Registry is fully integrated with GitHub, so you can use the same search, browsing, and management tools to find and publish packages as you do for your repositories. You can also use the same user and team permissions to manage code and packages together. GitHub Package Registry provides fast, reliable downloads backed by GitHub’s global CDN. And it supports familiar package management tools: JavaScript (npm), Java (Maven), Ruby (RubyGems), .NET (NuGet), and Docker images, with more to come.

اضافه شدن قابلیت Package Registry به Github
مطالب
تولید و ارسال خودکار بسته‌های NuGet پروژه‌های NET Core. به کمک AppVeyor
اگر پروژه‌ی شما به همراه توزیع بسته‌های نیوگت است، پس از مدتی، از build و آپلود دستی بسته‌های نیوگت آن‌ها خسته خواهید شد. همچنین این سؤال هم برای مصرف کنندگان بسته‌ی نیوگت شما همواره وجود خواهد داشت: «آیا بسته‌ی نهایی را که آپلود کرده، دقیقا بر اساس سورس کد موجود در مخزن کد عمومی آن تهیه شده‌است؟»
برای رفع این مشکلات، از روش‌های توسعه‌ی به همراه ابزارهای یکپارچگی مداوم استفاده می‌شود. برای نمونه، AppVeyor یکی از سرویس‌های ابری یکپارچگی مداوم (Continuous Integration و یا به اختصار CI) است. به کمک آن می‌توان یک image از ویندوز سرور را به همراه ابزارهای build، آزمایش و توزیع برنامه‌های NET. در اختیار داشت. این سرویس، مخزن کد شما را مونیتور کرده و هر زمانیکه تغییری را در آن ایجاد کردید، آن‌ها را به صورت خودکار build و در صورت موفقیت آمیز بودن این عملیات، بسته‌ی نیوگت متناظری را به سایت nuget.org ارسال می‌کند. بنابراین پس از یکپارچه کردن مخزن کد خود با این نوع سرویس‌های یکپارچگی مداوم، دیگر حتی نیازی به build دستی آن نیز نخواهید داشت. همینقدر که کدی را به مخزن کد تحت نظر، commit کنید، مابقی مراحل آن خودکار است.
به همین جهت در این مطلب قصد داریم نحوه‌ی اتصال یک مخزن کد GitHub را به سرویس یکپارچگی مداوم AppVeyor، جهت تولید خودکار بسته‌های Nuget، بررسی کنیم.




معرفی سرویس ابری AppVeyor

AppVeyor یک راه حل یکپارچگی مداوم چند سکویی است که استفاده‌ی از آن برای پروژه‌های سورس باز رایگان است و سازگاری فوق العاده‌ای را با محصولات مایکروسافت دارد. برای ورود به آن می‌توان از اکانت‌های GitHub ،BitBucket و VSTS (Visual Studio Team Services) استفاده کرد.
گردش کاری متداول یکپارچگی مداوم AppVeyor به این صورت است:
الف) با اکانت GitHub خود به آن وارد شوید.
ب) یک مخزن کد GitHub خود را به آن Import کنید.
ج) به مخزن کد GitHub خود یک فایل yml. تنظیمات مخصوص AppVeyor را اضافه کنید.
د) نظاره‌گر Build و توزیع خودکار پروژه‌ی خود باشید.


ایجاد اکانت و اتصال به مخزن کد GitHub

در ابتدا به صفحه‌ی لاگین آن مراجعه کنید. در اینجا جهت سهولت کار با GitHub و مخازن کد آن، گزینه‌ی GitHub را انتخاب کرده و توسط آن به سیستم وارد شوید:


پس از ورود موفق، گزینه‌ی new project را انتخاب کنید:


در ادامه مخزن کد GitHub و نوع عمومی آن‌را انتخاب می‌کنیم تا AppVeyor بتواند پروژه‌های آن‌را Import کند و همچنین به آن‌ها web hookهایی را اضافه کند تا با اعمال تغییراتی در سمت GitHub، کار اطلاع رسانی آن‌ها به AppVeyor به صورت خودکار صورت گیرد:


پس از آن لیست مخزن‌های کد شما در همینجا ارائه می‌شود تا بتوانید یک یا چند مورد را انتخاب کنید:


انجام تنظیمات عمومی مخزن کد

در صفحه‌ی بعدی، برگه‌ی settings و سپس از منوی کنار صفحه‌ی آن، گزینه‌ی ‌General را انتخاب کنید:


در اینجا اگر پروژه‌ی شما از نوع NET Core. است، گزینه‌ی NET Core .csproj patching. را انتخاب نمائید:


سپس در پایین صفحه بر روی دکمه‌ی Save کلیک کنید.


انتخاب و تنظیم محیط Build

در ادامه در برگه‌ی settings و سپس از منوی کنار صفحه‌ی آن، گزینه‌ی Environment را انتخاب کنید:


در این صفحه، worker image را بر روی VS 2017 قرار دهید و همچنین در قسمت Cached directories and files، مسیر C:\Users\appveyor\.dnx را جهت کش کردن عملیات Build و بالا بردن سرعت آن، مقدار دهی کنید. سپس در پایین صفحه بر روی دکمه‌ی Save کلیک نمائید.

اکنون بر روی گزینه‌ی Build در منوی سمت چپ صفحه کلیک کنید. در اینجا سه حالت msbuild ،script و off را می‌توان انتخاب کرد.
- در حالت msbuild، که ساده‌ترین حالت ممکن است، فایل sln. مخزن کد، یافت شده و بر اساس آن به صورت خودکار تمام پروژه‌های این solution یکی پس از دیگری build خواهند شد. این مورد برای برنامه‌های Full .NET Framework شاید گزینه‌ی مناسبی باشد.
- حالت script برای پروژه‌های NET Core. مناسب‌تر است و در این حالت می‌توان کنترل بیشتری را بر روی build داشت. به علاوه این روش بر روی لینوکس هم کار می‌کند؛ زیرا در آنجا دسترسی به msbuild نداریم.
- حالت off به معنای خاموش کردن عملیات build است.

در اینجا گزینه‌ی cmd را جهت ورود build script انتخاب می‌کنیم:


سپس دستورات ذیل را جهت ورود به پوشه‌ی پروژه‌ی کتابخانه (جائیکه فایل csproj آن قرار دارد)، بازیابی وابستگی‌های پروژه و سپس تولید بسته‌ی نیوگتی از آن، وارد می‌کنیم:
cd ./src/DNTPersianUtils.Core
dotnet restore
dotnet pack -c release
cd ../..
در ادامه در پایین صفحه بر روی دکمه‌ی Save کلیک نمائید.
ذکر  ../.. cd  در انتهای این دستورات ضروری است. در غیر اینصورت cd بعدی در تنظیماتی دیگر، داخل همین پوشه انجام می‌شود.


تنظیم اجرای خودکار آزمون‌های واحد


در همین صفحه، گزینه‌های settings -> tests -> script -> cmd را انتخاب و سپس دستورات زیر را وارد کرده و آن‌را ذخیره کنید:
cd ./src/DNTPersianUtils.Core.Tests
dotnet restore
dotnet test
cd ../..
به این ترتیب به صورت خودکار آزمون‌های واحد موجود در پروژه‌ی انتخابی، توسط NET Core CLI. اجرا خواهند شد.


تنظیم اطلاع رسانی خودکار از اجرای عملیات


در برگه‌ی settings -> notifications مطابق تنظیمات فوق می‌توان نوع email را جهت اطلاع رسانی شکست انجام عملیات یکپارچگی مداوم، انتخاب کرد.


آزمایش Build خودکار

برای آزمایش تنظیماتی که انجام دادیم، به برگه‌ی latest build مراجعه کرده و بر روی دکمه‌ی new build کلیک کنید تا اسکریپت‌های build و test فوق اجرا شوند. بدیهی است اجرای بعدی این اسکریپت‌ها خودکار بوده و به ازای هر commit به GitHub، بدون نیاز به مراجعه‌ی مستقیم به appveyor صورت می‌گیرند.



اضافه کردن نماد AppVeyor به پروژه

در تنظیمات برگه‌ی Settings، گزینه‌ی AppVeyor badge نیز در منوی سمت چپ صفحه، وجود دارد:


در اینجا همان کدهای mark down آن‌را انتخاب کرده و به ابتدای فایل readme پروژه‌ی خود اضافه کنید. برای نمونه نماد فعلی (تصویر فوق)، build failing را نمایش می‌دهد؛ چون سه آزمون واحد آن مشکل دارند و باید اصلاح شوند.
پس از رفع مشکلات پروژه و commit آن‌ها، build و اجرای خودکار آزمون‌های واحد آن توسط AppVeyor صورت گرفته و اینبار این نماد به صورت زیر تغییر می‌کند:



ارسال خودکار بسته‌ی نیوگت تولید شده به سایت nuget.org

برای ارسال خودکار حاصل Build، به سایت نیوگت، نیاز است یک API Key داشته باشیم. به همین جهت به صفحه‌ی مخصوص آن در سایت nuget پس از ورود به سایت آن، مراجعه کرده و یک کلید API جدید را صرفا برای این پروژه تولید کنید (در قسمت Available Packages بسته‌ی پیشینی را که دستی آپلود کرده بودید انتخاب کنید).
پس از کپی کردن کلید تولید شده‌ی در سایت nuget، به قسمت settings -> deployment مراجعه کرده و یک تامین کننده‌ی جدید از نوع nuget را اضافه کنید:


در اینجا API Key را ذکر خواهیم کرد. سپس در پایین صفحه بر روی دکمه‌ی Save کلیک کنید.

همچنین نیاز است مشخص کنیم که بسته‌های nupkg تولید شده در چه مسیری قرار دارند. به همین جهت در قسمت settings -> artifacts مسیر پوشه‌ی bin نهایی را ذکر می‌کنیم:


این مورد را نیز با کلیک بر روی دکمه‌ی Save ذخیره کنید.

اکنون اگر نگارش جدیدی را به GitHub ارسال کنیم (تغییر VersionPrefix در فایل csproj و سپس commit آن)، پس از Build پروژه، بسته‌ی نیوگت آن نیز به صورت خودکار تولید شده و به سایت nuget.org ارسال می‌شود. لاگ آن‌را در پایین صفحه‌ی برگه‌ی latest build می‌توانید مشاهده کنید.



ساده سازی مراحل تنظیمات AppVeyor

در صفحه‌ی settings‌، در منوی سمت چپ آن، گزینه‌ی export YAML نیز وجود دارد. در اینجا می‌توان تمام تنظیمات انجام شده‌ی فوق را با فرمت yml. دریافت کرد و سپس این فایل را به ریشه‌ی مخزن کد خود افزود. با وجود این فایل، دیگر نیازی به طی کردن دستی هیچکدام از مراحل فوق نیست.
برای نمونه فایل appveyor.yml نهایی مطابق با توضیحات این مطلب، چنین محتوایی را دارد:
version: 1.0.{build}
image: Visual Studio 2017
dotnet_csproj:
  patch: true
  file: '**\*.csproj'
  version: '{version}'
  package_version: '{version}'
  assembly_version: '{version}'
  file_version: '{version}'
  informational_version: '{version}'
cache: C:\Users\appveyor\.dnx
build_script:
- cmd: >-
    cd ./src/DNTPersianUtils.Core

    dotnet restore

    dotnet pack -c release

    cd ../..
test_script:
- cmd: >-
    cd ./src/DNTPersianUtils.Core.Tests

    dotnet restore

    dotnet test

    cd ../..
artifacts:
- path: ./src/DNTPersianUtils.Core/bin/release/*.nupkg
  name: NuGet
deploy:
- provider: NuGet
  api_key:
    secure: xyz
  skip_symbols: true
notifications:
- provider: Email
  to:
  - me@yahoo.com
  on_build_success: false
  on_build_failure: true
  on_build_status_changed: true
بنابراین بجای طی مراحل عنوان شده‌ی در این بحث می‌توانید یک فایل appveyor.yml را با محتوای فوق (پس از اصلاح مسیرها و نام‌ها) در ریشه‌ی پروژه‌ی خود قرار دهید و ... صرفا مخزن کد آن‌را در appveyor ثبت و import کنید. مابقی مراحل یکپارچگی مداوم آن خودکار است و نیاز به تنظیم دیگری ندارد. فقط برای آزمایش آن به برگه‌ی Latest build مراجعه کرده و یک build جدید را شروع کنید تا مطمئن شوید همه چیز به درستی کار می‌کند.
یک نکته: api_key ذکر شده‌ی در اینجا در قسمت secure، رمزنگاری شده‌است. برای تولید آن می‌توانید از مسیر https://ci.appveyor.com/tools/encrypt استفاده کنید. به این صورت مشکلی با عمومی کردن فایل appveyor.yml خود در GitHub نخواهید داشت.
اشتراک‌ها
دانلود تمامی محتوای کنفرانس Microsoft Build 2017
در هفته‌ی گذشته کنفرانس Microsoft Build 2017 برگزار و جدیدترین تکنولوژی‌های مرتبط با حوزه‌های کاربری و توسعه در آن مطرح گردید. با استفاده از فایل PowerShell ذیل می‌توانید تمامی محتوای این کنفرانس را با کیفیت بالا دانلود نمایید. ویژگی‌های استفاده از این اسکریپت به شرح ذیل می‌باشد:
  • دانلود تمامی محتواها شامل ویدیوها، اسلایدها و متون ارائه شده
  • گروه بندی در فولدرهای مختلف
  • شروع مجدد دانلود در صورت قطع ارتباط از آخرین مرحله
  • فیلتر نمودن دانلود براساس کلمات کلیدی مورد نظر
  • توانایی انتخاب فرمت ویدیوها جهت دانلود
جهت استفاده از این اسکریپت مراحل ذیل را دنبال کنید.
  • به آدرس ذیل مراجعه و فایل PowerShell مربوطه را دانلود نمایید.
  • جهت دانلود تمامی محتواهای مطرح شده با حجم حدودی 1 ترابایت از دستور ذیل استفاده نمایید.
.\ DownloadBuild2017Content.ps1
  • جهت دانلود براساس کلمات کلیدی مورد نظر از دستور ذیل استفاده نمایید. 
.\ DownloadBuild2017Content.ps1 -keyword "Graph,Bluetooth"
  • جهت دانلود براساس کدهای مرتبط با Session‌های برگزار شده از دستور ذیل استفاده نمایید.
.\DownloadBuild2017Content.ps1 -session "P4116,P4118,C9R04"
دقت داشته باشید که تمامی ویدیوها به صورت پیش فرض با فرمت HD دانلود می‌شوند، در صورتی که می‌خواهید فرمت ویدیوها را جهت دانلود سریع‌تر تغییر دهید می‌بایست خطوط 18 و 19 فایل دانلود شده را تغییر دهید.
دانلود تمامی محتوای کنفرانس Microsoft Build 2017
نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 1 - NET Core. چیست؟
Error: پس از ارتقا به نسخه Net Core 1.0.1.  با خطای زیر در زمان Build مواجه شدم :

Can not find runtime target for framework '.NETCoreAPP, Version=v1.0' compatible with one of the target runtimes: 'win10-x64, win81-x64, win8-x64, win7-x64'. Possible causes:

  The project has not been restored or restore failed -run 'dotnet restore'
  The project does not list one of 'win10-x64, win81-x64, win7-x64' in the 'runtimes'

Fix: اضافه کردن موارد زیر در project.json :

"runtimes": {
    "win10-x64": {},
    "win81-x64": {},
    "win8-x64": {}
  }
نظرات نظرسنجی‌ها
برای توسعه برنامه‌های مبتنی بر NET Core. از چه محیطی استفاده می‌کنید؟
پروژه ای دارم در نسخه Core 1.1 که  شامل 10 پروژه سایت خروجی و یک پروژه مدیریتی ادمین  

بعد از افزایش ماژول‌های مختلف و  به نسبت حجیم شدن پروژه به طور وحشتناکی کاهش سرعت اجرا بعد از build پروژه رو دارم
پروژه در حالت توسعه هم این مشکل رو به ندرت در محیط Visual Studio داره ولی در VSCode این مشکل فقط پس از اجرای dotnet run با این مشکل روبرو میشم.

به طوری که بعد از اجرا CPU Usage 100% , Disk Usage 100%  تا مدتی استفاده میشه برای مثال سیستمی با این مشخصات :
cpu: i7
Ram : 16 DDR4
HDD: 500 SSD  

 این مشکل در بین تمامی اعضا تیم داریم. آیا محدودیت در حجم پروژه هست؟ آیا با مهاجرت به نسخه 2.1 این مشکل کمتر میشه؟
مطالب
انتشار عمومی localhost به صورت HTTPS توسط ngrok
شاید برای شما هم پیش آمده باشد که با Webhookها کار کنید و یا در حین اجرای پروژه‌ی وب خودتان بخواهید خروجی آن را به اطرافیان خود نشان دهید. یکی از راه‌ها این است  که پروژه را بر روی یک مخزن Git ارسال کنید و سپس دوستان خودتان را اضافه کنید تا بتوانند پروژه را دریافت و اجرا کنند. البته در این حالت شاید نخواهید کسی سورس شما را ببیند! روش دیگر این است که هاست و دامین بخرید و پروژه را بر روی آن آپلود کنید و در مرحله‌ی آخر، آدرس وبسایت را برای اطرافیان خود بفرستید که این راه، هم  پرهزینه هست و هم ممکن است پروژه کامل نشده باشد که بخواهید آن‌را آپلود کنید. اینجاست که Ngrok وارد شده و پروژه‌ی لوکال شما را بر روی Https ارائه میکند.


شروع به کار با ngrok 

- قبل از ادامه‌ی این آموزش، وارد سایت https://ngrok.com شده و بعد از ثبت نام، مطابق سیستم عاملی که دارید، فایل مخصوص آن را دریافت کنید.
- بعد از دریافت، فایل زیپ را از حالت فشرده خارج کنید. به محل فایل استخراج شده رفته و در یک مکان خالی در پوشه‌ی جاری، دکمه‌ی Shift را فشرده و سپس کلیک راست کنید و در آخر گزینه‌ی Open Command Window Here را انتخاب نمائید و یا کلا از طریق cmd وارد پوشه‌ی مربوطه شوید.
- سپس پروژه‌ی خود را توسط ویژوال استودیو (IIS Express) و یا بر روی IIS لوکال خود، اجرا کنید.

- همانطور که در تصویر مشاهده می‌کنید؛ آدرس پروژه‌ی لوکال ما به شکل زیر می‌باشد: 
 http://localhost:51095/Home/Index
توجه! این پروژه‌ی آزمایشی صرفا یک صفحه‌ی HTML خیلی ساده بوده که فقط عبارت ngrok را نشان می‌دهد.

- حال به cmd برمی‌گردیم و سپس با دستور زیر میتوانیم لوکال‌هاست خود را بر روی https به اشتراک بگذاریم:
 ngrok http [port] -host-header="localhost:[port]"
اگر کار را به درستی انجام داده باشید، خروجی شبیه به تصویر زیر را خواهید داشت:


همانطور که ملاحظه می‌فرمائید، هم http و هم https را در اختیار ما قرار می‌دهد. برای مثال اگر نیاز است با webhookهای تلگرام کار کنید، باید از آدرس https آن استفاده کنید.
در اینجا در قسمت  HTTP Requests  می‌توانید درخواست‌هایی را که فرستاده می‌شوند، ببینید و یا میتوانید با رفتن به آدرس زیر، دستورات بالا را در نمایی گرافیکی و بصورت کامل نظاره‌گر باشید:
 http://127.0.0.1:4040

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



 
مطالب
آموزش Prism #2
در پست قبلی توضیح کلی درباره فریم ورک Prism داده شد. در این بخش قصد داریم آموزش‌های داده شده در پست قبلی را با هم در یک مثال مشاهده کنیم. در پروژه‌های ماژولار طراحی و ایجاد زیر ساخت قوی برای مدیریت ماژول‌ها بسیار مهم است. Prism فریم ورکی است که فقط چارچوب و قواعد اصول طراحی این گونه پروژه‌ها را در اختیار ما قرار می‌دهد. در پروژه‌های ماژولار هر ماژول باید در یک اسمبلی جدا قرار داشته باشد که ساختار پیاده سازی آن می‌تواند کاملا متفاوت با پیاده سازی سایر ماژول‌ها باشد.
 برای شروع  باید فایل‌های اسمبلی Prism رو دانلود کنید(لینک دانلود).
تشریح پروژه:
می‌خواهیم برنامه ای بنویسیم که دارای سه ماژول زیر است.:
  1. ماژول Navigator : برای انتخاب و Switch کردن بین ماژول‌ها استفاده می‌شود؛
  2. ماژول طبقه بندی کتاب‌ها : لیست طبقه بندی کتاب‌ها را به ما نمایش می‌دهد؛
  3. ماژول لیست کتاب‌ها : عناوین کتاب‌ها به همراه نویسنده و کد کتاب را به ما نمایش می‌دهد.

*در این پروژه از UnityContainer برای مباحث Dependency Injection استفاده شده است.
ابتدا یک پروژه WPF در Vs.Net ایجاد کنید(در اینجا من نام آن را  FirstPrismSample گذاشتم). قصد داریم یک صفحه طراحی کنیم که دو ماژول مختلف در آن لود شود. ابتدا باید Shell پروژه رو طراحی کنیم. یک Window جدید به نام Shell بسازید و کد زیر را در آن کپی کنید.
<Window x:Class="FirstPrismSample.Shell"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:com="http://www.codeplex.com/CompositeWPF"
    Title="Prism Sample By Masoud Pakdel" Height="400" Width="600" WindowStartupLocation="CenterScreen">
    <DockPanel>
      <ContentControl com:RegionManager.RegionName="WorkspaceRegion" Width="400"/>
      <ContentControl com:RegionManager.RegionName="NavigatorRegion"  DockPanel.Dock="Left" Width="200" />     
    </DockPanel>
</Window>
در این صفحه دو ContentControl تعریف کردم یکی به نام Navigator و دیگری به نام Workspace. به وسیله RegionName که یک AttachedProperty است هر کدوم از این نواحی را برای Prism تعریف کردیم. حال باید یک ماژول برای Navigator و دو ماژول دیگر یکی برای طبقه بندی کتاب‌ها و دیگری برای لیست کتاب‌ها بسازیم.

#پروژه Common
قبل از هر چیز یک پروژه Common می‌سازیم و مشترکات بین ماژول‌ها رو در آن قرار می‌دهیم(این پروژه باید به تمام ماژول‌ها رفرنس داده شود).  این مشترکات شامل :
  • کلاس پایه ViewModel
  • کلاس ViewRequestEvent
  • کلاس ModuleService

کد کلاس ViewModelBase که فقط اینترفیس INotifyPropertyChanged رو پیاده سازی کرده است:

using System.ComponentModel;

namespace FirstPrismSample.Common
{
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void RaisePropertyChangedEvent( string propertyName )
        {
            if ( PropertyChanged != null )
            {
                PropertyChangedEventArgs e = new PropertyChangedEventArgs( propertyName );
                PropertyChanged( this, e );
            }
        }
    }
}
کلاس ViewRequestEvent که به صورت زیر است:
using Microsoft.Practices.Composite.Presentation.Events;

namespace FirstPrismSample.Common.Events
{
    public class ViewRequestedEvent : CompositePresentationEvent<string>
    {
    }
}
توضیح درباره CompositePresentationEvent :
در طراحی و توسعه پروژه‌های ماژولار نکته ای که باید به آن دقت کنید این است که ماژول‌های پروژه نباید به هم وابستگی مستقیم داشته باشند در عین حال ماژول‌ها باید بتوانند با هم در ارتباط باشند. CPE یا CompositePresentationEventدقیقا برای این منظور به وجود آمده است. CPE که در این جا طراحی کردم فقط کلاسی است که از CompositePresentationEventارث برده است و دلیل آن که به صورت string generic استفاده شده است این است که می‌خواهیم در هر درخواست نام ماژول درخواستی را داشته باشیم و به همین دلیل نام آن را ViewRequestedEvent گذاشتم.

توضیح درباره EventAggregator

EventAggregator یا به اختصار EA مکانیزمی است در پروژهای ماژولار برای اینکه در Composite UI‌ها بتوانیم بین کامپوننت‌ها ارتباط برقرار کنیم. استفاده از EA وابستگی بین ماژول‌ها را  از بین خواهد برد. برنامه نویسانی که با MVVM Light آشنایی دارند از قابلیت Messaging موجود در این فریم ورک برای ارتباط بین View و  ViewModel استفاده می‌کنند. در Prism این عملیات توسط EA انجام می‌شود. یعنی برای ارتباط با View‌ها باید از EA تعبیه شده در Prism استفاده کنیم. در ادامه مطلب، چگونگی استفاده از EA را خواهید آموخت.
اینترفیس IModuleService که فقط شامل یک متد است:
namespace FirstPrismSample .Common
{
    public interface IModuleServices
    {     
        void ActivateView(string viewName);
    }
}
کلاس ModuleService که اینترفیس بالا را پیاده سازی کرده است:
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;

namespace FirstPrismSample.Common
{
    public class ModuleServices : IModuleServices
    {     
        private readonly IUnityContainer m_Container;  
     
        public ModuleServices(IUnityContainer container)
        {
            m_Container = container;
        }      
   
        public void ActivateView(string viewName)
        {        
            var regionManager = m_Container.Resolve<IRegionManager>();

            // غیر فعال کردن ویو
            IRegion workspaceRegion = regionManager.Regions["WorkspaceRegion"];
            var views = workspaceRegion.Views;
            foreach (var view in views)
            {
                workspaceRegion.Deactivate(view);
            }

            //فعال کردن ویو انتخاب شده 
            var viewToActivate = regionManager.Regions["WorkspaceRegion"].GetView(viewName);
            regionManager.Regions["WorkspaceRegion"].Activate(viewToActivate);
        }
    }
}
متد ActivateView نام view مورد نظر برای فعال سازی را دریافت می‌کند. برای فعال کردن View ابتدا باید سایر view‌های فعال در RegionManager را غیر فعال کنیم. سپس فقط view مورد نظر در RegionManager انتخاب و فعال می‌شود.

*نکته: در هر ماژول ارجاع به اسمبلی‌های Prism مورد نیاز است.

#ماژول طبقه بندی کتاب ها:
برای شروع یک Class Library جدید به نام ModuleCategory به پروژه اضافه کنید. یک UserControl به نام CategoryView بسازید و کد‌های زیر را در آن کپی کنید.
<UserControl x:Class="FirstPrismSample.ModuleCategory.CategoryView "
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             Background="LightGray" FlowDirection="RightToLeft" FontFamily="Tahoma">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBlock Text=" طبقه بندی ها"/>
        <ListView Grid.Row="1"  Margin="10" Name="lvCategory">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="کد" Width="50" />
                    <GridViewColumn Header="عنوان" Width="200"  />                  
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</UserControl>
یک کلاس به نام CategoryModule بسازید که اینترفیس IModule رو پیاده سازی کند.
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;
using FirstPrismSample.Common;
using FirstPrismSample.Common.Events;
using Microsoft.Practices.Composite.Presentation.Events;

namespace FirstPrismSample.ModuleCategory
{
    [Module(ModuleName = "ModuleCategory")]
    public class CategoryModule : IModule
    {      
        private readonly IUnityContainer m_Container;
        private readonly string moduleName = "ModuleCategory";
            
        public CategoryModule(IUnityContainer container)
        {
            m_Container = container;
        }   
      
        ~CategoryModule()
        {
            var eventAggregator = m_Container.Resolve<IEventAggregator>();
            var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>();       
            viewRequestedEvent.Unsubscribe(ViewRequestedEventHandler);
        }
     
        public void Initialize()
        {           
            var regionManager = m_Container.Resolve<IRegionManager>();
            regionManager.Regions["WorkspaceRegion"].Add(new CategoryView(), moduleName);
         
            var eventAggregator = m_Container.Resolve<IEventAggregator>();
            var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>();
            viewRequestedEvent.Subscribe(this.ViewRequestedEventHandler, true);
        }
       
        public void ViewRequestedEventHandler(string moduleName)
        {
            if (this.moduleName != moduleName) return;
          
            var moduleServices = m_Container.Resolve<IModuleServices>();
            moduleServices.ActivateView(moduleName);
        }      
    }
}
چند نکته :
*ModuleAttribute استفاده شده در بالای کلاس برای تعیین نام ماژول استفاده می‌شود. این Attribute دارای دو خاصیت دیگر هم است :
  1. OnDemand : برای تعیین اینکه ماژول باید به صورت OnDemand (بنا به درخواست) لود شود.
  2. StartupLoaded : برای تعیین اینکه ماژول به عنوان ماژول اول پروزه لود شود.(البته این گزینه Obsolute شده است)

*برای تعریف ماژول کلاس مورد نظر حتما باید اینترفیس IModule را پیاده سازی کند. این اینترفیس فقط شامل یک متد است به نام Initialize.

*در این پروژه چون View‌های برنامه صرفا جهت نمایش هستند در نتیجه نیاز به ایجاد ViewModel برای آن‌ها نیست. در پروژه‌های اجرایی حتما برای هر View باید ViewModel متناظر با آن تهیه شود.

توضیح درباره متد Initialize

در این متد ابتدا با استفاده از Container موجود RegionManager را به دست می‌آوریم. با استفاده از RegionManager می‌تونیم یک CompositeUI طراحی کنیم. در فایل Shell مشاهده کردید که یک صفحه به دو ناحیه تقسیم شد و به هر ناحیه هم یک نام اختصاص دادیم. دستور زیر به یک ناحیه اشاره خواهد داشت:

regionManager.Regions["WorkspaceRegion"]
در خط بعد با استفاده از EA یا Event Aggregator توانستیم CPE را بدست بیاوریم. متد Subscribe در کلاس CPE  یک ارجاع قوی به delegate مورد نظر ایجاد می‌کند(پارامتر دوم این متد که از نوع boolean است) که به این معنی است که این delegate هیچ گاه توسط GC جمع آوری نخواهد شد. در نتیجه، قبل از اینکه ماژول بسته شود باید به صورت دستی این کار را انجام دهیم که مخرب را برای همین ایجاد کردیم. اگر به کد‌های مخرب دقت کنید می‌بینید که با استفاده از EA توانستیم ViewRequestEventHandler را Unsubscribe کنیم به دلیل اینکه از ارجاع قوی با strong Reference در متد Subscribe استفاده شده است.
دستور moduleService.ActiveateView ماژول مورد نظر را در region مورد نظر هاست خواهد کرد.

#ماژول لیست کتاب ها:
ابتدا یک Class Library به نام ModuleBook بسازید  و همانند ماژول قبلی نیاز به یک Window و یک کلاس داریم:
BookWindow که کاملا مشابه به CategoryView است.
<UserControl x:Class="FirstPrismSample.ModuleBook.BookView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Background="LightGray" FontFamily="Tahoma" FlowDirection="RightToLeft">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBlock Text="لیست کتاب ها"/>
        <ListView Grid.Row="1" Margin="10" Name="lvBook">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="کد" Width="50"  />
                    <GridViewColumn Header="عنوان" Width="200" />
                    <GridViewColumn Header="نویسنده" Width="150" />
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</UserControl>

کلاس BookModule که پیاده سازی  و توضیحات آن کاملا مشابه به CategoryModule می‌باشد.
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.Presentation.Events;
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;
using FirstPrismSample.Common;
using FirstPrismSample.Common.Events;

namespace FirstPrismSample.ModuleBook
{
    [Module(ModuleName = "moduleBook")]
    public class BookModule : IModule
    {      
        private readonly IUnityContainer m_Container;
        private readonly string moduleName = "ModuleBook";     
    
        public BookModule(IUnityContainer container)
        {
            m_Container = container;          
        }     
       
        ~BookModule()
        {           
            var eventAggregator = m_Container.Resolve<IEventAggregator>();
            var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>();
          
            viewRequestedEvent.Unsubscribe(ViewRequestedEventHandler);
        }     
     
        public void Initialize()
        {           
            var regionManager = m_Container.Resolve<IRegionManager>();
            var view = new BookView();
            regionManager.Regions["WorkspaceRegion"].Add(view, moduleName);
            regionManager.Regions["WorkspaceRegion"].Deactivate(view);
      
            var eventAggregator = m_Container.Resolve<IEventAggregator>();
            var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>();
            viewRequestedEvent.Subscribe(this.ViewRequestedEventHandler, true);
        }     
      
        public void ViewRequestedEventHandler(string moduleName)
        {           
            if (this.moduleName != moduleName) return;
         
            var moduleServices = m_Container.Resolve<IModuleServices>();
            moduleServices.ActivateView(m_WorkspaceBName);
        }
    }
}
#ماژول Navigator
برای این ماژول هم ابتدا View مورد نظر را ایجاد می‌کنیم:
<UserControl x:Class="FirstPrismSample.ModuleNavigator.NavigatorView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
    <Grid>
        <StackPanel VerticalAlignment="Center">
            <TextBlock Text="انتخاب ماژول" Foreground="Green" HorizontalAlignment="Center"
                VerticalAlignment="Center" FontFamily="Tahoma" FontSize="24" FontWeight="Bold" />
        <Button Command="{Binding ShowModuleCategory}" Margin="5" Width="125">طبقه بندی کتاب ها</Button>
        <Button Command="{Binding ShowModuleBook}" Margin="5" Width="125">لیست کتاب ها</Button>
        </StackPanel>
        </Grid>
</UserControl>
حال قصد داریم برای این View یک ViewModel بسازیم. نام آن را INavigatorViewModel خواهیم گذاشت:
public interface INavigatorViewModel
    {    
        ICommand ShowModuleCategory { get; set; }       
        ICommand ShowModuleBook { get; set; }

        string ActiveWorkspace { get; set; }       

        IUnityContainer Container { get; set; }

        event PropertyChangedEventHandler PropertyChanged;
    }
 *در اینترفیس بالا دو Command داریم که هر کدام وظیفه لود یک ماژول را بر عهده دارند.
 *خاصیت ActiveWorkspace برای تعیین workspace فعال تعریف شده است.

حال به پیاده سازی مثال بالا می‌پردازیم:
public class NavigatorViewModel : ViewModelBase, INavigatorViewModel
    {        
        public NavigatorViewModel(IUnityContainer container)
        {
            this.Initialize(container);
        }   
       
        public ICommand ShowModuleCategory { get; set; }
      
        public ICommand ShowModuleBook { get; set; }      
              
        public string ActiveWorkspace { get; set; }       

        public IUnityContainer Container { get; set; }        
     
        private void Initialize(IUnityContainer container)
        {
            this.Container = container;
            this.ShowModuleCategory = new ShowModuleCategoryCommand(this);
            this.ShowModuleBook = new ShowModuleBookCommand(this);
            this.ActiveWorkspace = "ModuleCategory";
        }        
    }
تنها نکته مهم در کلاس بالا متد Initialize است که دو Command مورد نظر را پیاده سازی کرده است. ماژول پیش فرض هم ماژول طبقه بندی کتاب‌ها یا ModuleCategory در نظر گرفته شده است.  همان طور که می‌بینید پیاده سازی Command‌ها بالا توسط دو کلاس ShowModuleCategoryCommand و ShowModuleBookCommand انجام شده که در زیر کد‌های آن‌ها را می‌بینید.
#کد کلاس ShowModuleCategoryCommand  
public class ShowModuleCategoryCommand : ICommand
    {      
        private readonly NavigatorViewModel viewModel;
        private const string workspaceName = "ModuleCategory";         

        public ShowModuleCategoryCommand(NavigatorViewModel viewModel)
        {
            this.viewModel = viewModel;
        }          

        public bool CanExecute(object parameter)
        {
            return viewModel.ActiveWorkspace != workspaceName;
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
     
        public void Execute(object parameter)
        {
            CommandServices.ShowWorkspace(workspaceName, viewModel);
        }      
    }
#کد کلاس ShowModuleBookCommand  
public class ShowModuleBookCommand : ICommand
    {
        private readonly NavigatorViewModel viewModel;
        private readonly string workspaceName = "ModuleBook";

        public ShowModuleBookCommand( NavigatorViewModel viewModel )
        {
            this.viewModel = viewModel;
        }

        public bool CanExecute( object parameter )
        {
            return viewModel.ActiveWorkspace != workspaceName;
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute( object parameter )
        {
            CommandServices.ShowWorkspace( workspaceName , viewModel );
        }
    }
با توجه به این که فرض است با متد‌های Execute و CanExecute و CanExecuteChanged آشنایی دارید از توضیح این مطالب خودداری خواهم کرد. فقط کلاس CommandServices  در متد Execute دارای متدی به نام ShowWorkspace است که کد‌های زیر را شامل می‌شود:
public static void ShowWorkspace(string workspaceName, INavigatorViewModel viewModel)
  {           
            var eventAggregator = viewModel.Container.Resolve<IEventAggregator>();
            var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>();
            viewRequestedEvent.Publish(workspaceName);
        
            viewModel.ActiveWorkspace = workspaceName;
 }
در این متد با استفاده از CPE که در پروژه Common ایجاد کردیم ماژول مورد نظر را لود خواهیم کرد. و بعد از آن مقدار ActiveWorkspace جاری در ViewModel به نام ماژول تغییر پیدا می‌کند. متد Publish در CPE این کار را انجام خواهد دارد.

عدم وابستگی ماژول ها
همان طور که می‌بینید ماژول‌های پروژه به هم Reference داده نشده اند حتی هیچ Reference هم به پروژه اصلی یعنی جایی که فایل App.xaml قرار دارد، داده نشده است ولی در عین حال باید با هم در ارتباط باشند. برای حل این مسئله این ماژول‌ها باید در فولدر bin پروژه اصلی خود را کپی کنند. بهترین روش استفاده از Pre-Post Build Event خود VS.Net است. برای این کار از پنجره Project Properties وارد برگه Build Events شوید و از قسمت Post Build Event Command Line  استفاده کنید و کد زیر را در آن کپی نمایید:
xcopy "$(TargetDir)FirstPrismSample.ModuleBook.dll" "$(SolutionDir)FirstPrismSample\bin\$(ConfigurationName)\Modules\" /Y
قطعا باید به جای FirstPrismSample نام Solution خود و به جای ModuleBook نام ماژول را وارد نمایید.

مانند:


مراحل بالا برای هر ماژول باید تکرار شود(ModuleNavigation , ModuleBook , ModuleCategory). بعد از Rebuild  پروژه در فولدر bin پروژه اصلی یک فولدر به نام Module ایجاد می‌شود که اسمبلی هر ماژول در آن کپی خواهد شد.

ایجاد Bootstrapper
حال نوبت به Bootstrapper میرسد(در پست قبلی در باره مفهوم Bootstrapper شرح داده شد). در پروژه اصلی یعنی جایی که فایل App.xaml قرار دارد کلاس زیر را ایجاد کنید.
    public class Bootstrapper : UnityBootstrapper
    {     
        protected override void ConfigureContainer()
        {
            base.ConfigureContainer();
            Container.RegisterType<IModuleServices, ModuleServices>();
        }
   
        protected override DependencyObject CreateShell()
        {
            var shell = new Shell();
            shell.Show();
            return shell;
        }
   
        protected override IModuleCatalog GetModuleCatalog()
        {           
            var catalog = new DirectoryModuleCatalog();
            catalog.ModulePath = @".\Modules";
            return catalog;
        }
    }
متد ConfigureContainer برای تزریق وابستگی به وسیله UnityContainer استفاده می‌شود. در این متد باید تمامی Registration‌های مورد نیاز برای DI را انجام دهید. نکته مهم این است که عملیات وهله سازی و Initialization برای  Container  در متد base کلاس UnityBootstrapper انجام خواهد شد پس همیشه باید متد base این کلاس در ابتدای این متد فراخوانی شود در غیر این صورت با خطا متوقف خواهید شد.
متد CreateShell برای ایجاد و وهله سازی از Shell پروژه استفاده می‌شود. در این جا یک وهله از Shell Window برگشت داده می‌شود.
متد GetModuleCatalog برای تعیین مسیر ماژول‌ها در پروژه کاربرد دارد. در این متد با استفاده از خاصیت ModulePath کلاس DirectoryModuleCatalog تعیین کرده ایم که ماژول‌های پروژه در فولدر Modules موجود در bin اصلی پروژه قرار دارد. اگر به دستورات کپی در Post Build Event قسمت قبل توجه کنید می‌بینید که دستور ساخت فولدر وجود دارد.
"$(SolutionDir)FirstPrismSample\bin\$(ConfigurationName)\Modules\" /Y
*نکته: اگر استفاده از این روش برای شناسایی ماژول‌ها توسط Bootstrapper را چندان جالب نمی‌دانید می‌تونید از MEF استفاده کنید که اسمبلی ماژول‌های پروژه را به راحتی شناسایی می‌کند و در اختیار Bootsrtapper قرار می‌دهد(از آن جا در مستندات مربوط به Prism، بیشتر به استفاده از MEF تاکید شده است من هم در پست‌های بعدی، مثال‌ها را با MEF پیاده سازی خواهم کرد)

در پایان باید فایل App.xaml را تغییر دهید به گونه ای که متد Run در کلاس Bootstapper ابتدا اجرا شود.
public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            var bootstrapper = new Bootstrapper();
            bootstrapper.Run();
        }
    }


اجرای پروژه:
بعد از اجرا، با انتخاب ماژول مورد نظر اطلاعات ماژول در Workspace Content Control لود خواهد شد.

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