eitaa logo
پروژه ASP.Net Core MVC (وب و سی شارپ)
119 دنبال‌کننده
168 عکس
38 ویدیو
376 فایل
❁﷽❁ آموزش 📖 برنامه نویسی ASP.Net Core MVC (وب و سی شارپ) Admin: @alialirezapanahi برنامه نویسی برنامه نویسی سی شارپ eitaa.com/sisharpapp برنامه نویسی وب eitaa.com/aspdatnet ویراستی virasty.com/alialirezapanahi آپارات aparat.com/alialirezapanahi
مشاهده در ایتا
دانلود
پروژه ASP.Net Core MVC (وب و سی شارپ)
ترجمه مقادیر فنی به متن فارسی در سی شارپ کالبد شکافی الگوی enum در ASP.Net Core MVC ساخته شده از هو
بیایم این کد رو کلمه به کلمه و خط به خط بررسی کنیم تا دقیقاً بفهمی چطور enum با DisplayAttribute و EnumHelper ترکیب شده و در کنترلر استفاده شده. --- ✅ بخش اول: تعریف Enum با نام‌های نمایشی
csharp
public enum OrderStatus
{
    [Display(Name = "در انتظار")]
    Pending,

    [Display(Name = "تأیید شده")]
    Confirmed,

    [Display(Name = "ارسال شده")]
    Shipped,

    [Display(Name = "تحویل داده شده")]
    Delivered,

    [Display(Name = "لغو شده")]
    Canceled
}
🔹 enum OrderStatus تعریف یک نوع شمارشی برای وضعیت سفارش‌ها 🔹 [Display(Name = "...")] مشخص کردن نام نمایشی فارسی برای هر مقدار مثلاً OrderStatus.Pending در View به صورت «در انتظار» نمایش داده می‌شه --- ✅ بخش دوم: کلاس کمکی برای گرفتن نام نمایشی
csharp
public static class EnumHelper
{
    public static string GetDisplayName(Enum value)
    {
        var field = value.GetType().GetField(value.ToString());
        var attr = field?.GetCustomAttribute<DisplayAttribute>();
        return attr?.Name ?? value.ToString();
    }
}
🔹 GetDisplayName(Enum value) متدی برای گرفتن نام نمایشی از Enum 🔹 value.GetType().GetField(value.ToString()) دریافت اطلاعات فیلد مربوط به مقدار Enum 🔹 GetCustomAttribute<DisplayAttribute>() بررسی اینکه آیا ویژگی [Display(Name = "...")] وجود دارد یا نه 🔹 attr?.Name ?? value.ToString() اگر DisplayAttribute وجود داشت، مقدار Name را برمی‌گرداند اگر نبود، نام اصلی Enum را برمی‌گرداند (مثلاً Pending) --- ✅ بخش سوم: اکشن Index برای صفحه اصلی فروشگاه
csharp
public IActionResult Index()
{
    var recentOrders = _context.Orders
        .Include(o => o.OrderProducts)
            .ThenInclude(op => op.Product)
        .OrderByDescending(o => o.OrderDate)
        .Take(5)
        .ToList()
        .Select(o => new
        {
            o.Id,
            o.CustomerName,
            o.OrderDate,
            StatusText = EnumHelper.GetDisplayName(Enum.Parse<OrderStatus>(o.Status)),
            ProductCount = o.OrderProducts.Count,
            TotalAmount = o.OrderProducts.Sum(op => op.Product.Price * op.Quantity)
        })
        .ToList();

    var topProducts = _context.Products
        .Where(p => p.IsAvailable)
        .OrderByDescending(p => p.Price)
        .Take(5)
        .ToList();

    ViewBag.RecentOrders = recentOrders;
    ViewBag.TopProducts = topProducts;

    return View();
}
🔹 Include و ThenInclude برای بارگذاری سفارش‌ها و محصولات مرتبط 🔹 OrderByDescending(o => o.OrderDate) مرتب‌سازی سفارش‌ها از جدید به قدیم 🔹 Enum.Parse<OrderStatus>(o.Status) تبدیل رشته‌ی Status به مقدار enum از نوع OrderStatus 🔹 EnumHelper.GetDisplayName(...) گرفتن نام فارسی وضعیت سفارش 🔹 ProductCount و TotalAmount تعداد محصولات و مجموع قیمت سفارش --- ✅ بخش چهارم: اکشن OrderDetails
csharp
public IActionResult OrderDetails(int id)
{
    var order = _context.Orders
        .Include(o => o.OrderProducts)
            .ThenInclude(op => op.Product)
        .FirstOrDefault(o => o.Id == id);

    if (order == null)
        return NotFound();

    var status = OrderStatus.Shipped;
    var displayName = EnumHelper.GetDisplayName(status);
    ViewBag.StatusText = displayName;

    return View(order);
}
🔹 Include و ThenInclude بارگذاری سفارش و محصولات مرتبط 🔹 OrderStatus.Shipped مقدار Enum به‌صورت مستقیم 🔹 EnumHelper.GetDisplayName(status) گرفتن نام فارسی «ارسال شده» --- ✅ بخش پنجم: اکشن ProductDetails
csharp
public IActionResult ProductDetails(int id)
{
    var product = _context.Products.Find(id);

    if (product == null)
        return NotFound();

    return View(product);
}
🔹 Find(id) یافتن محصول با شناسه 🔹 return View(product) ارسال محصول به View برای نمایش جزئیات --- 🎯 نتیجه‌گیری - Enum با DisplayAttribute برای نمایش فارسی وضعیت‌ها استفاده شده - کلاس EnumHelper به‌صورت عمومی قابل استفاده در همه جا هست - در کنترلر، مقدار Enum از دیتابیس تبدیل می‌شه و نام نمایشی گرفته می‌شه - این ساختار هم خوانا، هم قابل توسعه، و هم مناسب برای چندزبانه‌سازیه
بیایم این سه اکشن مهم رو با مثال‌های ساده و توضیح مرحله‌به‌مرحله بررسی کنیم تا دقیقاً بفهمی چه اتفاقی می‌افته و چطور با هم کار می‌کنن. این سه اکشن مربوط به سناریویی هستن که: > کاربر می‌خواد محصولی رو ویرایش کنه، ولی اگر اون محصول در سفارش‌ها استفاده شده باشه و بخواد غیرفعالش کنه، باید اول تأیید کنه که از سفارش‌ها حذف بشه. --- ✅ ۱. اکشن [HttpPost] Edit — ویرایش محصول
csharp
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, Product updatedProduct)
🔹 هدف: ویرایش اطلاعات محصولی که شناسه‌اش id هست با داده‌های جدیدی که از فرم اومده (updatedProduct) --- 🔹 مرحله‌به‌مرحله: 1. پیدا کردن محصول اصلی از دیتابیس
csharp
   var product = _context.Products.Find(id);
   if (product == null)
       return NotFound();
   
