The future of C#
Over the last year we shipped no less than three "point releases" of C# (7.1, 7.2 and 7.3), full of small but useful language features. Mads and Dustin will race you through a tour of these, before turning to some of the big things we have in store for the future: Nullable reference types, recursive patterns, asynchronous streams and more.
پیشنیازها
برای دنبال کردن این مثال فرض بر این است که NET Core 2.0 SDK. و همچنین Angular CLI را نیز پیشتر نصب کردهاید. مابقی بحث توسط خط فرمان و ابزارهای dotnet cli و angular cli ادامه داده خواهند شد و الزامی به نصب هیچگونه IDE نیست و این مثال تنها توسط VSCode پیگیری شدهاست.
تدارک ساختار ابتدایی مثال جاری
ساخت برنامهی وب، توسط dotnet cli
ابتدا یک پوشهی جدید را به نام SignalRCore2Sample ایجاد میکنیم. سپس داخل این پوشه، پوشهی دیگری را به نام SignalRCore2WebApp ایجاد خواهیم کرد (تصویر فوق). از طریق خط فرمان به این پوشه وارد شده (در ویندوز، در نوار آدرس، دستور cmd.exe را تایپ و enter کنید) و سپس فرمان ذیل را صادر میکنیم:
dotnet new mvc
ساخت برنامهی کلاینت، توسط angular cli
سپس از طریق خط فرمان به پوشهی SignalRCore2Sample بازگشته و دستور ذیل را صادر میکنیم:
ng new SignalRCore2Client
اکنون که در پوشهی ریشهی SignalRCore2Sample قرار داریم، اگر در خط فرمان، دستور . code را صادر کنیم، VSCode هر دو پوشهی وب و client را با هم در اختیار ما قرار میدهد:
تکمیل پیشنیازهای برنامهی وب
پس از ایجاد ساختار اولیهی برنامههای وب ASP.NET Core و کلاینت Angular، اکنون نیاز است وابستگی جدید AspNetCore.SignalR را به آن معرفی کنیم. به همین جهت به فایل SignalRCore2WebApp.csproj مراجعه کرده و تغییرات ذیل را به آن اعمال میکنیم:
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp2.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.0.0-alpha1-final" /> </ItemGroup> <ItemGroup> <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" /> <DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="2.0.0" /> </ItemGroup> </Project>
پس از این تغییرات، دستور ذیل را در خط فرمان صادر میکنیم تا وابستگیهای پروژه نصب شوند:
dotnet restore
یک نکته: نگارش فعلی افزونهی #C مخصوص VSCode، با تغییر فایل csproj و restore وابستگیهای آن نیاز دارد یکبار آنرا بسته و سپس مجددا اجرا کنید، تا اطلاعات intellisense خود را به روز رسانی کند. بنابراین اگر VSCode بلافاصله کلاسهای مرتبط با بستههای جدید را تشخیص نمیدهد، علت صرفا این موضوع است.
پس از بازیابی وابستگیها، به ریشهی پروژهی برنامهی وب وارد شده و دستور ذیل را صادر کنید:
dotnet watch run
تکمیل برنامهی وب جهت ارسال پیامهایی به کلاینتهای متصل به آن
پس از افزودن وابستگیهای مورد نیاز، بازیابی و build برنامه، اکنون نوبت به تعریف یک Hub است، تا از طریق آن بتوان پیامهایی را به کلاینتهای متصل ارسال کرد. به همین جهت یک پوشهی جدید را به نام Hubs به پروژهی وب افزوده و سپس کلاس جدید MessageHub را به صورت ذیل به آن اضافه میکنیم:
using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR; namespace SignalRCore2WebApp.Hubs { public class MessageHub : Hub { public Task Send(string message) { return Clients.All.InvokeAsync("Send", message); } } }
پس از تعریف این Hub، نیاز است به کلاس Startup مراجعه کرده و دو تغییر ذیل را اعمال کنیم:
الف) ثبت و معرفی سرویس SignalR
ابتدا باید SignalR را فعالسازی کرد. به همین جهت نیاز است سرویسهای آنرا به صورت یکجا توسط متد الحاقی AddSignalR در متد ConfigureServices به نحو ذیل معرفی کرد:
public void ConfigureServices(IServiceCollection services) { services.AddSignalR(); services.AddMvc(); }
ب) ثبت مسیریابی دسترسی به Hub
پس از تعریف Hub، مرحلهی بعدی، مشخص سازی نحوهی دسترسی به آن است. به همین جهت در متد Configure، به نحو ذیل Hub را معرفی کرده و سپس یک path را برای آن مشخص میکنیم:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseSignalR(routes => { routes.MapHub<MessageHub>(path: "message"); });
http://localhost:5000/message
انتشار پیامهایی به تمام کاربران متصل به برنامه
آدرس فوق به تنهایی کار خاصی را انجام نمیدهد. از آن جهت اتصال کلاینتهای برنامه استفاده میشود و این کلاینتها پیامهای رسیدهی از طرف برنامه را از این آدرس دریافت خواهند کرد. بنابراین مرحلهی بعد، ارسال تعدادی پیام به سمت کلاینتها است. برای این منظور به HomeController برنامهی وب مراجعه کرده و آنرا به نحو ذیل تغییر میدهیم:
using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; using SignalRCore2WebApp.Hubs; namespace SignalRCore2WebApp.Controllers { public class HomeController : Controller { private readonly IHubContext<MessageHub> _messageHubContext; public HomeController(IHubContext<MessageHub> messageHubContext) { _messageHubContext = messageHubContext; } public IActionResult Index() { return View(); // show the view } [HttpPost] public async Task<IActionResult> Index(string message) { await _messageHubContext.Clients.All.InvokeAsync("Send", message); return View(); } } }
در این مثال ابتدا View ذیل نمایش داده میشود:
@{ ViewData["Title"] = "Home Page"; } <form method="post" asp-action="Index" asp-controller="Home" role="form"> <div class="form-group"> <label label-for="message">Message: </label> <input id="message" name="message" class="form-control"/> </div> <button class="btn btn-primary" type="submit">Send</button> </form>
تکمیل برنامهی کلاینت Angular جهت نمایش پیامهای رسیدهی از طرف سرور
تا اینجا ساختار ابتدایی برنامهی Angular را توسط Angular CLI ایجاد کردیم. اکنون نیاز است وابستگی سمت کلاینت SignalR Core را نصب کنیم. به همین جهت از طریق خط فرمان به پوشهی SignalRCore2Client وارد شده و دستور ذیل را صادر کنید:
npm install @aspnet/signalr-client --save
کلاینت رسمی signalr، هم جاوا اسکریپتی است و هم تایپاسکریپتی. به همین جهت به سادگی توسط یک برنامهی تایپ اسکریپتی Angular قابل استفاده است. کلاسهای آنرا در مسیر node_modules\@aspnet\signalr-client\dist\src میتوانید مشاهده کنید.
در ابتدا، فایل app.component.ts را به نحو ذیل تغییر میدهیم:
import { Component, OnInit } from "@angular/core"; import { HubConnection } from "@aspnet/signalr-client"; @Component({ selector: "app-root", templateUrl: "./app.component.html", styleUrls: ["./app.component.css"] }) export class AppComponent implements OnInit { hubPath = "http://localhost:5000/message"; messages: string[] = []; ngOnInit(): void { const connection = new HubConnection(this.hubPath); connection.on("send", data => { this.messages.push(data); }); connection.start().then(() => { // connection.invoke("send", "Hello"); console.log("connected."); }); } }
آرایهی messages را به نحو ذیل توسط یک حلقه در قالب این کامپوننت نمایش خواهیم داد:
<div> <h1> The messages from the server: </h1> <ul> <li *ngFor="let message of messages"> {{message}} </li> </ul> </div>
ng serve -o
همانطور که مشاهده میکنید، پیام خطای ذیل را صادر کردهاست:
Failed to load http://localhost:5000/message: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.
برای این منظور به فایل آغازین برنامهی وب مراجعه کرده و سرویسهای AddCors را به مجموعهی سرویسهای برنامه اضافه میکنیم:
public void ConfigureServices(IServiceCollection services) { services.AddSignalR(); services.AddCors(options => { options.AddPolicy("CorsPolicy", builder => builder .AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials()); }); services.AddMvc(); }
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseCors(policyName: "CorsPolicy");
در آخر برای آزمایش برنامه، به آدرس http://localhost:5000 یا همان برنامهی وب، مراجعه کرده و پیامی را ارسال کنید. بلافاصله مشاهده خواهید کرد که این پیام توسط کلاینت Angular دریافت شده و نمایش داده میشود:
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید: SignalRCore2Sample.zip
برای اجرا آن، ابتدا به پوشهی SignalRCore2WebApp مراجعه کرده و دو فایل bat آنرا به ترتیب اجرا کنید. اولی وابستگیهای برنامه را بازیابی میکند و دومی برنامه را بر روی پورت 5000 ارائه میدهد.
سپس به پوشهی SignalRCore2Client مراجعه کرده و در آنجا نیز دو فایل bat ابتدایی آنرا به ترتیب اجرا کنید. اولی وابستگیهای برنامهی Angular را بازیابی میکند و دومی برنامهی Angular را بر روی پورت 4200 اجرا خواهد کرد.
بررسی ویژگیهای C# 11
PdfRpt-1.9.zip
- Updated the project to use iTextSharp 5.4.1.0. - Updated the project to use EPPlus 3.1.3. EPPlus 3.1.3 has a reference to System.Web for Uri decoding. So to use PdfReport from now on you need to change your project's target framework to full profile instead of the client profile. - Added FlushType parameter to FlushInBrowser method. FlushType.Inline displays PDF in the browser instead of showing the download popup. - Added EFCodeFirstMvc4Sample. - Added PdfFilePrinter sample. - Fixed `StartIndex cannot be less than zero` exception when parameter values are defined without defining the actual parameters in SQL data sources.
برنامههای Vue.jsای از چندین کامپوننت برای بخش بندی هر قسمت تشکیل میشوند و این بخش بندی برای مدیریت بهتر تغییرات، خطایابی، نگهداری و استفاده مجدد (reusable) میباشد. فرض کنید تعدادی کامپوننت در برنامه داریم و اطلاعات این کامپوننتها بهم وابسته میباشند؛ بطور مثال یک کامپوننت انتخاب دسته بندی را داریم و به محض تغییر این مقدار، میخواهیم لیستی از محصولات پیشنهادی یا پرفروشِ آن دسته بندی، در کامپوننت پایین صفحه نمایش داده شود و یا خرید یک محصول را در نظر بگیرید که بلافاصله محتوای نمایش سبد خرید، بروزرسانی شود. در این مقاله ارتباط از نوع Parent و Child بین کامپوننتها بررسی میشود.
نکته: برای ارسال اطلاعات از کامپوننتِ Parent به Child، از Props استفاده میشود و برای ارتباط از Child به Parent، از emit$ استفاده میشود.
یک برنامه Vue.js با نام vue-communication-part-1 ایجاد نمایید. سپس دو کامپوننت را با نامهای Parent و Child، در پوشه components ایجاد کنید:
در کامپوننت Parent، یک تابع با نام increase وجود دارد که مقدار متغیر parentCounter را افزایش میدهد. چون قصد داریم مقدار متغیر parentCounter در کامپوننت Child نیز بروزرسانی شود، آن را به کامپوننت Child پاس میدهیم:
<Child :childCounter="parentCounter"/>
محتوای کامپوننت Parent:
<template> <div> <div> <h2>Parent Component</h2> <!-- را نمایش میدهید parentCounter مقدار --> <h1>{{ parentCounter }}</h1> <button @click="increase">Increase Parent</button> </div> <div> <!-- پاس میدهید Child در کامپوننت childCounter را به پراپرتی parentCounter مقدار --> <!--از طریق decreaseParent سبب اتصال و فراخوانی تابع @callDecreaseParent به decreaseParent با انتساب --> <!-- میشود Child در کامپوننت callDecreaseMethodInParent تابع --> <Child :childCounter="parentCounter" @callDecreaseParent="decreaseParent"/> </div> </div> </template> <script> //برای استفاده در کامپوننت جاری Child ایمپورت کردن کامپوننت import Child from "./Child.vue"; export default { // در این بخش متغیرهای مورد نیاز کامپوننت را تعریف میکنیم data() { return { parentCounter: 0 }; }, components: { // میتوان آرایه ای از کامپوننتها را در یک کامپوننت استفاده نمود // در این مثال فقط از یک کامپوننت استفاده شده Child }, methods: { //را یک واحد افزایش میدهد parentCounter این متد مقدار increase() { this.parentCounter++; }, decreaseParent() { this.parentCounter--; } } }; </script> <style> .parent-block, .child { text-align: center; margin: 20px; padding: 20px; border: 2px gray solid; } </style>
در کامپوننت Child قصد دریافت مقدار پراپرتیِ childCounter را داریم که از طریق کامپوننت Parent، مقدارش تنظیم و بروزرسانی میشود. به این منظور در قسمت props یک متغیر بنام childCounter را ایجاد میکنیم.
Data is the private memory of each component where you can store any variables you need. Props are how you pass this data from a parent component down to a child component
محتوای کامپوننت Child
<template> <div> <h3>Child Component</h3> <!-- را نمایش میدهید childCounter مقدار --> <h3>{{ childCounter }}</h3> <button @click="increase">Increase Me</button> <button @click="callDecreaseMethodInParent">Call Decrease Method In Parent</button> </div> </template> <script> export default { // استفاده میشود Child به Parent برای ارتباط بین کامپوننت props از props: { childCounter: Number }, data() { return {}; }, methods: { //را یک واحد افزایش میدهد childCounter این متد مقدار increase() { this.childCounter++; }, // فراخوانی میکند Parent را در کامپوننت decreaseParent تابع callDecreaseMethodInParent() { this.$emit("callDecreaseParent"); } } }; </script>
محتوای کامپوننت اصلی برنامه App.vue:
<template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png"> <parent></parent> </div> </template> <script> import Parent from "./components/Parent.vue"; export default { name: "app", components: { parent: Parent } }; </script> <style> #app { width: 50%; margin: 0 auto; text-align: center; } </style>
اکنون برنامه را با دستور زیر اجرا کنید:
npm run serve
بعد از اجرای دستور فوق، روی گزینه زیر ctrl+click میکنیم تا نتیجه کار در مرورگر قابل رویت باشد:
نمایش صفحه زیر نشان دهندهی درستی انجام کار تا اینجا است:
اکنون روی دکمهی Increase Parent کلیک میکنیم. همزمان مقدار شمارشگر، در هر دو کامپوننت Parent و Child افزایش مییابد و این بدین معناست که با استفاده از Props میتوانیم دادههای دلخواهی را در کامپوننت Child بروز رسانی کنیم. هر زمانی روی دکمهی Increase Me در کامپوننت Child کلیک کنیم، فقط به مقدار شمارشگر درون خودش اضافه میشود و تاثیری را بر شمارشگر Parent ندارد. در واقع یک کپی از مقدار شمارشگر Parent را درون خود دارد.
در ادامه قصد داریم بروزرسانی داده را از Child به Parent انجام دهیم. برای انجام اینکار از emit$ استفاده میکنیم. در دیکشنری Cambridge Dictionary معنی emit به ارسال یک سیگنال ترجمه شدهاست. در واقع بااستفاده از emit میتوانیم یک تابع را در کامپوننت Parent فراخوانی کنیم و در آن تابع، کد دلخواهی را برای دستکاری دادهها مینویسیم.
در تابع callDecreaseMethodInParent در کامپوننت Child، کد زیر را قرار میدهیم:
this.$emit("callDecreaseParent");
هر زمانکه این تابع اجرا شود، یک سیگنال از طریق کد زیر برای کامپوننت Parent ارسال میشود:
<Child @callDecreaseParent="decreaseParent"/>
در کد فوق مشخص شده که با ارسال سیگنال callDecreaseParent، تابع decreaseParent در کامپوننت Parent فراخوانی شود.
نکته: برای اجرای برنامه و دریافت پکیجهای مورد استفاده در مثال جاری، نیاز است دستور زیر را اجرا کنید:
npm install
چند نکته
this.$emit //dispatches an event to its parent component
کد فوق سبب اجرای یک تابع در کامپوننتِ Parent خودش میشود.
this.$parent // gives you a reference to the parent component
ارجاعی به کامپوننت Parent خودش را فراهم میکند:
this.$root // gives you a reference to the root component
زمانیکه چندین کامپوننت تو در تو را داریم یا به اصطلاح nested component، سبب ارجاعی به بالاترین کامپوننت Parent میگردد.
this.$parent.$emit // will make the parent dispatch the event to its parent
سبب اجرای تابعِ Parent کامپوننتِ Parent جاری میشود. به بیان ساده اگر این کد در کامپوننت فرزند فراخوانی شود، سبب اجرای تابعی در کامپوننت پدربزرگِ خود میشود.
this.$root.$emit // will make the root dispatch the event to itself
سبب اجرای تابعی در کامپوننت root میشود (بالاترین کامپوننتِ پدرِ کامپوننت جاری).
تابع emit$ دارای آگومانهای دیگری برای پاس دادن اطلاعات از کامپوننت Child به Parent میباشد؛ مثل زمانیکه قصد دارید اطلاعاتی در مورد محصول خریداری شده را به سبد خرید پاس دهید. در مثال دیگری که در ادامه قرار میگیرد نحوه کارکرد ارتباط کامپوننت Parent و Child را در یک برنامه بهتر تجربه میکنیم.
پیاده سازی یک سبد خرید ساده با روش مقالهی جاری
نکته: برای اجرای برنامه و دریافت پکیجهای مورد استفاده در مثال جاری، نیاز است دستور زیر را اجرا کنید:
npm install
همچنین نیاز هست تا پکیچ node-sass را با دستور زیر برای این مثال نصب کنید.
npm install node-sass
مطالب مفید در مورد CQRS
Welcome to the Edument CQRS and Intentful Testing Starter Kit project page. On this page, you will find links to our open sourced CQRS Starter Kit on GitHub as well as tutorials to get you started.
سری 6 قسمتی در رابطه با User Stories
در این مقاله با ادیتور VS Code کار میکنیم. بعد از نصب آن، از منوی Terminal، گزینهی New Terminal را کلیک کنید تا پنجرهی PowerShell نمایش داده شود؛ برای سرعت و دقت بیشتر در برنامههای vue.js ای. با دستور زیر vue cli را نصب میکنیم (فقط یک مرتبه و برای برنامههای بعدی vue.jsای، نیازی به اجرای این دستور نداریم):
npm install -g @vue/cli
جهت راه اندازی یک برنامهی پیش فرض Vue.js ای، کافیست دستور زیر را اجرا نماییم تا پکیجهای مورد نیاز، به همراه کانفیگ اولیه (Zero config) برای ما ایجاد شوند:
vue create movie-app
بعد از ایجاد برنامه در vs code، از طریق منوی File، گزینه Open Folder را کلیک کرده و پوشه برنامهای را که ایجاد کردیم، Select Folder میکنیم. ساختار اولیهی برنامهی ایجاد شده، به شکل زیر میباشد:
نیازمندیهای مثال جاری
A) برای گرفتن اطلاعات مورد نمایش در مثال جاری، از سایت omdbapi.com استفاده میکنیم که با دریافت یک api key آن بصورت رایگان، میتوانیم web serviceهای آن را Call نماییم.
B) از vuetify برای ui استفاده میکنیم که بصورت Material Design و دارای کامپوننتهای غنی میباشد؛ ضمن اینکه RTL را هم پشتیبانی میکند.
برای نصب آن در Terminal دستور زیر را اجرا میکنیم:
vue add vuetify
سپس جهت تست و صحت افزوده شدن و کانفیگ درست، با دستور زیر برنامه را اجرا میکنیم:
npm run serve
بعد از اجرای دستور فوق، روی گزینه زیر ctrl+click میکنیم تا نتیجه کار در مرورگر قابل رویت باشد:
نمایش صفحه زیر نشان دهندهی درستی انجام کار تا اینجا است:
نکته: جهت استفاده از امکان RTL کافیست در فایل vuetify.js واقع در پوشهی plugins، تغییرات زیر را انجام دهیم. در مثال جاری بدلیل اینکه اطلاعات انگلیسی میباشند، از نسخه LTR آن استفاده میکنیم؛ هر چند یکسری api فارسی نیز موجود میباشد که میتوان از آنها استفاده نمود.
import Vue from 'vue' import Vuetify from 'vuetify/lib' import 'vuetify/src/stylus/app.styl' Vue.use(Vuetify, { iconfont: 'md', rtl: true })
C) نصب vue-router : جهت انجام routeهای تودرتو ، مپ کردن کامپوننت ها با آدرسی مشخص، کار با پارامتر و HTML5 History API مورد استفاده قرار میگیرد. برای نصب آن، دستور زیر را اجرا میکنیم:
npm install vue-router
برای نوشتن routeهای مورد نیاز، یک فولدر را با نام router، در پوشه src برنامه ایجاد میکنیم و یک فایل جاوا اسکریپتی را در آن با نام index.js، میسازیم (این ساختار برای مدیریت بهتر پروژه میباشد):
درون فایل index.js، محتویات زیر را طبق مستندات آن قرار میدهیم:
import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter)
جهت استفاده از این router، نیاز است تا در نمونهی وهله سازی شدهی vue برنامه بکار گرفته شود. فایل main.js را باز کنید و خط زیر را در قسمت بالای برنامه وارد کنید:
import router from './router'
اکنون محتویات فایل main.js بشکل زیر میباشد:
import Vue from 'vue' import './plugins/vuetify' import App from './App.vue' import router from './router' Vue.config.productionTip = false new Vue({ render: h => h(App), router }).$mount('#app')
D) نصب axios : برای انجام درخواستهای HTTP و عملیات ایجکس در vue.js ترجیحا بهتر است از axios که یک کتابخانهی محبوب میباشد و کار با آن ساده است، استفاده شود. برای نصب آن، دستور زیر را اجرا میکنیم:
npm install axios
E) نصب vuex : کتابخانهای جهت مدیریت حالت (state management) برای vue.js میباشد و مشابه آن Flux و Redux برای React میباشند. برای نصب، دستور زیر را اجرا میکنیم:
npm install vuex
برای بکارگیری آن یک فولدر را با نام store در پوشهی src برنامه ایجاد میکنیم و یک فایل جاوا اسکریپتی را در آن با نام index.js میسازیم (این ساختار برای مدیریت بهتر پروژه میباشد). درون فایل index.js، محتویات زیر را طبق مستندات آن و ^ قرار میدهیم.
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export const store = new Vuex.Store()
برای استفاده و کانفیگ آن، محتویات فایل main.js را بشکل زیر تغییر دهید:
import Vue from 'vue' import './plugins/vuetify' import App from './App.vue' import router from './router' import {store} from './store' Vue.config.productionTip = false new Vue({ render: h => h(App), store, router }).$mount('#app')
نکته: برای اجرای برنامه و دریافت پکیجهای مورد استفاده در مثال جاری، نیاز است دستور زیر را اجرا کنید:
npm install