We are thrilled to announce the highly anticipated .NET Conf 2024, a free, three-day virtual developer event celebrating the release of .NET 9. Co-organized by the .NET community and Microsoft, this annual tradition continues to grow, and we’re more excited than ever to bring you the latest innovations in .NET. Mark your calendars for November 12th to 14th, 2024, and prepare to be inspired by a wealth of knowledge, creativity, and community engagement.
نحوه اضافه کردن چند پروژه در vs code
Visual Studio Code doesn't support solution files, whereas Visual Studio does. In Visual Studio we use solution files to link multiple projects together, and at first I thought that this was going to be a deal breaker for Visual Studio Code - but it's not at all. Many people aren't aware that Visual Studio Code, although lightweight, is a powerful editor that can do most of the things you need.
ASP.NET Core 2.0 منتشر شد
The ASP.NET team is proud to announce general availability of ASP.NET Core 2.0. This release features compatibility with .NET Core 2.0, tooling support in Visual Studio 2017 version 15.3, and the new Razor Pages user-interface design paradigm. For a full list of updates, you can read the release notes. The latest SDK and tools can be downloaded from https://dot.net/core. Read the .NET Core 2.0 release announcement for more information and watch the launch video on Channel 9.
معرفی قابلیت Hot Restart زامارین
توسط این قابلیت در برنامههای زاماین، تغییرات کد، رفرنس ها، فایلها و... نیاز به کامپایل کامل و مجدد نداشته و تغییرات را سریعتر اعمال کرده و برنامه را ریستارت میکند.
Today at .NET Conf 2019, we announced Xamarin Hot Restart which enables you to test changes made to your app, including multi-file code edits, resources, and references, using a much faster build and deploy cycle.
XAML Hot Reload for Xamarin.Forms already provides fast iteration on XAML UIs by enabling you to see changes applied live in your running application. But what about other types of code and project edits? Xamarin Hot Restart will apply these types of changes to your application and quickly restart your app for rapid application development.
مشکل حذف تصاویر آپلود شده
در قسمت قبل، این امکان را مهیا کردیم که کاربران بتوانند پیش از ثبت اطلاعات یک اتاق، تصاویر آنرا به سرور آپلود کنند. یعنی تصاویری که در ابتدای کار آپلود میشوند، هنوز در بانک اطلاعاتی ثبت نشدهاند و هیچ رکوردی از آنها موجود نیست. در این حالت اگر کاربری تصاویری را آپلود کرده و سپس بر روی دکمهی 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
10 مرحله برای یادگیری پرسرعت هر چیزی
To Every Programmer Who’s Ever Scanned
Hacker News And /r/programming And Thought...
“How Will I Ever Keep Up?”
Here’s How To Turn “Information Overwhelm”
Into An Efficiency Edge That Can
Quickly Boost Your Income,
Earn You “MVP” Status With Your Team,
And Make You The In-Demand Developer
Companies Are Dying To Recruit