2. بررسی تکراری بودن نام محصول
csharp
   bool exists = _context.Products
       .Any(p => p.Name == updatedProduct.Name && p.Id != id);
   if (exists)
       ModelState.AddModelError("Name", "نام محصول تکراری است.");
   
3. بررسی اینکه آیا کاربر می‌خواد محصول فعال رو غیرفعال کنه
csharp
   if (product.IsAvailable && !updatedProduct.IsAvailable)
   {
       var hasOrders = _context.OrderProducts
           .Any(op => op.ProductId == product.Id);
       if (hasOrders)
       {
           TempData["ConfirmDeleteFromOrders"] = true;
           TempData["ProductId"] = product.Id;
           return RedirectToAction("ConfirmProductRemoval");
       }
   }
   
4. اگر همه چیز درست بود، ذخیره تغییرات
csharp
   if (ModelState.IsValid)
   {
       product.Name = updatedProduct.Name;
       product.Price = updatedProduct.Price;
       product.IsAvailable = updatedProduct.IsAvailable;

       _context.SaveChanges();
       TempData["Success"] = "محصول با موفقیت ویرایش شد.";
       return RedirectToAction(nameof(Index));
   }
   
5. اگر خطا وجود داشت، نمایش فرم با خطاها
csharp
   return View(updatedProduct);
   
--- ✅ ۲. اکشن ConfirmProductRemoval — نمایش تأیید حذف از سفارش‌ها
csharp
public IActionResult ConfirmProductRemoval()
🔹 هدف: نمایش صفحه‌ای به کاربر که تأیید کنه آیا می‌خواد محصولی که در سفارش‌ها استفاده شده، حذف بشه یا نه --- 🔹 مرحله‌به‌مرحله: 1. گرفتن شناسه محصول از TempData
csharp
   int productId = (int)TempData["ProductId"];
   
