At Build 2019, Microsoft announced the release date for .NET Core 3.0 to be this coming September. This release includes the highly touted support for desktop platforms like WinForms and WPF. Today, there’s still a large developer base that’s building desktop applications using these .NET Windows desktop frameworks and by using .NET Core 3.0, you can now build desktop applications on the .NET Core platform.
- Create Database
- Delete Database
- Migrate Database
- Seed Database
services.AddParbad() .ConfigureDatabase(builder => { // Choose your database provider (SQL Server, MySql, Sqlite, etc.) builder.Use.... }) .ConfigureDatabaseInitializer(builder => { builder.UseInitializer(async context => { await context.Database.EnsureDeletedAsync(); // OR await context.Database.EnsureCreatedAsync(); // OR await context.Database.MigrateAsync(); }); });
services.AddParbad() .ConfigureDatabase(builder => { // Choose your Entity Framework provider (SQL Server, MySql, Sqlite, etc.) builder.Use.... }) .ConfigureDatabaseInitializer(builder => { builder.CreateDatabase(); // OR builder.DeleteAndCreateDatabase(); // OR builder.CreateAndMigrateDatabase(); });
services.AddParbad() .ConfigureDatabase(builder => { // SQL Server builder.UseSqlServer("Connection String", options => options.UseParbadMigrations()); }) .ConfigureDatabaseInitializer(builder => { builder.CreateAndMigrateDatabase(); });
builder.UseSqlServer("Connection String", options => options.MigrationsAssembly("Parbad"));
نمونه مثالها را همچنین میتوانید در صفحه GitHub پروژه مشاهده کنید.
- نصب آسان با استفاده از Nuget
- بدون نیاز به هیچگونه وب سرویس و یا دانش پیاده سازی سیستمهای پرداخت آنلاین
- پشتیبانی از درگاههای: ملت، ملی (سداد)، پارسیان، پاسارگاد، ایران کیش و سامان
- انجام پرداخت، فقط با نوشتن ۳ خط کد
- طراحی کاملا یکپارچه برای انجام عملیات پرداخت با تمامی بانکها
- رعایت نکات امنیتی پرداخت آنلاین
- درگاه مجازی، برای شبیه سازی عملیات پرداخت
- امکان استفاده از پروکسی برای سرورهای خارج از ایران در صورت نیاز
- استفاده از تکنولوژیهای مدرن و استاندارد
- قابل نصب بر روی پروژههای: ASP.NET Core, ASP.NET MVC, ASP.NET WebForms
- ASP.NET WebForms
- ASP.NET MVC
- ASP.NET CORE
- درخواست پرداخت
- تایید پرداخت
- بازگرداندن مبلغ پرداخت شده
- درگاهها
- HttpContext
- پایگاه داده
- پیامها
- ایجاد یک صورتحساب پرداخت با استفاده از InvoiceBuilder
- درگاه مجازی پرداخت
- استفاده از پروکسی
- توکن پرداخت
- تزریق وابستگی
- Logging
Install-Package Parbad.Owin
Install-Package Parbad.Mvc5
Install-Package Parbad.AspNetCore
dotnet add package Parbad.AspNetCore
SignalR - قسمت سوم
به این درخواست اولیه در کتابخونه SignalR همونطور که مشاهده میشه مذاکره (negotiate) گفته میشه. برقراری ارتباط این درخواست اولیه هم توسط XHR انجام میشه. نتیجه این مذاکره در تب Response Body قابل مشاهده است:
میبینین که علاوه بر آی دی ارتباط که یک guid است (تلفظ مرسومش «گوئِد» هستش) امکان برقراری ارتباط از طریق روش WebSocket رو هم از طرف سرور مشخص میکنه که با توجه به استفاده من از ویندوز 7 امکانش وجود نداره. یعنی اگر مثلا شما از ویندوز 8 و IIS 8 استفاده کنین مقدار TryWebSocket برابر true بوده و همچنین پارامتر WebSocketServerUrl نال نخواهد بود. البته این پارامتر تنها مربوط به سروره و برنامه کلاینت مورد استفاده (در اینجا یک مرورگر) هم باید توانایی استفاده از این روش رو داشته باشه. پس از اتمام این مذاکره ارتباط اصلی برقرار میشه.
یکی از قابلیتهای خوب این کتابخونه ارسال خطاهای رخ داده در سمت سرور به کلاینت هست. در تصویر زیر بدنه یک نمونه از پاسخهای سرور که نمایش دهنده خطای رخ داده در سمت سرور هست رو نشون داده شده:
برای راحتی دوستان در استفاده از راهنماهای این کتابخونه، یه مقدار کار روشون انجام دادم و با خلاصه کردن محتوای اونا (و کاهش حجم 95 درصدی!) برای استفاده آفلاین آماده کردم:
اگه فرصتی پیش بیاد و دوستان هم علاقه داشته باشن در قسمت بعدی برنامه چت رو بیشتر با هم بررسی میکنیم.
در ادامه قصد دارم تا روی بازدهی و کارایی این کتابخونه تو بار زیاد یه بررسی هایی انجام بدم (البته اگه وقت کنم چون راه اندازی یه محیط تست برا این جور کتابخونهها چندان آسون نیست) و مطلبی هم در مورد نحوه راه اندازی تست بار ارائه بدم.
متدهای async تقلبی
class MyService { public int CalculateXYZ() { // Tons of work to do in here! for (int i = 0; i != 10000000; ++i) ; return 42; } }
class MyService { public async Task<int> CalculateXYZAsync() { return await Task.Run(() => { // Tons of work to do in here! for (int i = 0; i != 10000000; ++i) ; return 42; }); } }
کاری که در اینجا انجام شده، استفادهی ناصحیح از Task.Run در حین طراحی یک متد و یک API است. عملیات انجام شده در آن واقعا غیرهمزمان نیست و در زمان انجام آن، باز هم ترد جدید اختصاص داده شده را تا پایان عملیات قفل میکند. اینجا است که باید بین CPU-bound operations و IO-bound operations تفاوت قائل شد. اگر Entity Framework 6 و یا کلاس WebClient و امثال آن، متدهایی Async را نیز ارائه دادهاند، اینها به معنای واقعی کلمه، غیرهمزمان هستند و در آنها کوچکترین CPU-bound operation ایی انجام نمیشود.
در حلقهای که در مثال فوق در حال پردازش است و یا تمام اعمال انجام شده توسط CPU، از مرزهای سیستم عبور نمیکنیم. نه قرار است فایلی را ذخیره کنیم، نه با اینترنت سر و کار داشته باشیم و یا مثلا اطلاعاتی را از وب سرویسی دریافت کنیم و نه هیچگونه IO-bound operation خاصی قرار است صورت گیرد.
زمانیکه برنامه نویسی قرار است با API شما کار کند و به امضای async Task میرسد، فرضش بر این است که در این متد واقعا یک کار غیرهمزمان در حال انجام است. بنابراین جهت بالابردن کارآیی برنامه، این نسخه را نسبت به نمونهی غیرهمزمان انتخاب میکند.
حال تصور کنید که استفاده کننده از این API یک برنامهی دسکتاپ نیست، بلکه یک برنامهی ASP.NET است. در اینجا Task.Run فراخوانی شده صرفا سبب خواهد شد عملیات مدنظر، بر روی یک ترد دیگر، نسبت به ترد اصلی اختصاص داده شده توسط ASP.NET برای فراخوانی و پردازش CalculateXYZAsync، صورت گیرد. این عملیات بهینه نیست. تمام پردازشهای درخواستهای ASP.NET در تردهای خاص خود انجام میشوند. وجود ترد دوم ایجاد شده توسط Task.Run در اینجا چه حاصلی را بجز سوئیچ بیجهت بین تردها و همچنین بالا بردن میزان کار Garbage collector دارد؟ در این حالت نه تنها سبب بالا بردن مقیاس پذیری سیستم نشدهایم، بلکه میزان کار Garbage collector و همچنین سوئیچ بین تردهای مختلف را در Thread pool برنامه به شدت افزایش دادهایم. همچنین یک چنین سیستمی برای تدارک تردهای بیشتر و مدیریت آنها، مصرف حافظهی بیشتری نیز خواهد داشت.
یک اصل مهم در طراحی کدهای Async
استفاده از Task.Run در پیاده سازی بدنه متدهای غیرهمزمان، یک code smell محسوب میشود.
چکار باید کرد؟
اگر در کدهای خود اعمال Async واقعی دارید که IO-bound هستند، از معادلهای Async طراحی شده برای کار با آنها، مانند متد SaveChangesAsync در EF، متد DownloadStringTaskAsync کلاس WebClient و یا متدهای جدید Async کلاس Stream برای خواندن و نوشتن اطلاعات استفاده کنید. در یک چنین حالتی ارائه متدهای async Task بسیار مفید بوده و در جهت بالابردن مقیاس پذیری سیستم بسیار مؤثر واقع خواهند شد.
اما اگر کدهای شما صرفا قرار است بر روی CPU اجرا شوند و تنها محاسباتی هستند، اجازه دهید مصرف کننده تصمیم بگیرد که آیا لازم است از Task.Run برای فراخوانی متد ارائه شده در کدهای خود استفاده کند یا خیر. اگر برنامهی دسکتاپ است، این فراخوانی مفید بوده و سبب آزاد شدن ترد UI میشود. اگر برنامهی وب است، به هیچ عنوان نیازی به Task.Run نبوده و فراخوانی متداول آن با توجه به اینکه درخواستهای برنامههای ASP.NET در تردهای مجزایی اجرا میشوند، کفایت میکند.
به صورت خلاصه
از Task.Run در پیاده سازی بدنه متدهای API خود استفاده نکنید.
از Task.Run در صورت نیاز (مثلا در برنامههای دسکتاپ) در حین فراخوانی و استفاده از متدهای API ارائه شده استفاده نمائید:
private async void MyButton_Click(object sender, EventArgs e) { await Task.Run(() => myService.CalculateXYZ()); }
بنابراین نوشتن یک چنین کدهایی در پیاده سازی یک API غیرهمزمان
await Task.Run(() => { for (int i = 0; i != 10000000; ++i) ; });
برای مطالعه بیشتر
Should I expose asynchronous wrappers for synchronous methods
checkBoxList جزو کنترلهایی در ASP.Net است که نمیتوان RequiredFieldValidator استاندارد را بر آن اعمال کرد. به عبارتی اگر نیاز بود حداقل یک آیتم چک باکس لیست حتما توسط کاربر انتخاب شود، راه حل آمادهای برای آن وجود ندارد. پیاده سازی اینکار با استفاده از jQuery به سادگی میسر است که در ادامه آنرا مرور خواهیم کرد.
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CheckBoxListValidator._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
//<![CDATA[
function CheckItems(sender, args) {
//Get the total nuumber of selected CheckBoxes
var num = jQuery("table#<%=CheckBoxList1.ClientID%> input:checked").length;
args.IsValid = num > 0;
}
//]]>
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:CheckBoxList ID="CheckBoxList1" runat="server">
<asp:ListItem>item1</asp:ListItem>
<asp:ListItem>item2</asp:ListItem>
</asp:CheckBoxList>
<asp:CustomValidator ClientValidationFunction="CheckItems" ID="ValidateIt"
runat="server" ErrorMessage="(*)"> </asp:CustomValidator>
<asp:Button ID="Button1" runat="server" />
</div>
</form>
</body>
</html>
توضیحات:
یک CustomValidator استاندارد را به فرم اضافه کردهایم تا توسط تابعی که به ClientValidationFunction آن معرفی میشود، کار اعتبار سنجی سمت کاربر را انجام دهد. این تابع یا همان CheckItems مثال فوق، امضای استاندارد و آشنایی دارد. اگر تعیین اعتبار صورت گرفته باشد، باید args.IsValid در آن به true تنظیم شود یا بر عکس.
اصل قضیه هم، همین یک سطر کد زیر است:
var num = jQuery("table#<%=CheckBoxList1.ClientID%> input:checked").length;
استفاده از AutoMapper در برنامههای چند ریسمانی
برای نمونه یک چنین کدی را نباید در برنامههای خود داشته باشید:
public ActionResult Index() { Mapper.CreateMap<UserViewModel, User>(); //ادامهی کدها
نمونهی دیگر آن، یک چنین کدهایی هستند:
using (var context = new TestDbContext()) { Mapper.CreateMap<SourceClass, DestinationClass>() .AfterMap((src, dest) => { //using context }); var dest = Mapper.Map<DestinationClass>(source); }
اگر در یک چنین حالتی نیاز به استفادهی context داشتید، بهتر است متدهای استاتیک AutoMapper را فراموش کرده و به نحو ذیل یک موتور محلی نگاشت را ایجاد کنید. چون سطح دید و دسترسی این موتور، عمومی و سراسری نیست، مشکلات thread safety را نخواهد داشت.
var configurationStore = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers); configurationStore.AddProfile<TestProfile1>(); var mapper = new MappingEngine(configurationStore); configurationStore.CreateMap<SourceClass, DestinationClass>() //ادامهی کدها