چگونه یک کد آنالیزر Roslyn بنویسیم؟
Roslyn analyzers inspect your code for style, quality, maintainability, design and other issues. Because they are powered by the .NET Compiler Platform, they can produce warnings in your code as you type even before you’ve finished the line. In other words, you don’t have to build your code to find out that you made a mistake. Analyzers can also surface an automatic code fix through the Visual Studio light bulb prompt that allows you to clean up your code immediately.
انتشار ماشین حساب ویندوز برای لینوکس
This unexpected app port is arrives courtesy of the Uno Platform. They took the source code for Windows’s default Calculator app (which Microsoft made open source last year) and got it up and running on Linux desktops.
مشکل حذف تصاویر آپلود شده
در قسمت قبل، این امکان را مهیا کردیم که کاربران بتوانند پیش از ثبت اطلاعات یک اتاق، تصاویر آنرا به سرور آپلود کنند. یعنی تصاویری که در ابتدای کار آپلود میشوند، هنوز در بانک اطلاعاتی ثبت نشدهاند و هیچ رکوردی از آنها موجود نیست. در این حالت اگر کاربری تصاویری را آپلود کرده و سپس بر روی دکمهی back کلیک کند، با تعدادی تصویر آپلود شدهی غیرمنتسب به اتاقهای موجود، مواجه خواهیم شد. همچنین اگر شخصی به قسمت ویرایش تصاویر مراجعه کند و با کلیک بر روی دکمهی حذف یک تصویر، آنرا حذف کند، این حذف باید در بانک اطلاعاتی هم منعکس شود؛ در غیر اینصورت باز هم کاربر میتواند تصویری را حذف کند، اما در آخر بر روی دکمهی به روز رسانی اطلاعات رکورد کلیک نکند. در این حالت در دفعات بعدی مراجعهی به اطلاعات یک چنین اتاقی، با نقص اطلاعات تصاویری مواجه میشویم که در لیست تصاویر منتسب به یک اتاق وجود دارند، اما اصل فایل تصویری متناظر با آنها از سرور حذف شدهاست.
حذف اطلاعات تصاویر، در حالت ثبت اطلاعات
زمانیکه قرار است اطلاعات اتاقی برای اولین بار ثبت شود، حذف تصاویر آپلود شدهی مرتبط با آن سادهاست؛ چون هنوز اصل رکورد اتاق ثبت نشدهاست و این تصاویر در این لحظه، به رکوردی تعلق ندارند. بنابراین ابتدا متد رویدادگردان DeletePhoto را به دکمهی حذف اطلاعات هر تصویر نمایش داده شده، انتساب میدهیم:
@if (HotelRoomModel.HotelRoomImages.Count > 0) { var serial = 1; foreach (var roomImage in HotelRoomModel.HotelRoomImages) { <div class="col-md-2 mt-3"> <div class="room-image" style="background: url('@roomImage.RoomImageUrl') 50% 50%; "> <span class="room-image-title">@serial</span> </div> <button type="button" @onclick="()=>DeletePhoto(roomImage)" class="btn btn-outline-danger btn-block mt-4">Delete</button> </div> serial++; } }
@code { private const string UploadFolder = "Uploads"; private void DeletePhoto(HotelRoomImageDTO imageDto) { var imageFileName = imageDto.RoomImageUrl.Replace($"{UploadFolder}/", "", StringComparison.OrdinalIgnoreCase); if (HotelRoomModel.Id == 0 && Title == "Create") { FileUploadService.DeleteFile(imageFileName, WebHostEnvironment.WebRootPath, UploadFolder); HotelRoomModel.HotelRoomImages.Remove(imageDto); } } }
- در این شیء، مقدار خاصیت RoomImageUrl، همواره با نام پوشهای که فایلهای تصویری در آن آپلود شدهاند، شروع میشود. به همین جهت نام پوشه را از آن حذف کرده و بر این اساس، متد FileUploadService.DeleteFile را فراخوانی میکنیم تا تصویر جاری را از سرور حذف کند.
- سپس با فراخوانی متد Remove بر روی لیست تصاویر موجود، سبب به روز رسانی UI نیز خواهیم شد و به این ترتیب، تصویری که فایل آن از سرور حذف شده، از UI نیز حذف خواهد شد.
حذف تصاویر، در زمان ویرایش اطلاعات یک اتاق تعریف شده
همانطور که در ابتدای بحث نیز عنوان شد، نمیخواهیم در حالت ویرایش یک رکورد، با کلیک بر روی حذف یک تصویر، بلافاصله آنرا از سرور نیز حذف کنیم. چون ممکن است کاربری تصویری را حذف کند، اما بجای ذخیره سازی اطلاعات رکورد، بر روی دکمهی back کلیک کند. بنابراین در اینجا حذف تصاویر را صرفا به حذف آنها از UI محدود میکنیم و حذف نهایی را به زمان کلیک بر روی دکمهی ذخیره سازی اطلاعات در حال ویرایش، موکول خواهیم کرد.
به همین جهت در ابتدا با کلیک بر روی دکمهی حذف، ابتدا با حذف آن تصویر از HotelRoomImages، سبب به روز رسانی UI خواهیم شد، اما این تصویر را واقعا حذف نمیکنیم. در اینجا فقط نام آنرا در یک لیست، برای حذف نهایی، ذخیره سازی خواهیم کرد:
@code { private List<string> DeletedImageFileNames = new List<string>(); private void DeletePhoto(HotelRoomImageDTO imageDto) { var imageFileName = imageDto.RoomImageUrl.Replace($"{UploadFolder}/", "", StringComparison.OrdinalIgnoreCase); if (HotelRoomModel.Id == 0 && Title == "Create") { // ... } else { // Edit Mode DeletedImageFileNames.Add(imageFileName); HotelRoomModel.HotelRoomImages.Remove(imageDto); // Update UI } }
سپس در جائیکه کار مدیریت ثبت اطلاعات صورت میگیرد، پس از به روز رسانی رکورد متناظر با یک اتاق، بر اساس لیست DeletedImageFileNames، فایلهای علامتگذاری شدهی برای حذف را نیز واقعا از سرور حذف میکنیم:
private async Task HandleHotelRoomUpsert() { // ... if (HotelRoomModel.Id != 0 && Title == "Update") { // Update Mode var updatedRoomDto = await HotelRoomService.UpdateHotelRoomAsync(HotelRoomModel.Id, HotelRoomModel); foreach(var imageFileName in DeletedImageFileNames) { FileUploadService.DeleteFile(imageFileName, WebHostEnvironment.WebRootPath, UploadFolder); } // await AddHotelRoomImageAsync(updatedRoomDto); await JsRuntime.ToastrSuccess($"The `{HotelRoomModel.Name}` updated successfully."); } else { // ... } } }
نمایش «لطفا منتظر بمانید» در حین آپلود تصاویر
در ادامه میخواهیم تا پایان نمایش آپلود تصاویر، پیام «لطفا منتظر بمانید» را به همراه یک spinner نمایش دهیم. بنابراین در ابتدا کلاسهای جدید زیر را به فایل wwwroot\css\site.css اضافه میکنیم:
.spinner { border: 16px solid silver !important; border-top: 16px solid #337ab7 !important; border-radius: 50% !important; width: 80px !important; height: 80px !important; animation: spin 700ms linear infinite !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%); position: absolute !important; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
@code { private bool IsImageUploadProcessStarted; private async Task HandleImageUpload(InputFileChangeEventArgs args) { try { IsImageUploadProcessStarted = true; // ... } finally { IsImageUploadProcessStarted = false; } } }
<InputFile OnChange="HandleImageUpload" multiple></InputFile> <div class="row"> @if (IsImageUploadProcessStarted) { <div class="col-md-12"> <span><i class="spinner"></i> Please wait.. Images are uploading...</span> </div> }
دریافت تائیدیهی حذف، پس از کلیک بر روی دکمههای حذف تصاویر
در قسمت 12 این سری، کامپوننت Confirmation.razor را توسعه دادیم. در اینجا میخواهیم با کلیک بر روی دکمههای حذف تصاویر، ابتدا توسط این کامپوننت، تائیدیهای دریافت شود و در صورت تائید، آن تصویر انتخابی را حذف کنیم.
به همین جهت در ابتدا فایل Confirmation.razor را به پوشهی جدید Pages\Components کپی میکنیم. سپس فضای نام آنرا به فایل BlazorServer\BlazorServer.App\_Imports.razor اضافه میکنیم تا در تمام کامپوننتهای برنامه قابل استفاده شود:
@using BlazorServer.App.Pages.Components
<Confirmation @ref="Confirmation1" OnCancel="OnCancelDeleteImageClicked" OnConfirm="@(()=>OnConfirmDeleteImageClicked(ImageToBeDeleted))"> <div> Do you want to delete @ImageToBeDeleted?.RoomImageUrl image? </div> </Confirmation>
- سپس روالهای رویدادگردان OnCancel و OnConfirm به متدهایی در کامپوننت جاری متصل شدهاند.
- در آخر پیامی تعریف شدهاست.
برای اینکه کامپوننت فوق عمل کند، نیاز است تغییرات زیر را به قسمت کدها اعمال کنیم:
private Confirmation Confirmation1; private HotelRoomImageDTO ImageToBeDeleted; private void OnCancelDeleteImageClicked() { // Confirmation1.Hide(); } private void DeletePhoto(HotelRoomImageDTO imageDto) { ImageToBeDeleted = imageDto; Confirmation1.Show(); } private void OnConfirmDeleteImageClicked(HotelRoomImageDTO imageDto) {
- در اینجا محتوای متد DeletePhoto اصلی را (متدی را که تا پیش از این مرحله تکمیل کردیم) به متد جدید OnConfirmDeleteImageClicked منتقل کردهایم. یعنی در ابتدا فقط یک modal نمایش داده میشود. پس از اینکه کاربر عملیات حذف را تائید کرد، رویداد OnConfirm، سبب فراخوانی متد OnConfirmDeleteImageClicked خواهد شد (که همان DeletePhoto قبل از این تغییرات است).
حذف کامل یک اتاق به همراه تمام تصاویر منتسب به آن
مرحلهی آخر این قسمت، اضافه کردن دکمهی حذف، به ردیفهای کامپوننت نمایش لیست اتاقها است که این مورد نیز باید به همراه دریافت تائیدیهی حذف و همچنین حذف تمام وابستگیهای اتاق ثبت شده باشد:
<td> <NavLink href="@($"hotel-room/edit/{room.Id}")" class="btn btn-primary">Edit</NavLink> <button class="btn btn-danger" @onclick="()=>HandleDeleteRoom(room)">Delete</button> </td>
اکنون برای مدیریت دریافت تائیدیهی حذف از کاربر، کامپوننت Confirmation را اضافه کرده:
<Confirmation @ref="Confirmation1" OnCancel="OnCancelDeleteRoomClicked" OnConfirm="OnConfirmDeleteRoomClicked"> <div> Do you want to delete @RoomToBeDeleted?.Name? </div> </Confirmation>
@code { private List<HotelRoomDTO> HotelRooms = new List<HotelRoomDTO>(); private HotelRoomDTO RoomToBeDeleted; private Confirmation Confirmation1; private void OnCancelDeleteRoomClicked() { // Confirmation1.Hide(); } private void HandleDeleteRoom(HotelRoomDTO roomDto) { RoomToBeDeleted = roomDto; Confirmation1.Show(); } private async Task OnConfirmDeleteRoomClicked() { if(RoomToBeDeleted is null) { return; } await HotelRoomService.DeleteHotelRoomAsync(RoomToBeDeleted.Id); HotelRooms.Remove(RoomToBeDeleted); // Update UI }
مشکل! این روش استفادهی از DbContext کار نمیکند!
اگر برنامه را اجرا کرده و سعی در حذف یک ردیف کنیم، به خطای زیر میرسیم:
An exception occurred while iterating over the results of a query for context type 'BlazorServer.DataAccess.ApplicationDbContext'. System.InvalidOperationException: A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-18.zip
Unit Testing در AngularJS
JavaScript is a dynamically typed language which comes with great power of expression, but it also comes with almost no help from the compiler. For this reason we feel very strongly that any code written in JavaScript needs to come with a strong set of tests. We have built many features into Angular which make testing your Angular applications easy. With Angular, there is no excuse for not testing.
افزونه .NET Portability Analyzer
- XAML designer for UWP - control properties not displayed
- UWP XAML designer doesn't update elements on updating XAML code
- XAML properties and document structure
- Properties window not showing the properties when clicking an object
- Unable to see properties of any items
- Installation error when trying to connect to the Mac: "The Xamarin.iOS version installed on 'x' (12.8.0.2) is newer than your version".
- Fixed right click solution name in titlebar VS crash bug.
- Improved performance for customers with the Azure workload installed.
- Corrected errors during restore and build on SDK-based projects that use 3rd party SDKs to target UWP platforms.
- Fixed a bug in C# compiler where it was not properly warning customers about incomplete interface implementations.
- Improved error messaging in Visual Studio Tools for Kubernetes.
- Fixed error when adding a comment in PR for SymbolCheck.