2. پیدا کردن محصول از دیتابیس
csharp
   var product = _context.Products.Find(productId);
   
3. ارسال محصول به View برای نمایش اطلاعات و دکمه تأیید
csharp
   return View(product);
   
--- ✅ ۳. اکشن [HttpPost] RemoveProductFromOrders — حذف محصول از سفارش‌ها
csharp
[HttpPost]
public IActionResult RemoveProductFromOrders(int productId)
🔹 هدف: حذف همه سفارش‌هایی که شامل این محصول هستن، و سپس غیرفعال کردن محصول --- 🔹 مرحله‌به‌مرحله: 1. پیدا کردن همه سفارش‌های مربوط به محصول
csharp
   var orderItems = _context.OrderProducts
       .Where(op => op.ProductId == productId)
       .ToList();
   
2. حذف همه سفارش‌ها از جدول OrderProducts
csharp
   _context.OrderProducts.RemoveRange(orderItems);
   
3. پیدا کردن محصول و غیرفعال کردنش
csharp
   var product = _context.Products.Find(productId);
   product.IsAvailable = false;
   
4. ذخیره تغییرات در دیتابیس
csharp
   _context.SaveChanges();
   
5. نمایش پیام موفقیت و برگشت به صفحه اصلی
csharp
   TempData["Success"] = "محصول از سفارش‌ها حذف شد و غیرفعال گردید";
   return RedirectToAction("Index");
   
--- 🎯 جمع‌بندی تصویری
ویرایش محصول
   ↓
اگر غیرفعال شد و در سفارش‌ها بود
   ↓
نمایش تأییدیه حذف از سفارش‌ها
   ↓
اگر کاربر تأیید کرد
   ↓
حذف سفارش‌ها + غیرفعال کردن محصول
---
بررسی تکراری بودن نام محصول csharp bool exists = _context.Products .Any(p => p.Name == updatedProduct.Name && p.Id != id); if (exists) ModelState.AddModelError("Name", "نام محصول تکراری است."); بذار با مثال ساده و دقیق برات توضیح بدم تا کاملاً جا بیفته: --- ✅ سناریو: بررسی تکراری بودن نام محصول در اکشن ویرایش فرض کن در دیتابیس محصولی داریم با:
plaintext
Id = 1
Name = "گوشی"
حالا کاربر می‌خواد محصولی با Id = 1 رو ویرایش کنه. --- 🔹 حالت اول: کاربر نام محصول رو به "گوشی" تغییر می‌ده ولی محصول دیگری با همین نام وجود دارد
csharp
bool exists = _context.Products
    .Any(p => p.Name == updatedProduct.Name && p.Id != id);
