انتشار ویژوال استدیو ۲۰۲۲ نگارش 17.5
For .NET and cloud developers, we’ve focused on improving the inner-loop dev experience. New .http/.rest files make it easier to test and iterate on your APIs directly in Visual Studio, while improved Dev Tunnels help streamline the configuration and management of your webhooks. We’ve also made it easier than ever to deploy your ASP.NET apps to containers.
Game developers can now view properties from base classes modified in an Unreal Blueprint asset without leaving the IDE. Visual Studio has improved the cross-platform development experience with a new remote file explorer, Linux Console output to the Integrated Terminal window, dev container improvements, and more.
Beyond individuals, Visual Studio also has new features to better support dev teams at scale, with exportable configuration files and a persistent update toggle helping ensure everyone on your team is working from the latest version of the tool.
This blog covers several of the top new features in Visual Studio 17.5—to see some in action, watch the Visual Studio 17.5 release video. As always lot of these features come straight from your feedback and suggestions. Your feedback is critical to help us make Visual Studio the best tool it can be!
در اینجا میخواهیم بدون استفاده از هیچگونه کامپوننت ثالثی، صرفا بر اساس قابلیتهای جدید ماژول HttpClient ارائه شدهی در Angular 4.3، درصد پیشرفت آپلود را نیز نمایش دهیم. تغییرات مورد نیاز برای این منظور به شرح زیر هستند:
1) تغییر سرویس برنامه جهت استفاده از HTTP Client و گزارش درصد پیشرفت
postTicket(ticket: Ticket, filesList: FileList): Observable<HttpEvent<any>> { //… the same as before const headers = new HttpHeaders().set("Accept", "application/json"); return this.http .post(`${this.baseUrl}/SaveTicket`, formData, { headers: headers, reportProgress: true, observe: "events" }) .map(response => response || {}) .catch((error: HttpErrorResponse) => { console.error("observable error: ", error); return Observable.throw(error.statusText); }); }
الف) خروجی متد آن یک Observable از نوع HttpEvent تعیین شدهاست. به این ترتیب مشترکین به آن قادر خواهند شد به رخدادهای HTTP Client گوش فرا دهند:
postTicket(ticket: Ticket, filesList: FileList): Observable<HttpEvent<any>> {
ب) به قسمت options متد post، گزینههای "observe: "events و reportProgress: true اضافه شدهاند:
{ headers: headers, reportProgress: true, observe: "events" }
2) تغییر متد submitForm کامپوننت جهت گزارش و اعمال تغییرات به قالب برنامه
queueProgress: number; isUploading: boolean; uploadTimeRemaining: number; uploadTimeElapsed: number; uploadSpeed: number; submitForm(form: NgForm) { const fileInput: HTMLInputElement = this.screenshotInput.nativeElement; this.queueProgress = 0; this.isUploading = true; let startTime = Date.now(); this.uploadService.postTicket(this.model, fileInput.files).subscribe( (event: HttpEvent<any>) => { switch (event.type) { case HttpEventType.Sent: startTime = Date.now(); console.log("Request sent!"); break; case HttpEventType.DownloadProgress: case HttpEventType.UploadProgress: if (event.total) { this.queueProgress = Math.round(event.loaded / event.total * 100); const timeElapsed = Date.now() - startTime; const uploadSpeed = event.loaded / (timeElapsed / 1000); this.uploadTimeRemaining = Math.ceil( (event.total - event.loaded) / uploadSpeed ); this.uploadTimeElapsed = Math.ceil(timeElapsed / 1000); this.uploadSpeed = uploadSpeed / 1024 / 1024; } break; case HttpEventType.Response: this.queueProgress = 100; this.isUploading = false; console.log("Done! ResponseBody:", event.body); break; } }, (error: HttpErrorResponse) => { this.isUploading = false; console.log(error); } ); }
- HttpEventType.Sent شروع عملیات است. برای مثال از آن میتوان جهت تعیین زمان شروع به آپلود استفاده کرد. سپس این زمان را با زمان جاری، در رخداد آپلود به سرور مقایسه و گزارش سرعت آپلود، زمانهای صرف شده و باقیمانده را محاسبه کرد.
- HttpEventType.DownloadProgress و HttpEventType.UploadProgress گزارش درصد آپلود را مشخص میکنند. در اینجا event.total حجم کلی است و event.loaded حجم ارسالی کلی به سمت سرور میباشد. به همین جهت از این اطلاعات میتوان برای نمایش درصد کل آپلود استفاده کرد.
- HttpEventType.Response در پایان عملیات رخخواهد داد. در اینجا event.body همان بدنهی response دریافتی از سمت سرور است.
3) تغییر رابط کاربری برنامه (قالب کامپوننت) جهت گزارش درصد پیشرفت کلی آپلود
<div *ngIf="queueProgress > 0"> <table class="table"> <thead> <tr> <th width="15%">Event</th> <th>Status</th> </tr> </thead> <tbody> <tr> <td><strong>Elapsed time</strong></td> <td nowrap>{{uploadTimeElapsed | number:'.1'}} second(s)</td> </tr> <tr> <td><strong>Remaining time</strong></td> <td nowrap>{{uploadTimeRemaining | number:'.1'}} second(s)</td> </tr> <tr> <td><strong>Upload speed</strong></td> <td nowrap>{{uploadSpeed | number:'.3'}} MB/s</td> </tr> <tr> <td><strong>Queue progress</strong></td> <td> <div class="progress-bar progress-bar-info progress-bar-striped" role="progressbar" aria-valuemin="0" aria-valuemax="100" [attr.aria-valuenow]="queueProgress" [ngStyle]="{ 'width': queueProgress + '%' }"> {{queueProgress}}% </div> </td> </tr> </tbody> </table> </div> <button class="btn btn-primary" [disabled]="form.invalid || isUploading" type="submit">Submit</button>
همچنین در طی مدت آپلود، خاصیت isUploading به true تنظیم میشود تا دکمهی ارسال را غیرفعال کند. این خاصیت در صورت بروز خطایی و یا تکمیل شدن عملیات، false میشود.
یک نکته: اگر میخواهید درصد پیشرفت آپلود را در حالت آزمایش local بهتر مشاهده کنید، دربرگهی network، سرعت را بر روی 3G تنظیم کنید:
کدهای کامل این تغییرات را از اینجا میتوانید دریافت کنید.
مدل برنامه
using System.ComponentModel; using System.ComponentModel.DataAnnotations; namespace Mvc4TwitterBootStrapTest.Models { public class User { [DisplayName("نام")] [Required(ErrorMessage="لطفا نام را تکمیل کنید")] public string Name { set; get; } [DisplayName("نام خانوادگی")] [Required(ErrorMessage = "لطفا نام خانوادگی را تکمیل کنید")] public string LastName { set; get; } } }
کنترلر برنامه
using System.Web.Mvc; using Mvc4TwitterBootStrapTest.Models; namespace Mvc4TwitterBootStrapTest.Controllers { public class HomeController : Controller { [HttpGet] public ActionResult Index() { return View(); } [HttpPost] public ActionResult Index(User user) { if (this.ModelState.IsValid) { if (user.Name != "Vahid") { this.ModelState.AddModelError("", "لطفا مشکلات را برطرف کنید!"); this.ModelState.AddModelError("Name", "نام فقط باید وحید باشد!"); return View(user); } // todo: save ... return RedirectToAction("Index"); } return View(user); } } }
طراحی View سازگار با Twitter bootstrap
@model Mvc4TwitterBootStrapTest.Models.User @{ ViewBag.Title = "تعریف کاربر"; } @using (Html.BeginForm("Index", "Home", FormMethod.Post, new { @class = "form-horizontal" })) { @Html.ValidationSummary(true, null, new { @class = "alert alert-error alert-block" }) <fieldset> <legend>تعریف کاربر</legend> <div class="control-group"> @Html.LabelFor(x => x.Name, new { @class = "control-label" }) <div class="controls"> @Html.TextBoxFor(x => x.Name) @Html.ValidationMessageFor(x => x.Name, null, new { @class = "help-inline" }) </div> </div> <div class="control-group"> @Html.LabelFor(x => x.LastName, new { @class = "control-label" }) <div class="controls"> @Html.TextBoxFor(x => x.LastName) @Html.ValidationMessageFor(x => x.LastName, null, new { @class = "help-inline" }) </div> </div> <div class="form-actions"> <button type="submit" class="btn btn-primary"> ارسال</button> </div> </fieldset> }
1) کلاس form-horizontal به فرم جاری اضافه شده است تا در ادامه بتوانیم برچسبها را در کنار تکست باکسها به صورت افقی نمایش دهیم.
2) به ValidationSummary کلاسهای alert alert-error alert-block انتساب داده شدهاند تا نمایش خطای کلی یک فرم، متناسب با Twitter bootstrap شود.
3) هر خاصیت، با یک div دارای کلاس control-group محصور شده است.
4) هر برچسب دارای کلاس control-label است.
5) به هر ValidationMessageFor کلاس help-inline انتساب داده شده است.
6) کنترلهای ورودی برنامه در divایی با کلاس controls محصور شدهاند.
7) قسمت دکمه فرم، در div ایی با کلاس form-actions قرار گرفته تا یک زمینه خاکستری در اینجا ظاهر شود.
8) دکمه فرم، با کلاس btn خاص bootstrap تزئین شده.
در این حالت به شکل فوق خواهیم رسید. همانطور که ملاحظه میکنید در صورتیکه بر روی دکمه ارسال کلیک شود، همان رنگهای متداول jQuery Validator ظاهر میشوند و کل ردیف همانند روشهای متداول Twitter bootstrap دارای رنگ قرمز انتساب یافته توسط کلاس error نخواهد شد.
برای رفع این مشکل باید اندکی اسکریپت نویسی کرد:
@section javaScript { <script type="text/javascript"> $.validator.setDefaults({ highlight: function (element, errorClass, validClass) { if (element.type === 'radio') { this.findByName(element.name).addClass(errorClass).removeClass(validClass); } else { $(element).addClass(errorClass).removeClass(validClass); $(element).closest('.control-group').removeClass('success').addClass('error'); } $(element).trigger('highlated'); }, unhighlight: function (element, errorClass, validClass) { if (element.type === 'radio') { this.findByName(element.name).removeClass(errorClass).addClass(validClass); } else { $(element).removeClass(errorClass).addClass(validClass); $(element).closest('.control-group').removeClass('error').addClass('success'); } $(element).trigger('unhighlated'); } }); $(function () { $('form').each(function () { $(this).find('div.control-group').each(function () { if ($(this).find('span.field-validation-error').length > 0) { $(this).addClass('error'); } }); }); }); </script> }
اینبار در حالت اعتبار سنجی، به شکل ذیل خواهیم رسید:
و یا اگر کاربر فیلد را تکمیل کند، رنگ فیلد و ردیف تعیین اعتبار شده، سبز میشود:
و در حالت خطاهای سفارشی سمت سرور، پس از postback، شکل زیر نمایش داده میشود:
پیاده سازی Product Tour
بازنویسی سطح دوم کش برای Entity framework 6
var autoCompletesViewModelTest = _hotels.Include(row => row.Profile.City.State.Country).Where(row => row.IsApproved && row.Profile.IsDeleted == false && row.Profile.IsDeactivated == false).Cacheable().ProjectToList<AutoCompleteHotelViewModel>(_mapper.ConfigurationProvider);
var autoCompletesViewModel = await _hotels.Include(row => row.Profile.City.State.Country).Where(row => row.IsApproved && row.Profile.IsDeleted == false && row.Profile.IsDeactivated == false).Cacheable().ProjectToListAsync<AutoCompleteHotelViewModel>(_mapper.ConfigurationProvider);
HasKey(row => row.Id); HasRequired(row => row.User).WithRequiredDependent(row => row.UserProfile); Property(row => row.Id).HasColumnName("UserId");
اضافه کردن یک ردیف بالای جدول