VSTest is a very extensible unit test execution framework. The base engine, discovers tests and runs them. It can parallelize across cores, provides process isolation and can integrate with Visual Studio. It has extensibility for different test frameworks, code coverage, test impact analysis, data collection, test result reporting and much more.
اشتراکها
دلایل ارتقاء به SQL Server 2017
Following is a chart that shows licensing cost comparison between standard and Enterprise Edition of SQL Server 2012, 2016 and 2017.
Version | Edition | License Cost | 2 Quad core Processors | 4 Quad core Processors |
---|---|---|---|---|
Per Core | 8 Cores | 16 Cores | ||
SQL Server 2012 | Standard | $1,793 | $14,344 | $28,688 |
SQL Server 2012 | Enterprise | $6,874 | $54,992 | $109,984 |
SQL Server 2016\ 2017 | Standard | $1,858 | $14,864 | $29,728 |
SQL Server 2016 \2017 | Enterprise | $7,128 | $57,024 | $114,048 |
در قسمت قبل، پیشنیازهای کار با AngularJS 2.0 مرور و دریافت شدند. اگر مطالب آنرا پیگیری کرده باشید، هم اکنون باید در پوشهی node_modules واقع در ریشهی پروژه، تمام اسکریپتهای لازم جهت شروع به کار با AngularJS 2.0 موجود باشند.
تعاریف مداخل فایل index.html یک سایت AngularJS 2.0
پروژهای که در اینجا در حال استفاده است یک برنامهی ASP.NET MVC 5.x است؛ اما الزامی هم به استفادهی از آن وجود ندارد. یا یک فایل index.html را به ریشهی پروژه اضافه کنید و یا فایل Views\Shared\_Layout.cshtml را به نحو ذیل تغییر دهید:
در اینجا ابتدا تعاریف مداخل بوت استرپ و cssهای سفارشی برنامه را مشاهده میکنید.
سپس کتابخانههای جاوا اسکریپتی مورد نیاز جهت کار با AngularJS 2.0 به ترتیبی که ذکر شده، باید تعریف شوند.
ذکر /~ در ابتدای آدرسها، مختص به ASP.NET MVC است. اگر از آن استفاده نمیکنید، نیازی به ذکر آن هم نیست.
در ادامه تعاریف System.JS ذکر شدهاست. System.JS کار بارگذاری ماژولهای برنامه را به عهده دارد. به این ترتیب دیگر نیازی نیست تا به ازای هر قسمت جدید برنامه، مدخلی را در اینجا اضافه کرد و کار بارگذاری آنها خودکار خواهد بود. فرمت register ایی که در اینجا ذکر شدهاست، تا ماژولهای استاندارد با فرمت ES 6 را نیز پشتیبانی میکند. همچنین با ذکر و تنظیم پسوند پیش فرض به js، دیگر نیازی نخواهد بود تا در حین تعریف importها در قسمتهای مختلف برنامه، پسوند فایلها را به صورت صریح ذکر کرد. مبحث improtها مرتبط است به مفاهیم ماژولها در ES 6 و همچنین TypeScript.
سطر System.import کار بارگذاری اولین ماژول برنامه را از پوشهی app قرار گرفته در ریشهی سایت انجام میدهد. این ماژول main نام دارد.
نوشتن اولین کامپوننت AngularJS 2.0
برنامههای AngularJS 2.0 متشکل هستند از تعدادی کامپوننت و سرویس:
و هر کامپوننت تشکیل شدهاست از:
- یک قالب یا Template: با استفاده از HTML تعریف میشود و کار تشکیل View و نحوهی رندر کامپوننت را مشخص میکند. در این Viewها با استفاده از امکانات binding و directives موجود در AngularJS 2.0 کار دسترسی به دادهها صورت میگیرد.
- یک کلاس: کار این کلاس که توسط TypeScript تهیه میشود، فراهم آوردن کدهای مرتبط با قالب است. برای مثال این کلاس حاوی تعدادی خاصیت خواهد بود که از اطلاعات آنها در View مرتبط استفاده میشود. همچنین این کلاس میتواند حاوی متدهای مورد نیاز در View نیز باشد؛ برای مثال متدی که کار نمایش یا مخفی سازی یک تصویر را با کلیک بر روی دکمهای انجام میدهد.
- متادیتا: متادیتا (یا decorator در اینجا) به AngularJS 2.0 اعلام میکند که این کلاس تعریف شده، صرفا یک کلاس ساده نیست و باید به آن به صورت یک کامپوننت نگاه شود.
در ذیل، کدهای یک کامپوننت نمونهی AngularJS 2.0 را مشاهده میکنید:
در انتهای کدها، یک کلاس را مشاهده میکنید که کار تعریف خواص و متدهای مورد نیاز توسط View را انجام میدهد.
بلافاصله در بالای این کلاس، متد decorator ایی را به نام Component مشاهده میکنید. این متادیتا است که به AngularJS 2.0 اعلام میکند، کلاس AppComponent تعریف شده، یک کامپوننت است و نه تنها یک کلاس ساده.
در متد Component تعریف شده، قالب یا template نحوهی رندر این کامپوننت را مشاهده میکنید.
در ابتدای این ماژول نیز کار import تعاریف مرتبط با متد ویژهی Component، از هستهی AngularJS 2.0 انجام شدهاست تا کامپایلر TypeScript بتواند این فایل ts را کامپایل کند.
مروری بر نحوهی تعریف class در TypeScript
مرور جامع کلاسها در TypeScript را در مطلب «مبانی TypeScript؛ کلاسها» میتوانید مطالعه کنید. در اینجا جهت یادآوری، خلاصهای از آنرا که نیاز داریم، بررسی خواهیم کرد:
- جهت تعریف یک کلاس، ابتدا واژهی کلیدی class به همراه نام کلاس ذکر میشوند.
- در AngularJS 2.0 مرسوم است که نام کلاس را به صورت نام ویژگی مدنظر به همراه پسوند Component ذکر کنیم؛ مانند AppComponent مثال فوق. این نام pascal case است و با حروف بزرگ شروع میشود.
- همچنین مرسوم است در برنامههای AngularJS 2.0، کامپوننت ریشهی سایت نیز AppComponent نامیده شود.
- در مثال فوق، واژهی کلیدی export را نیز پیش از واژهی کلیدی class مشاهده میکنید. به این ترتیب این کلاس خارج از ماژولی که در آن تعریف میشود، قابل دسترسی خواهد بود. اکنون این کلاس و فایل، تبدیل به ماژولی خواهند شد که توسط module loader معرفی شدهی در ابتدای بحث یا همان System.JS به صورت خودکار بارگذاری میشود و دیگر نیازی به تعریف مدخل script متناظر با آن در فایل index.html نخواهد بود.
- در بدنهی کلاس، کار تعریف متدها و خواص مورد نیاز View صورت میگیرند. برای نمونه در اینجا تنها یک خاصیت «عنوان صفحه» تعریف شدهاست. در جاوا اسکریپت مرسوم است که نام خواص را camel case شروع شده با حروف کوچک تعریف کنیم. سپس نوع این خاصیت به صورت رشتهای تعریف شدهاست و در آخر مقدار پیش فرض این خاصیت ذکر گردیدهاست.
البته باید دقت داشت که الزامی به ذکر نوع خاصیت، در TypeScript وجود ندارد. همینقدر که مقدار پیش فرض این خاصیت رشتهای است، بر اساس ویژگی به نام Type inference در TypeScript، نوع این خاصیت نیز رشتهای درنظر گرفته خواهد شد و دیگر نمیتوان برای مثال یک عدد را به آن انتساب داد.
- سطح دسترسی خواص تعریف شدهی در یک کلاس TypeScript به صورت پیش فرض public است. بنابراین در اینجا نیازی به ذکر صریح آن نبودهاست.
مروری بر متادیتا یا decorator یک کلاس در AngularJS 2.0
خوب، تا اینجا کلاس AppComponent تعریف و همچنین export شد تا توسط system.js به صورت خودکار بارگذاری شود. اما این کلاس به خودی خود صرفا یک کلاس TypeScript ایی است و توسط AngularJS شناسایی نمیشود. برای معرفی این کلاس به صورت یک کامپوننت، از یک تزئین کننده یا decorator ویژه به نام Component استفاده میشود که بلافاصله در بالای تعریف کلاس قرار میگیرد؛ چیزی شبیه به data annotations یا attributes در زبان #C.
یک decorator متدی است که اطلاعاتی اضافی را به یک کلاس، اعضاء و متدهای آن و یا حتی آرگومانهای آن متدها، الصاق میکند. این ویژگی قرار است به صورت استاندارد در ES 2016 یا نگارش بعدی جاوا اسکریپت حضور داشته باشد و در حال حاضر توسط TypeScript پشتیبانی شده و در نهایت به کدهای ES 5 قابل اجرای در تمام مرورگرها ترجمه میشود.
یک decorator همیشه با @ شروع میشود و AngularJS 2.0 به همراه تعدادی decorator توکار است؛ مانند Component. از آنجائیکه decorator یک متد است، همیشه به همراه یک جفت پرانتز () ذکر میشود و در انتهای آن نیازی به ذکر سمیکالن نیست. در اینجا تزئین کنندهی Component یک شیء را میپذیر که به همراه تعدادی خاصیت است. به همین جهت پارامتر آن به صورت {} ذکر شدهاست.
خاصیت selector یک کامپوننت مشخص میکند که نام directive متناظر با این کامپوننت چیست:
از این نام، به صورت یک المان جدید و سفارشی HTML جهت تعریف این کامپوننت استفاده خواهیم کرد. برای مثال اگر به کدهای ابتدای بحث دقت کنید، نام pm-app به صورت ذیل و به شکل یک تگ جدید HTML استفاده شدهاست:
همچنین یک کامپوننت همواره به همراه یک قالب است که نحوهی رندر آنرا مشخص میکند:
در اینجا از back tick مربوط به ES 6.0 که توسط TypeScript نیز پشتیبانی میشود، جهت تعریف یک رشتهی چندسطری جاوا اسکریپتی، استفاده شدهاست.
همچنین {{}} به معنای تعریف data binding است. به این ترتیب مقداری که قرار است به صورت تگ h1 رندر شود، از خاصیت pageTitle کلاس مزین شدهی توسط این ویژگی یا decorator تامین خواهد شد؛ یعنی مقدار پیش فرض خاصیت pageTitle در کلاس AppComponent.
import اطلاعات مورد نیاز جهت کامپایل یک فایل TypeScript
تا اینجا مفاهیم موجود در کلاس AppComponent را به همراه متادیتای آن بررسی کردیم. اما این متادیتای جدید کامپوننت، به صورت پیش فرض ناشناختهاست:
همانطور که مشاهده میکنید، در اینجا ذیل کامپوننت، خط قرمزی جهت یادآوری عدم تعریف آن، کشیده شدهاست. در TypeScript و یا ES 6، پیش از استفاده از یک کلاس یا متد خارجی، نیاز است به module loader اعلام کنیم تا آنرا باید از کجا تامین کند. اینکار توسط عبارت import انجام میشود که شبیه به عبارت using در زبان سیشارپ است. عبارت import جزئی از استاندارد ES 6 است و همچنین در TypeScript نیز پشتیبانی میشود. به این ترتیب امکان دسترسی به ویژگیهای export شدهی از سایر ماژولها را در ماژول فعلی (فایل فعلی در حال کار) خواهیم یافت. نمونهی آنرا با ذکر واژهی کلیدی export پیش از کلاس AppComponent پیشتر ملاحظه کردید.
این ماژولهای خارجی میتوانند سایر ماژولها و فایلهای ts نوشته شدهی توسط خودمان و یا حتی اجزای AngularJS 2.0 باشند. طراحی AngularJS 2.0 نیز ماژولار است و از ماژولهایی مانند angular2/core، angular2/animation، angular2/http و angular2/router تشکیل شدهاست.
برای نمونه متادیتای کامپوننت، در ماژول angular2/core قرار دارد. به همین جهت نیاز است در ابتدای ماژول فعلی آنرا import کرد:
کار با ذکر واژهی کلیدی import شروع میشود. سپس جزئی را که نیاز داریم داخل {} ذکر کرده و در آخر مسیر یافتن آنرا مشخص میکنیم.
ساخت کامپوننت ریشهی یک برنامهی AngularJS 2.0
در ابتدای بحث که تعاریف مداخل مورد نیاز جهت اجرای یک برنامهی AngularJS 2.0 ذکر شدند، عنوان شد که system.js به دنبال ماژول آغازین app/main میگردد.
بنابراین در ریشهی پروژه، پوشهی جدیدی را به نام app ایجاد کنید.
سپس یک فایل TypeScript جدید را به نام app.component.ts به این پوشه اضافه کنید. قالب این نوع فایلها در add new item و با جستجو typescript در دسترس است و یا حتی یک فایل متنی ساده را هم با پسوند ts ایجاد کنید، کافی است.
نامگذاری این فایلها هم مرسوم است به صورت ذکر نام ویژگی به همراه یک دات و سپس ذکر کامپوننت صورت گیرد. در اینجا چون قصد داریم کامپوننت ریشهی برنامه را ایجاد کنیم، نام ویژگی آن app خواهد بود و نام کامل فایل به این ترتیب app.component.ts میشود.
سپس محتوای این فایل را به دقیقا معادل کدهای قسمت «نوشتن اولین کامپوننت AngularJS 2.0» ابتدای بحث تغییر دهید (همان import، متادیتا و کلاس AppComponent).
تا اینجا کامپوننت ریشهی برنامه ایجاد شد. اما چگونه باید از آن استفاده کنیم و چگونه AngularJS 2.0 آنرا شناسایی میکند؟ به این فرآیند آغازین شناسایی و بارگذاری ماژولها، اصطلاحا bootstrapping میگویند. تنها صفحهی واقعی موجود در یک برنامهی تک صفحهای وب، همان فایل index.html است و سایر صفحات و محتوای آنها به محتوای این صفحهی آغازین اضافه یا کم خواهند شد.
در اینجا برای نمایش اولین کامپوننت تهیه شده، نام تگ selector آن که توسط متادیتای کلاس AppComponent تعریف شد، در body فایل index.html به نحو فوق به صورت یک تگ سفارشی جدید اضافه میشود. به آن directive نیز میگویند.
خوب، اکنون module loader یا system.js چگونه این pm-app یا کامپوننت ریشهی برنامه را شناسایی میکند؟
این شناسایی توسط سطر System.import تعریف شدهی در فایل index.html انجام میشود. در اینجا system.js، در پوشهی app واقع در ریشهی سایت، به دنبال ماژول راه اندازی به نام main میگردد. یعنی نیاز است فایل TypeScript جدیدی را به نام main.ts به ریشهی پوشهی app اضافه کنیم. محتوای این فایل ویژهی بوت استرپ AngularJS 2.0 به صورت ذیل است:
این فایل ویژه را نیز مانند کلاس AppComponent که پیشتر بررسی کردیم، نیاز است از انتها به ابتدا بررسی کرد.
در انتهای این فایل متد bootstrap مربوط به AngularJS 2.0 را مشاهده میکنید. کار آن بارگذاری اولین ماژول و کامپوننت برنامه یا همان AppComponent است.
در ادامه نیاز است AppComponent را به این ماژول معرفی کرد؛ در غیراینصورت کامپایل نخواهد شد. برای این منظور سطر import این کلاس را از فایل app.component، مشاهده میکنید. در اینجا نیازی به ذکر پسوند ts. فایل app.component نیست.
سپس نیاز است محل تعریف متد بوت استرپ را نیز مشخص کنیم. این متد در ماژول angular2/platform/browser قرار دارد که به عنوان اولین import این فایل ذکر شدهاست.
سطر اول، مربوط است به تعریف فایلهای d.ts. مربوط به TypeScript جهت شناسایی نوعهای مرتبط با AngularJS 2.0. اگر اینکار صورت نگیرد، خطاهای ذیل را در حین کامپایل فایلهای TypeScript دریافت خواهید کرد:
تهیه فایل main.ts تنها یکبار صورت میگیرد و دیگر با آن کاری نخواهیم داشت.
تا اینجا پوشهی app واقع در ریشهی سایت، یک چنین شکلی را پیدا میکند:
و اکنون اگر برنامه را اجرا کنیم (فشردن دکمهی F5)، خروجی آن در مرورگر به صورت ذیل خواهد بود:
همچنین در اینجا میتوان HTML نهایی تزریق شدهی به صفحه را بهتر مشاهده کرد:
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: MVC5Angular2.part2.zip
برای اجرای آن، ابتدا به فایل project.json مراجعه کرده و یکبار آنرا ذخیره کنید تا وابستگیهای اسکریپتی پروژه از اینترنت دریافت شوند (این موارد در قسمت قبل مرور شدند). سپس یکبار هم پروژه را Build کنید تا تمام فایلهای ts آن کامپایل شوند و در آخر، اجرای نهایی پروژه.
خلاصهی بحث
یک برنامهی AngularJS 2.0 متشکل است از تعدادی کامپوننت. بنابراین کلاسی را ایجاد خواهیم کرد تا کدهای پشتیبانی کنندهی View این کامپوننت را تولید کند. سپس این کلاس را با متادیتایی مزین کرده و توسط آن تگ سفارشی ویژهی این کامپوننت و تگهای HTML تشکیل دهندهی این کامپوننت را به AngularJS 2.0 معرفی میکنیم. در اینجا در صورت نیاز وابستگیهای تعریف این متادیتا را توسط واژهی کلیدی import دریافت میکنیم. نام این کلاس بهتر است Pascal case بوده و با حروف بزرگ شروع شود و همچنین به صورت نام ویژگی ختم شدهی به کلمهی Component باشد. در اینجا حتما نیاز است این کلاس export شود تا توسط module loader قابل استفاده و بارگذاری گردد. اگر View این کامپوننت نیاز به دریافت اطلاعاتی دارد، این اطلاعات به صورت خواصی در کلاس کامپوننت تعریف میشوند. این خواص تعریف شدهی با سطح دسترسی عمومی، مرسوم است به صورت camel case تعریف شوند و حروف اول آنها کوچک باشد.
تعاریف مداخل فایل index.html یک سایت AngularJS 2.0
پروژهای که در اینجا در حال استفاده است یک برنامهی ASP.NET MVC 5.x است؛ اما الزامی هم به استفادهی از آن وجود ندارد. یا یک فایل index.html را به ریشهی پروژه اضافه کنید و یا فایل Views\Shared\_Layout.cshtml را به نحو ذیل تغییر دهید:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>@ViewBag.Title - My ASP.NET Application</title> <link href="~/node_modules/bootstrap/dist/css/bootstrap.css" rel="stylesheet" /> <link href="~/app/app.component.css" rel="stylesheet"/> <link href="~/Content/Site.css" rel="stylesheet" type="text/css" /> <!-- 1. Load libraries --> <!-- IE required polyfills, in this exact order --> <script src="~/node_modules/angular2/es6/dev/src/testing/shims_for_IE.js"></script> <script src="~/node_modules/es6-shim/es6-shim.min.js"></script> <script src="~/node_modules/systemjs/dist/system-polyfills.js"></script> <script src="~/node_modules/angular2/bundles/angular2-polyfills.js"></script> <script src="~/node_modules/systemjs/dist/system.src.js"></script> <script src="~/node_modules/rxjs/bundles/Rx.js"></script> <script src="~/node_modules/angular2/bundles/angular2.dev.js"></script> <!-- Required for http --> <script src="~/node_modules/angular2/bundles/http.dev.js"></script> <!-- Required for routing --> <script src="~/node_modules/angular2/bundles/router.dev.js"></script> <!-- 2. Configure SystemJS --> <script> System.config({ packages: { app: { format: 'register', defaultExtension: 'js' } } }); System.import('app/main') .then(null, console.error.bind(console)); </script> </head> <body> <div> @RenderBody() <pm-app>Loading App...</pm-app> </div> @RenderSection("Scripts", required: false) </body> </html>
سپس کتابخانههای جاوا اسکریپتی مورد نیاز جهت کار با AngularJS 2.0 به ترتیبی که ذکر شده، باید تعریف شوند.
ذکر /~ در ابتدای آدرسها، مختص به ASP.NET MVC است. اگر از آن استفاده نمیکنید، نیازی به ذکر آن هم نیست.
در ادامه تعاریف System.JS ذکر شدهاست. System.JS کار بارگذاری ماژولهای برنامه را به عهده دارد. به این ترتیب دیگر نیازی نیست تا به ازای هر قسمت جدید برنامه، مدخلی را در اینجا اضافه کرد و کار بارگذاری آنها خودکار خواهد بود. فرمت register ایی که در اینجا ذکر شدهاست، تا ماژولهای استاندارد با فرمت ES 6 را نیز پشتیبانی میکند. همچنین با ذکر و تنظیم پسوند پیش فرض به js، دیگر نیازی نخواهد بود تا در حین تعریف importها در قسمتهای مختلف برنامه، پسوند فایلها را به صورت صریح ذکر کرد. مبحث improtها مرتبط است به مفاهیم ماژولها در ES 6 و همچنین TypeScript.
سطر System.import کار بارگذاری اولین ماژول برنامه را از پوشهی app قرار گرفته در ریشهی سایت انجام میدهد. این ماژول main نام دارد.
نوشتن اولین کامپوننت AngularJS 2.0
برنامههای AngularJS 2.0 متشکل هستند از تعدادی کامپوننت و سرویس:
و هر کامپوننت تشکیل شدهاست از:
- یک قالب یا Template: با استفاده از HTML تعریف میشود و کار تشکیل View و نحوهی رندر کامپوننت را مشخص میکند. در این Viewها با استفاده از امکانات binding و directives موجود در AngularJS 2.0 کار دسترسی به دادهها صورت میگیرد.
- یک کلاس: کار این کلاس که توسط TypeScript تهیه میشود، فراهم آوردن کدهای مرتبط با قالب است. برای مثال این کلاس حاوی تعدادی خاصیت خواهد بود که از اطلاعات آنها در View مرتبط استفاده میشود. همچنین این کلاس میتواند حاوی متدهای مورد نیاز در View نیز باشد؛ برای مثال متدی که کار نمایش یا مخفی سازی یک تصویر را با کلیک بر روی دکمهای انجام میدهد.
- متادیتا: متادیتا (یا decorator در اینجا) به AngularJS 2.0 اعلام میکند که این کلاس تعریف شده، صرفا یک کلاس ساده نیست و باید به آن به صورت یک کامپوننت نگاه شود.
در ذیل، کدهای یک کامپوننت نمونهی AngularJS 2.0 را مشاهده میکنید:
import { Component } from 'angular2/core'; @Component({ selector: 'pm-app', template:` <div><h1>{{pageTitle}}</h1> <div>My First Component</div> </div> ` }) export class AppComponent { pageTitle: string = "DNT AngularJS 2.0 APP"; }
بلافاصله در بالای این کلاس، متد decorator ایی را به نام Component مشاهده میکنید. این متادیتا است که به AngularJS 2.0 اعلام میکند، کلاس AppComponent تعریف شده، یک کامپوننت است و نه تنها یک کلاس ساده.
در متد Component تعریف شده، قالب یا template نحوهی رندر این کامپوننت را مشاهده میکنید.
در ابتدای این ماژول نیز کار import تعاریف مرتبط با متد ویژهی Component، از هستهی AngularJS 2.0 انجام شدهاست تا کامپایلر TypeScript بتواند این فایل ts را کامپایل کند.
مروری بر نحوهی تعریف class در TypeScript
مرور جامع کلاسها در TypeScript را در مطلب «مبانی TypeScript؛ کلاسها» میتوانید مطالعه کنید. در اینجا جهت یادآوری، خلاصهای از آنرا که نیاز داریم، بررسی خواهیم کرد:
- جهت تعریف یک کلاس، ابتدا واژهی کلیدی class به همراه نام کلاس ذکر میشوند.
- در AngularJS 2.0 مرسوم است که نام کلاس را به صورت نام ویژگی مدنظر به همراه پسوند Component ذکر کنیم؛ مانند AppComponent مثال فوق. این نام pascal case است و با حروف بزرگ شروع میشود.
- همچنین مرسوم است در برنامههای AngularJS 2.0، کامپوننت ریشهی سایت نیز AppComponent نامیده شود.
- در مثال فوق، واژهی کلیدی export را نیز پیش از واژهی کلیدی class مشاهده میکنید. به این ترتیب این کلاس خارج از ماژولی که در آن تعریف میشود، قابل دسترسی خواهد بود. اکنون این کلاس و فایل، تبدیل به ماژولی خواهند شد که توسط module loader معرفی شدهی در ابتدای بحث یا همان System.JS به صورت خودکار بارگذاری میشود و دیگر نیازی به تعریف مدخل script متناظر با آن در فایل index.html نخواهد بود.
- در بدنهی کلاس، کار تعریف متدها و خواص مورد نیاز View صورت میگیرند. برای نمونه در اینجا تنها یک خاصیت «عنوان صفحه» تعریف شدهاست. در جاوا اسکریپت مرسوم است که نام خواص را camel case شروع شده با حروف کوچک تعریف کنیم. سپس نوع این خاصیت به صورت رشتهای تعریف شدهاست و در آخر مقدار پیش فرض این خاصیت ذکر گردیدهاست.
البته باید دقت داشت که الزامی به ذکر نوع خاصیت، در TypeScript وجود ندارد. همینقدر که مقدار پیش فرض این خاصیت رشتهای است، بر اساس ویژگی به نام Type inference در TypeScript، نوع این خاصیت نیز رشتهای درنظر گرفته خواهد شد و دیگر نمیتوان برای مثال یک عدد را به آن انتساب داد.
- سطح دسترسی خواص تعریف شدهی در یک کلاس TypeScript به صورت پیش فرض public است. بنابراین در اینجا نیازی به ذکر صریح آن نبودهاست.
مروری بر متادیتا یا decorator یک کلاس در AngularJS 2.0
خوب، تا اینجا کلاس AppComponent تعریف و همچنین export شد تا توسط system.js به صورت خودکار بارگذاری شود. اما این کلاس به خودی خود صرفا یک کلاس TypeScript ایی است و توسط AngularJS شناسایی نمیشود. برای معرفی این کلاس به صورت یک کامپوننت، از یک تزئین کننده یا decorator ویژه به نام Component استفاده میشود که بلافاصله در بالای تعریف کلاس قرار میگیرد؛ چیزی شبیه به data annotations یا attributes در زبان #C.
یک decorator متدی است که اطلاعاتی اضافی را به یک کلاس، اعضاء و متدهای آن و یا حتی آرگومانهای آن متدها، الصاق میکند. این ویژگی قرار است به صورت استاندارد در ES 2016 یا نگارش بعدی جاوا اسکریپت حضور داشته باشد و در حال حاضر توسط TypeScript پشتیبانی شده و در نهایت به کدهای ES 5 قابل اجرای در تمام مرورگرها ترجمه میشود.
یک decorator همیشه با @ شروع میشود و AngularJS 2.0 به همراه تعدادی decorator توکار است؛ مانند Component. از آنجائیکه decorator یک متد است، همیشه به همراه یک جفت پرانتز () ذکر میشود و در انتهای آن نیازی به ذکر سمیکالن نیست. در اینجا تزئین کنندهی Component یک شیء را میپذیر که به همراه تعدادی خاصیت است. به همین جهت پارامتر آن به صورت {} ذکر شدهاست.
خاصیت selector یک کامپوننت مشخص میکند که نام directive متناظر با این کامپوننت چیست:
selector: 'pm-app',
<div> @RenderBody() <pm-app>Loading App...</pm-app> </div>
template:` <div><h1>{{pageTitle}}</h1> <div>My First Component</div> </div> `
همچنین {{}} به معنای تعریف data binding است. به این ترتیب مقداری که قرار است به صورت تگ h1 رندر شود، از خاصیت pageTitle کلاس مزین شدهی توسط این ویژگی یا decorator تامین خواهد شد؛ یعنی مقدار پیش فرض خاصیت pageTitle در کلاس AppComponent.
import اطلاعات مورد نیاز جهت کامپایل یک فایل TypeScript
تا اینجا مفاهیم موجود در کلاس AppComponent را به همراه متادیتای آن بررسی کردیم. اما این متادیتای جدید کامپوننت، به صورت پیش فرض ناشناختهاست:
همانطور که مشاهده میکنید، در اینجا ذیل کامپوننت، خط قرمزی جهت یادآوری عدم تعریف آن، کشیده شدهاست. در TypeScript و یا ES 6، پیش از استفاده از یک کلاس یا متد خارجی، نیاز است به module loader اعلام کنیم تا آنرا باید از کجا تامین کند. اینکار توسط عبارت import انجام میشود که شبیه به عبارت using در زبان سیشارپ است. عبارت import جزئی از استاندارد ES 6 است و همچنین در TypeScript نیز پشتیبانی میشود. به این ترتیب امکان دسترسی به ویژگیهای export شدهی از سایر ماژولها را در ماژول فعلی (فایل فعلی در حال کار) خواهیم یافت. نمونهی آنرا با ذکر واژهی کلیدی export پیش از کلاس AppComponent پیشتر ملاحظه کردید.
این ماژولهای خارجی میتوانند سایر ماژولها و فایلهای ts نوشته شدهی توسط خودمان و یا حتی اجزای AngularJS 2.0 باشند. طراحی AngularJS 2.0 نیز ماژولار است و از ماژولهایی مانند angular2/core، angular2/animation، angular2/http و angular2/router تشکیل شدهاست.
برای نمونه متادیتای کامپوننت، در ماژول angular2/core قرار دارد. به همین جهت نیاز است در ابتدای ماژول فعلی آنرا import کرد:
import { Component } from 'angular2/core';
ساخت کامپوننت ریشهی یک برنامهی AngularJS 2.0
در ابتدای بحث که تعاریف مداخل مورد نیاز جهت اجرای یک برنامهی AngularJS 2.0 ذکر شدند، عنوان شد که system.js به دنبال ماژول آغازین app/main میگردد.
بنابراین در ریشهی پروژه، پوشهی جدیدی را به نام app ایجاد کنید.
سپس یک فایل TypeScript جدید را به نام app.component.ts به این پوشه اضافه کنید. قالب این نوع فایلها در add new item و با جستجو typescript در دسترس است و یا حتی یک فایل متنی ساده را هم با پسوند ts ایجاد کنید، کافی است.
نامگذاری این فایلها هم مرسوم است به صورت ذکر نام ویژگی به همراه یک دات و سپس ذکر کامپوننت صورت گیرد. در اینجا چون قصد داریم کامپوننت ریشهی برنامه را ایجاد کنیم، نام ویژگی آن app خواهد بود و نام کامل فایل به این ترتیب app.component.ts میشود.
سپس محتوای این فایل را به دقیقا معادل کدهای قسمت «نوشتن اولین کامپوننت AngularJS 2.0» ابتدای بحث تغییر دهید (همان import، متادیتا و کلاس AppComponent).
تا اینجا کامپوننت ریشهی برنامه ایجاد شد. اما چگونه باید از آن استفاده کنیم و چگونه AngularJS 2.0 آنرا شناسایی میکند؟ به این فرآیند آغازین شناسایی و بارگذاری ماژولها، اصطلاحا bootstrapping میگویند. تنها صفحهی واقعی موجود در یک برنامهی تک صفحهای وب، همان فایل index.html است و سایر صفحات و محتوای آنها به محتوای این صفحهی آغازین اضافه یا کم خواهند شد.
<div> @RenderBody() <pm-app>Loading App...</pm-app> </div>
خوب، اکنون module loader یا system.js چگونه این pm-app یا کامپوننت ریشهی برنامه را شناسایی میکند؟
System.import('app/main')
/// <reference path="../node_modules/angular2/typings/browser.d.ts" /> import { bootstrap } from "angular2/platform/browser"; // Our main component import { AppComponent } from "./app.component"; bootstrap(AppComponent);
در انتهای این فایل متد bootstrap مربوط به AngularJS 2.0 را مشاهده میکنید. کار آن بارگذاری اولین ماژول و کامپوننت برنامه یا همان AppComponent است.
در ادامه نیاز است AppComponent را به این ماژول معرفی کرد؛ در غیراینصورت کامپایل نخواهد شد. برای این منظور سطر import این کلاس را از فایل app.component، مشاهده میکنید. در اینجا نیازی به ذکر پسوند ts. فایل app.component نیست.
سپس نیاز است محل تعریف متد بوت استرپ را نیز مشخص کنیم. این متد در ماژول angular2/platform/browser قرار دارد که به عنوان اولین import این فایل ذکر شدهاست.
سطر اول، مربوط است به تعریف فایلهای d.ts. مربوط به TypeScript جهت شناسایی نوعهای مرتبط با AngularJS 2.0. اگر اینکار صورت نگیرد، خطاهای ذیل را در حین کامپایل فایلهای TypeScript دریافت خواهید کرد:
node_modules\angular2\src\core\application_ref.d.ts(171,81): error TS2304: Build: Cannot find name 'Promise'. node_modules\angular2\src\core\change_detection\differs\default_keyvalue_differ.d.ts(23,15): error TS2304: Build: Cannot find name 'Map'.
تا اینجا پوشهی app واقع در ریشهی سایت، یک چنین شکلی را پیدا میکند:
و اکنون اگر برنامه را اجرا کنیم (فشردن دکمهی F5)، خروجی آن در مرورگر به صورت ذیل خواهد بود:
با توجه به اینکه در حال کار با یک برنامهی جاوا اسکریپتی هستیم، باز نگه داشتن developer tools مرورگر، جهت مشاهدهی خطاهای احتمالی ضروری است.
در اینجا اگر خطایی وجود داشته باشد، یا اطلاعات اضافی مدنظر باشد، در console لاگ خواهند شد. برای مثال در اینجا عنوان شدهاست که برنامه در حالت توسعه در حال اجرا است. بهتر است برای ارائهی نهایی، متد enableProdMode را در فایل index.html فراخوانی کنید.
همچنین در اینجا میتوان HTML نهایی تزریق شدهی به صفحه را بهتر مشاهده کرد:
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: MVC5Angular2.part2.zip
برای اجرای آن، ابتدا به فایل project.json مراجعه کرده و یکبار آنرا ذخیره کنید تا وابستگیهای اسکریپتی پروژه از اینترنت دریافت شوند (این موارد در قسمت قبل مرور شدند). سپس یکبار هم پروژه را Build کنید تا تمام فایلهای ts آن کامپایل شوند و در آخر، اجرای نهایی پروژه.
خلاصهی بحث
یک برنامهی AngularJS 2.0 متشکل است از تعدادی کامپوننت. بنابراین کلاسی را ایجاد خواهیم کرد تا کدهای پشتیبانی کنندهی View این کامپوننت را تولید کند. سپس این کلاس را با متادیتایی مزین کرده و توسط آن تگ سفارشی ویژهی این کامپوننت و تگهای HTML تشکیل دهندهی این کامپوننت را به AngularJS 2.0 معرفی میکنیم. در اینجا در صورت نیاز وابستگیهای تعریف این متادیتا را توسط واژهی کلیدی import دریافت میکنیم. نام این کلاس بهتر است Pascal case بوده و با حروف بزرگ شروع شود و همچنین به صورت نام ویژگی ختم شدهی به کلمهی Component باشد. در اینجا حتما نیاز است این کلاس export شود تا توسط module loader قابل استفاده و بارگذاری گردد. اگر View این کامپوننت نیاز به دریافت اطلاعاتی دارد، این اطلاعات به صورت خواصی در کلاس کامپوننت تعریف میشوند. این خواص تعریف شدهی با سطح دسترسی عمومی، مرسوم است به صورت camel case تعریف شوند و حروف اول آنها کوچک باشد.
اشتراکها
عیب یابی سایت های ASP.NET5 در IIS
Results
Method | Jit | Platform | Mean | Scaled |
---|---|---|---|---|
ByReference | LegacyJit | X64 | 1.649 us | 1.00 |
ByReferenceUnsafe | LegacyJit | X64 | 1.721 us | 1.04 |
ByReference | LegacyJit | X86 | 1.666 us | 1.00 |
ByReferenceUnsafe | LegacyJit | X86 | 1.673 us | 1.00 |
ByReference | RyuJit | X64 | 1.684 us | 1.00 |
ByReferenceUnsafe | RyuJit | X64 | 1.709 us | 1.02 |
To our surprise, the safe way is faster than unsafe!
COM، یک فناوری قدیمی و مختص به ویندوز است؛ هرچند NET Core. به صورت چندسکویی طراحی شدهاست، اما حداقل نگارش ویندوز آن، از کار با اشیاء COM پشتیبانی میکند. البته باید درنظر داشت که نگارش 1x آن اینچنین نیست و پشتیبانی از آن، از نگارش 2x شروع شدهاست.
محدودیتهای کار با اشیاء COM در NET Core 2x.
پیاده سازی پشتیبانی از اشیاء COM در NET Core 2x. به همراه اینترفیس IDispatch نیست. به این معنا که از مفهوم «late binding» پشتیبانی نمیکند. حدود 10 سال قبل در زمان ارائهی C# 4.0، واژهی کلیدی dynamic نیز ارائه شد که یکی از مهمترین اهداف آن، ساده سازی کار با اشیاء COM و پشتیبانی از Late binding بود:
این قطعه کد که در Full .NET Framework بدون مشکل اجرا میشود، در NET Core 2x. با خطای زیر متوقف خواهد شد:
البته اگر به task manager ویندوز در این حالت مراجعه کنید، مشاهده خواهید کرد که Excel.exe واقعا اجرا شدهاست؛ اما چون پیاده سازی IDispatch در اینجا وجود ندارد، امکان کار با واژهی کلیدی dynamic و late binding برای دسترسی به خاصیت Visible پشتیبانی نمیشود.
یک نکته: NET Core 3x. از Late binding پشتیبانی میکند.
روش کار با اشیاء COM در NET Core 2x.
چون NET Core 2x. از late binding اشیاء COM پشتیبانی نمیکند، میتوان در اینجا از روش قدیمیتر کار با اشیاء COM که استفادهی از «Interop assemblies» نام دارد، استفاده کرد. Interop assemblies در حقیقت محصور کنندههای اشیاء COM هستند که امکان کار مستقیم با آنها را از طریق early binding میسر میکنند. در یک چنین حالتی، کدهای فوق برای دسترسی به اشیاء COM کار با اکسل، به صورت زیر که early binding نام دارد، تغییر میکند:
روش تولید Interop assemblies
هنوز خود NET Core. روشی را برای تولید Interop assemblies ارائه ندادهاست و تولید آنها یکی از معدود مواردی است که نیاز به نصب Visual Studio را دارد. برای این منظور یک پروژهی خالی (از هر نوعی) را که بر اساس NET Framework 4x. تهیه میشود، در VS آغاز کنید و سپس در solution explorer بر روی پروژهی ایجاد شده کلیک راست کرده و گزینهی Add > Reference را انتخاب کنید. در صفحهی باز شده، گزینهی COM آنرا باید انتخاب کنید. در اینجا است که میتوانید با انتخاب یکی از موارد، ارجاعی را به آن شیء COM اضافه کنید.
پس از اینکار:
- ابتدا این ارجاع اضافه شده را در solution explorer انتخاب کرده و در پایین صفحه، در قسمت برگهی خواص آن، گزینهی «Embed Interop Types» آنرا به false تنظیم کنید.
- سپس یکبار پروژه را نیز کامپایل کنید.
این مراحل سبب تولید یک فایل dll خواهند شد که Interop assembly نام دارد و هم در برنامههای NET. و هم NET Core.، قابل استفادهاست.
روش استفاده از Interop assemblies در برنامههای NET Core.
اکنون که یک فایل dll را از شیء COM انتخابی، در یک پروژهی مجزای مبتنی بر NET 4x. تولید کردیم، روش استفادهی از آن در یک برنامهی دیگر مبتنی بر NET Core. به صورت زیر است:
فایل csproj را گشوده و ابتدا نام اسمبلی را منهای dll آن در قسمت Reference Include ذکر کنید. سپس مسیر فایل dll تولید شدهی در قسمت قبل را به صورت HintPath مشخص کنید. اگر میخواهید این dll را به صورت جداگانهای به همراه برنامهی خود توزیع نکنید، خاصیت EmbedInteropTypes را در اینجا به true تنظیم کنید. در این حالت کامپایلر، قسمتهایی از Interop.WIA.dll را که در برنامهی شما استفاده شدهاست، جزئی از خروجی نهایی آن میکند.
یک نکته: اگر EmbedInteropTypes را به true تنظیم کردید، نیاز به بستهی Microsoft.CSharp را نیز خواهید داشت:
روش دیگر استفاده از Interop assemblies در برنامههای NET Core.
روش فوق، جهت کار با فایلهای dll ای است که خودمان تولید کردهایم. برای سایر حالاتی که این موارد در سیستم نصب شدهاند (مانند Office Primary Interop Assemblies (PIA))، پس از افزودن ارجاعی به COM reference مدنظر، فایل csproj همان پروژهی NET 4x. را باز کرده و قسمت COMReference آنرا در اینجا (در فایل csproj پروژهی NET Core.) کپی کنید:
اطلاعات COMReference فوق از یک پروژهی NET 4x. و فایل csproj آن پس از افزودن ارجاعی به اشیاء COM آفیس و اکسل، در اینجا کپی شدهاند.
سپس یک نمونه از MS Office automation را توسط اشیاء COM آن به صورت زیر میتوان پیاده سازی کرد:
مثال فوق، معادل NET Core. این مثال قدیمی است:
How to automate Microsoft Excel from Microsoft Visual C#.NET
محدودیتهای کار با اشیاء COM در NET Core 2x.
پیاده سازی پشتیبانی از اشیاء COM در NET Core 2x. به همراه اینترفیس IDispatch نیست. به این معنا که از مفهوم «late binding» پشتیبانی نمیکند. حدود 10 سال قبل در زمان ارائهی C# 4.0، واژهی کلیدی dynamic نیز ارائه شد که یکی از مهمترین اهداف آن، ساده سازی کار با اشیاء COM و پشتیبانی از Late binding بود:
dynamic excel = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application", true)); excel.Visible = true; Console.WriteLine("Press Enter to close Excel."); Console.ReadLine(); excel.Quit();
System.__ComObject does not contain a definition for 'Visible'
یک نکته: NET Core 3x. از Late binding پشتیبانی میکند.
روش کار با اشیاء COM در NET Core 2x.
چون NET Core 2x. از late binding اشیاء COM پشتیبانی نمیکند، میتوان در اینجا از روش قدیمیتر کار با اشیاء COM که استفادهی از «Interop assemblies» نام دارد، استفاده کرد. Interop assemblies در حقیقت محصور کنندههای اشیاء COM هستند که امکان کار مستقیم با آنها را از طریق early binding میسر میکنند. در یک چنین حالتی، کدهای فوق برای دسترسی به اشیاء COM کار با اکسل، به صورت زیر که early binding نام دارد، تغییر میکند:
using Excel = Microsoft.Office.Interop.Excel; // ... var excel = new Excel.Application(); excel.Visible = true; Console.WriteLine("Press Enter to close Excel."); Console.ReadLine(); excel.Quit();
روش تولید Interop assemblies
هنوز خود NET Core. روشی را برای تولید Interop assemblies ارائه ندادهاست و تولید آنها یکی از معدود مواردی است که نیاز به نصب Visual Studio را دارد. برای این منظور یک پروژهی خالی (از هر نوعی) را که بر اساس NET Framework 4x. تهیه میشود، در VS آغاز کنید و سپس در solution explorer بر روی پروژهی ایجاد شده کلیک راست کرده و گزینهی Add > Reference را انتخاب کنید. در صفحهی باز شده، گزینهی COM آنرا باید انتخاب کنید. در اینجا است که میتوانید با انتخاب یکی از موارد، ارجاعی را به آن شیء COM اضافه کنید.
پس از اینکار:
- ابتدا این ارجاع اضافه شده را در solution explorer انتخاب کرده و در پایین صفحه، در قسمت برگهی خواص آن، گزینهی «Embed Interop Types» آنرا به false تنظیم کنید.
- سپس یکبار پروژه را نیز کامپایل کنید.
این مراحل سبب تولید یک فایل dll خواهند شد که Interop assembly نام دارد و هم در برنامههای NET. و هم NET Core.، قابل استفادهاست.
روش استفاده از Interop assemblies در برنامههای NET Core.
اکنون که یک فایل dll را از شیء COM انتخابی، در یک پروژهی مجزای مبتنی بر NET 4x. تولید کردیم، روش استفادهی از آن در یک برنامهی دیگر مبتنی بر NET Core. به صورت زیر است:
<ItemGroup> <Reference Include="Interop.WIA"> <HintPath>..\DNTScanner.Core.TypeLibrary\bin\Debug\Interop.WIA.dll</HintPath> <EmbedInteropTypes>True</EmbedInteropTypes> </Reference> </ItemGroup>
یک نکته: اگر EmbedInteropTypes را به true تنظیم کردید، نیاز به بستهی Microsoft.CSharp را نیز خواهید داشت:
<ItemGroup Condition=" '$(TargetFramework)' == 'net40' "> <Reference Include="Microsoft.CSharp" /> </ItemGroup> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <PackageReference Include="Microsoft.CSharp" Version="4.5.0" /> </ItemGroup>
روش دیگر استفاده از Interop assemblies در برنامههای NET Core.
روش فوق، جهت کار با فایلهای dll ای است که خودمان تولید کردهایم. برای سایر حالاتی که این موارد در سیستم نصب شدهاند (مانند Office Primary Interop Assemblies (PIA))، پس از افزودن ارجاعی به COM reference مدنظر، فایل csproj همان پروژهی NET 4x. را باز کرده و قسمت COMReference آنرا در اینجا (در فایل csproj پروژهی NET Core.) کپی کنید:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp3.0</TargetFramework> </PropertyGroup> <!-- The following 'COMReference' items were copied from a .NET Framework project. They were added by using the Visual Studio COM References window. See https://docs.microsoft.com/en-us/visualstudio/ide/managing-references-in-a-project?view=vs-2017. Observe the 'EmbedInteropTypes' tag value. See https://docs.microsoft.com/en-us/visualstudio/msbuild/common-msbuild-project-items?view=vs-2017#comreference --> <ItemGroup> <COMReference Include="Microsoft.Office.Core"> <Guid>{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}</Guid> <VersionMajor>2</VersionMajor> <VersionMinor>8</VersionMinor> <Lcid>0</Lcid> <WrapperTool>primary</WrapperTool> <Isolated>False</Isolated> <EmbedInteropTypes>True</EmbedInteropTypes> </COMReference> <COMReference Include="Microsoft.Office.Interop.Excel"> <Guid>{00020813-0000-0000-C000-000000000046}</Guid> <VersionMajor>1</VersionMajor> <VersionMinor>9</VersionMinor> <Lcid>0</Lcid> <WrapperTool>primary</WrapperTool> <Isolated>False</Isolated> <EmbedInteropTypes>True</EmbedInteropTypes> </COMReference> <COMReference Include="VBIDE"> <Guid>{0002E157-0000-0000-C000-000000000046}</Guid> <VersionMajor>5</VersionMajor> <VersionMinor>3</VersionMinor> <Lcid>0</Lcid> <WrapperTool>primary</WrapperTool> <Isolated>False</Isolated> <EmbedInteropTypes>True</EmbedInteropTypes> </COMReference> </ItemGroup> </Project>
سپس یک نمونه از MS Office automation را توسط اشیاء COM آن به صورت زیر میتوان پیاده سازی کرد:
using System; using System.Reflection; using Excel = Microsoft.Office.Interop.Excel; namespace ExcelDemo { class Program { public static void Main(string[] args) { Excel.Application excel; Excel.Workbook workbook; Excel.Worksheet sheet; Excel.Range range; try { // Start Excel and get Application object. excel = new Excel.Application(); excel.Visible = true; // Get a new workbook. workbook = excel.Workbooks.Add(Missing.Value); sheet = (Excel.Worksheet)workbook.ActiveSheet; // Add table headers going cell by cell. sheet.Cells[1, 1] = "First Name"; sheet.Cells[1, 2] = "Last Name"; sheet.Cells[1, 3] = "Full Name"; sheet.Cells[1, 4] = "Salary"; // Format A1:D1 as bold, vertical alignment = center. sheet.get_Range("A1", "D1").Font.Bold = true; sheet.get_Range("A1", "D1").VerticalAlignment = Excel.XlVAlign.xlVAlignCenter; // Create an array to multiple values at once. string[,] saNames = new string[5, 2]; saNames[0, 0] = "John"; saNames[0, 1] = "Smith"; saNames[1, 0] = "Tom"; saNames[1, 1] = "Brown"; saNames[2, 0] = "Sue"; saNames[2, 1] = "Thomas"; saNames[3, 0] = "Jane"; saNames[3, 1] = "Jones"; saNames[4, 0] = "Adam"; saNames[4, 1] = "Johnson"; // Fill A2:B6 with an array of values (First and Last Names). sheet.get_Range("A2", "B6").Value2 = saNames; // Fill C2:C6 with a relative formula (=A2 & " " & B2). range = sheet.get_Range("C2", "C6"); range.Formula = "=A2 & \" \" & B2"; // Fill D2:D6 with a formula(=RAND()*100000) and apply format. range = sheet.get_Range("D2", "D6"); range.Formula = "=RAND()*100000"; range.NumberFormat = "$0.00"; // AutoFit columns A:D. range = sheet.get_Range("A1", "D1"); range.EntireColumn.AutoFit(); // Make sure Excel is visible and give the user control // of Microsoft Excel's lifetime. excel.Visible = true; excel.UserControl = true; } catch (Exception e) { Console.WriteLine($"Error: {e.Message} Line: {e.Source}"); } } } }
How to automate Microsoft Excel from Microsoft Visual C#.NET
بازخوردهای پروژهها
مشکل با نوشتن تابع تجمعی سفارشی(از طریق پیاده سازی IAggregateFunction)
با سلام؛ ضمن تشکر از اینکه تجربیاتتون رو رایگان در اختیار بقیه قرار میدید، به شخصه خیلی استفاده کردم.
و همچنین این هم تنظیمات ستونی که از این تابع تجمعی میخوام استفاده کنم.
ممنون میشم در صورت امکان کمکم کنید.
سوالی داشتم در رابطه با پیاده سازی اینترفیس IAggregateFunction
من میخوام یه گزارش بنویسم که تو اون ستون آخرش میخواد مانده تجمعی را حساب کنه.
بنابراین میخواستم با پیاده سازی این اینترفیس و همچنین بازنویسی متد ProcessingBoundary آخرین مقدار رو به عنوان خروجی تابع تجمعی ارسال کنم.
public object ProcessingBoundary(IList<SummaryCellData> columnCellsSummaryData) { if (columnCellsSummaryData == null || !columnCellsSummaryData.Any()) return 0; var list = columnCellsSummaryData; var lastItem = list.Last(); return lastItem.CellData.PropertyValue; }
در پروژهی دیگه ای این اینترفیس رو پیاده سازی کردم و مشکلی نبود ولی در پروژه جاری
که پروژه ایست با مشخصات:
نوع پروژه : WPF with MVVM
از Prism و Unity هم برای ماژولار شدن استفاده کردم.
خطای زیر رو میده :
Method 'set_DisplayFormatFormula' in type 'Hezareh.Modules.Accounting.Reporting.ViewModels.MySampleAggregateFunction' from assembly 'Hezareh.Modules.Accounting, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
در صورتی که اینترفیس IAggregateFunction به صورت کامل توسط کلاس MySampleAggregateFunction پیاده سازی شده است و این هم کد کامل کلاس که همون کد مثال Sum خودتونه، که فقط تابع ProcessingBoundary رو تغییر دادم. این هم کد کاملش :
public class MySampleAggregateFunction : IAggregateFunction { public MySampleAggregateFunction() { } /// <summary> /// Fires before rendering of this cell. /// Now you have time to manipulate the received object and apply your custom formatting function. /// It can be null. /// </summary> public Func<object, string> DisplayFormatFormula { set; get; } #region Fields (6) double _groupAvg; long _groupRowNumber; double _groupSum; double _overallAvg; long _overallRowNumber; double _overallSum; #endregion Fields #region Properties (2) /// <summary> /// Returns current groups' aggregate value. /// </summary> public object GroupValue { get { return _groupAvg; } } /// <summary> /// Returns current row's aggregate value without considering the presence of the groups. /// </summary> public object OverallValue { get { return _overallAvg; } } #endregion Properties #region Methods (4) // Public Methods (1) /// <summary> /// Fires after adding a cell to the main table. /// </summary> /// <param name="cellDataValue">Current cell's data</param> /// <param name="isNewGroupStarted">Indicated starting a new group</param> public void CellAdded(object cellDataValue, bool isNewGroupStarted) { checkNewGroupStarted(isNewGroupStarted); _overallRowNumber++; _groupRowNumber++; double cellValue; if (double.TryParse(cellDataValue.ToSafeString(), NumberStyles.AllowThousands | NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out cellValue)) { groupAvg(cellValue); overallAvg(cellValue); } } // Private Methods (3) private void checkNewGroupStarted(bool newGroupStarted) { if (newGroupStarted) { _groupRowNumber = 0; _groupAvg = 0; _groupSum = 0; } } private void groupAvg(double cellValue) { _groupSum += cellValue; _groupAvg = _groupSum / _groupRowNumber; } private void overallAvg(double cellValue) { _overallSum += cellValue; _overallAvg = _overallSum / _overallRowNumber; } /// <summary> /// A general method which takes a list of data and calculates its corresponding aggregate value. /// It will be used to calculate the aggregate value of each pages individually, with considering the previous pages data. /// </summary> /// <param name="columnCellsSummaryData">List of data</param> /// <returns>Aggregate value</returns> public object ProcessingBoundary(IList<SummaryCellData> columnCellsSummaryData) { if (columnCellsSummaryData == null || !columnCellsSummaryData.Any()) return 0; var list = columnCellsSummaryData; var lastItem = list.Last(); return lastItem.CellData.PropertyValue; } #endregion Methods }
columns.AddColumn(column => { column.PropertyName<VoucherRowPrintViewModel>(x => x.CaclulatedRemains); column.CellsHorizontalAlignment(PdfRpt.Core.Contracts.HorizontalAlignment.Right); column.IsVisible(true); column.Order(5); column.Width(1.5f); column.ColumnItemsTemplate(template => { template.TextBlock(); template.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj)); }); column.AggregateFunction(aggregateFunction => { aggregateFunction.CustomAggregateFunction(new MySampleAggregateFunction()); aggregateFunction.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj)); }); column.HeaderCell("مانده"); });
Top Issues Fixed in Visual Studio 2019 version 16.6.0
- When New Git experience feature flag is enabled, a message will appear in Team Explorer guiding users to the new Git tool window.
- Fix for intermittent UI delay while closing VS when WinForms .NET Core designer is in open state.
- Fixed issues creating projects using type providers, throwing missing method exception at runtime.
- Fixed project creation for .NET framework projects.
- New find in files experience respects options in Tools-Options-Find and Replace pane.
- Fixed a bug where Git repository does not change when closing a Folder and opening a Solution.
- Fixed bug when building iOS app using full debug symbols.
- Added back browing of Mac Distribution provisioning profiles and certificates from Windows.
- Fixed a bug causing Visual Studio 2019 to stop responding when working with Xamarin projects on certain scenarios.
- Added keyboard shortcut for "Copy with Headers" option in SQL Script Results Grid
- SSDT users will now be able to set and view sensitivity properties for all version above SQL Server 2008
- Improve Connection Properties dialog for accessibility users.
- Fixed occasional crashes when using Tested By Code Lens indicator.
- Ensure auto population of text in Find in files is as per legacy behavior.
- Ensure left arrow key behavior in find in files is correct.
- An issue blocking C++ users of the C++20 Ranges library from using algorithms.
اشتراکها
تغییرات SQL Server 2022
اشتراکها