این شرط یعنی: > آیا محصولی با همین نام وجود دارد ولی شناسه‌اش با محصول فعلی فرق دارد؟ ✅ اگر محصول دیگری با نام "گوشی" و Id ≠ 1 وجود داشته باشه، شرط exists == true می‌شه و خطا نمایش داده می‌شه: «نام محصول تکراری است.» --- 🔹 حالت دوم: کاربر فقط قیمت یا وضعیت محصول را تغییر می‌دهد و نام را همان "گوشی" باقی می‌گذارد در این حالت: - updatedProduct.Name == product.Name - و چون Id == id، شرط p.Id != id باعث می‌شه خود محصول فعلی در بررسی تکراری بودن محاسبه نشه ✅ بنابراین شرط exists == false می‌شه ✅ و خطای "نام محصول تکراری است" نمایش داده نمی‌شه --- 🎯 نتیجه‌گیری | وضعیت | آیا خطا نمایش داده می‌شود؟ | |-------|-----------------------------| | تغییر نام به نام محصول دیگری | ✅ بله، چون نام تکراری است و شناسه فرق دارد | | نام محصول تغییر نکرده | ❌ نه، چون خودش است و تکراری محسوب نمی‌شود |
Shop & Orders (4).zip
حجم: 24.9M
حذف @functions و آوردن تابع کمکی در کنترلر order
پروژه ASP.Net Core MVC (وب و سی شارپ)
حذف @functions و آوردن تابع کمکی در کنترلر order
بیایم خیلی خلاصه و دقیق فقط روی تغییرات لازم تمرکز کنیم، نه توضیح تئوری: --- ## ✅ تغییر در @functions - ❌ حذف @functions از View - ✅ تبدیل تابع
GetStatusList()
به تابع
private
در کنترلر - ✅ استفاده از خروجی تابع در ViewBag.StatusList --- ## ✅ تغییر در اکشن
Edit
### در حالت
GET
:
public IActionResult Edit(int id)
{
    var order = _context.Orders
        .Include(o => o.OrderProducts)
        .FirstOrDefault(o => o.Id == id);

    if (order == null)
        return NotFound();

    LoadFormData(); // ✅ آماده‌سازی لیست‌ها

    return View(order);
}
### در حالت
POST
:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, Order updatedOrder, List<int> selectedProductIds)
{
    var order = _context.Orders
        .Include(o => o.OrderProducts)
        .FirstOrDefault(o => o.Id == id);

    if (order == null)
        return NotFound();

    if (ModelState.IsValid)
    {
        // به‌روزرسانی سفارش
        ...
        _context.SaveChanges();
        TempData["Success"] = "سفارش با موفقیت ویرایش شد.";
        return RedirectToAction(nameof(Index));
    }

    LoadFormData(); // ✅ نمایش مجدد فرم با لیست‌ها

    return View(updatedOrder);
}
--- ## ✅ تابع کمکی در کنترلر
private void LoadFormData()
{
    ViewBag.Products = _context.Products
        .Where(p => p.IsAvailable)
        .OrderBy(p => p.Name)
        .ToList();

    ViewBag.StatusList = Enum.GetValues(typeof(OrderStatus))
        .Cast<OrderStatus>()
        .Select(s => new SelectListItem
        {
            Value = s.ToString(),
            Text = s.GetType()
                .GetMember(s.ToString())
                .First()
                .GetCustomAttribute<DisplayAttribute>()?.Name ?? s.ToString()
        }).ToList();
}
--- ## ✅ تغییر در View
<select asp-for="Status" class="form-select" asp-items="ViewBag.StatusList"></select>
--- این تغییرات باعث می‌شن کدت تمیزتر، قابل نگهداری‌تر و حرفه‌ای‌تر باشه.
پروژه ASP.Net Core MVC (وب و سی شارپ)
حذف @functions و آوردن تابع کمکی در کنترلر order
اگه همه منطق‌ها رو در یک کلاس مشترک قرار بدی و هم در اکشن GET و هم در POST صدا بزنی، این کار کاملاً قابل قبول و رایج هست — مخصوصاً در مراحل ابتدایی جداسازی منطق از کنترلر. --- ✅ مزایای کلاس مشترک برای GET و POST - کاهش تکرار کد: مثلاً تابع LoadFormData() یا GetStatusList() فقط یک‌بار نوشته می‌شه - افزایش خوانایی کنترلر: کنترلر فقط صدا می‌زنه، خودش درگیر جزئیات نیست - آمادگی برای جداسازی بیشتر: بعداً می‌تونی همین کلاس رو به سرویس یا لایه‌های CQRS تبدیل کنی - تست‌پذیری بهتر: کلاس جدا راحت‌تر تست می‌شه نسبت به کد داخل کنترلر --- ⚠️ نکته مهم اگر کلاس مشترک خیلی بزرگ بشه و هم منطق خواندن (GET) و هم منطق نوشتن (POST) رو با هم نگه داره، ممکنه: - وابستگی زیاد بین متدها ایجاد بشه - تست‌پذیری سخت‌تر بشه - اصل Single Responsibility نقض بشه --- ✅ راه‌حل حرفه‌ای‌تر در آینده وقتی آماده شدی برای معماری تمیز‌تر، می‌تونی: - کلاس رو به دو بخش تقسیم کنی: - OrderQueryService برای GET - OrderCommandService برای POST یا حتی با CQRS: - EditOrderQueryHandler برای گرفتن اطلاعات - EditOrderCommandHandler برای ذخیره تغییرات --- 🎯 نتیجه الان که در مرحله توسعه هستی، کلاس مشترک کاملاً خوبه و منطقیه. بعداً که پروژه بزرگ‌تر شد، می‌تونی با تقسیم وظایف و CQRS ساختار حرفه‌ای‌تری بسازی.
SmartShopPlus.zip
حجم: 25.3M
ترکیب سه پروژه قبل به یک پروژه + زبان نکته : پروژه در ظاهر اول نگاه کنی ظاهر خوبی ندارد ولی پروژه فقط برای آموزش ساخته شده و اگر کامل نیست خودتون کامل کنید مخصوصا زبان که فقط یک کلمه را ترجمه می کند و کل سایت را خودتون ترجمه و @inject SmartShopPlus.Service.ILanguageService Lang در بالای ویو <h1>@Lang.Translate("Dashboard.Title")</h1> در همه جا قرار دهید و ترجمه کنید
SmartShopPlus (3).zip
حجم: 25.5M
افزودن دیکشنری و تغییر زبان و عوض شدن محتوا داده
پروژه ASP.Net Core MVC (وب و سی شارپ)
افزودن دیکشنری و تغییر زبان و عوض شدن محتوا داده
هدف اینه که مخاطب بفهمه فرق بین LanguageHelper و LanguageService چیه، کدوم برای چه شرایطی بهتره، و اینکه لزومی نداره هر دو رو همزمان داشته باشه. --- 🎯 تفاوت LanguageHelper و LanguageService در پروژه‌های چندزبانه MVC در پروژه‌های چندزبانه، معمولاً نیاز داریم متن‌ها رو بر اساس زبان انتخاب‌شده توسط کاربر نمایش بدیم. برای این کار دو روش رایج وجود داره: --- ✅ ۱. LanguageHelper (کلاس استاتیک)
csharp
public static class LanguageHelper
{
    public static string Translate(ApplicationDbContext context, IHttpContextAccessor httpContext, string key)
    {
        var culture = httpContext.HttpContext.Request.Cookies["lang"] ?? "fa";

        var resource = context.LanguageResources
            .FirstOrDefault(r => r.Key == key && r.Culture == culture);

        return resource?.Value ?? key;
    }
}
📌 ویژگی‌ها: - استاتیکه، یعنی نیازی به ساخت نمونه (instance) نداره - در کنترلرها و کلاس‌ها قابل استفاده‌ست - باید هر بار context و httpContext رو دستی بهش بدی - در Razor View مستقیم قابل استفاده نیست (مگر با ViewBag یا ViewData) --- ✅ ۲. LanguageService (سرویس تزریقی)
csharp
public class LanguageService : ILanguageService
{
    private readonly ApplicationDbContext _context;
    private readonly IHttpContextAccessor _http;

    public LanguageService(ApplicationDbContext context, IHttpContextAccessor http)
    {
        _context = context;
        _http = http;
    }

    public string Translate(string key)
    {
        var culture = _http.HttpContext?.Request.Cookies["lang"] ?? "fa";

        var value = _context.LanguageResources
            .Where(x => x.Key == key && x.Culture == culture)
            .Select(x => x.Value)
            .FirstOrDefault();

        return value ?? $"[{key}]";
    }
}
📌 استفاده در Razor View:
razor
@inject ILanguageService Lang
<h1>@Lang.Translate("Dashboard.Title")</h1>
📌 ویژگی‌ها: - از طریق DI (Dependency Injection) تزریق می‌شه - در Razor View مستقیم قابل استفاده‌ست - نیازی به ارسال دستی context و http نداره - قابل تست، توسعه و مدیریت بهتره --- ⚖️ مقایسه نهایی | ویژگی | LanguageHelper | LanguageService | |--------------------------|----------------|-----------------| | سادگی اولیه | ✅ ساده | ❌ کمی پیچیده‌تر | | استفاده در View | ❌ سخت | ✅ راحت با @inject | | نیاز به DI | ❌ ندارد | ✅ دارد | | توسعه‌پذیری | ❌ محدود | ✅ قابل گسترش | | مناسب پروژه‌های بزرگ | ❌ نه | ✅ بله | --- ✅ کدام را انتخاب کنیم؟ 🔹 اگر پروژه‌ات ساده است یا فقط در کنترلرها ترجمه نیاز داری → LanguageHelper کافیست 🔹 اگر پروژه‌ات چندزبانه، قابل توسعه و حرفه‌ای است → LanguageService بهتر و استانداردتر است ❗ لزومی نداره هر دو را همزمان داشته باشی فقط یکی را انتخاب کن و در کل پروژه از همان استفاده کن تا کدت تمیز و منسجم بماند.