[Required(ErrorMessage = "قیمت محصول الزامی است")]
[Range(1, 1000000, ErrorMessage = "قیمت باید بین ۱ تا ۱,۰۰۰,۰۰۰ باشد.")]
[Column(TypeName = "decimal(18,2)")]
[Display(Name = ":قیمت")]
public decimal Price { get; set; }
بیایم هر کد رو جداگانه بررسی کنیم تا تفاوتشون کاملاً برات روشن بشه:
---
🧮 1. [Column(TypeName = "decimal(18,2)")]
این خط مربوط به تنظیم نوع ستون در دیتابیس هست:
- decimal(18,2) یعنی:
- مجموعاً ۱۸ رقم
- ۲ رقم بعد از اعشار
- یعنی میتونی عددی مثل 1234567890123456.78 ذخیره کنی
📌 این فقط روی ساختار دیتابیس تأثیر داره، نه روی اعتبارسنجی فرم یا محدودیتهای ورود کاربر.
---
✅ 2. [Range(1, 1000000, ErrorMessage = "...")]
این خط مربوط به اعتبارسنجی در فرم و سمت سرور هست:
- یعنی کاربر فقط میتونه عددی بین ۱ تا ۱,۰۰۰,۰۰۰ وارد کنه
- اگر عددی خارج از این بازه وارد بشه، پیام خطا نمایش داده میشه
📌 این فقط روی ورودی کاربر تأثیر داره، نه روی نوع ستون در دیتابیس.
---
🧠 نتیجهگیری
| ویژگی | مربوط به | توضیح |
|-------|-----------|--------|
| decimal(18,2) |
دیتابیس | ظرفیت ذخیره عدد بزرگ با دقت بالا |
| Range(1,1000000) |
اعتبارسنجی | محدودیت ورود عدد توسط کاربر |
🔹 پس حتی اگر دیتابیس اجازه بده عددی مثل 9999999999999999.99 ذخیره بشه، اعتبارسنجی فرم جلوی ورود عدد بالاتر از 1,000,000 رو میگیره.
پروژه ASP.Net Core MVC (وب و سی شارپ)
[Required(ErrorMessage = "قیمت محصول الزامی است")] [Range(1, 1000000, ErrorMessage = "قیمت باید بین
اگر منظورت اینه که در این خط:
csharp [Column(TypeName = "decimal(18,2)")]عدد ۱۸ رو به ۸ تغییر بدیم و بشه:
csharp [Column(TypeName = "decimal(8,2)")]بله، میتونی این کار رو انجام بدی، ولی باید بدونی دقیقاً چه تأثیری داره: --- 🔍 تفاوت decimal(18,2) با decimal(8,2) | نوع | توضیح | |-----|-------| | decimal(18,2) | عددی با حداکثر ۱۸ رقم، که ۲ رقم آن بعد از اعشار است. یعنی تا 9999999999999999.99 قابل ذخیره است. | | decimal(8,2) | عددی با حداکثر ۸ رقم، که ۲ رقم آن بعد از اعشار است. یعنی تا 999999.99 قابل ذخیره است. | 🔹 پس اگر از decimal(8,2) استفاده کنی، بیشترین مقدار قابل ذخیره میشه حدود ۹۹۹ هزار تومان 🔹 اگر محصولاتی با قیمت بالاتر داری، ممکنه خطای overflow یا ذخیره ناقص رخ بده --- ✅ پیشنهاد من چون در اعتبارسنجی Range(1, 1000000) استفاده کردی، decimal(8,2) کفایت میکنه و با اون بازه هماهنگه. پس بله، میتونی با خیال راحت عدد ۱۸ رو به ۸ تغییر بدی تا فضای دیتابیس بهینهتر بشه.
مشکل اینجاست که وقتی عدد خیلی بزرگ وارد میکنی، قبل از اینکه اعتبارسنجی
[Range]فرصت بررسی داشته باشه، مدلسازی (`Model Binding`) در ASP.NET Core با خطای `OverflowException` متوقف میشه**—چون عدد از محدودهی `int` یا `decimal` خارج شده و تبدیل مستقیم شکست میخوره. --- ## ✅ راهحل قطعی: اعتبارسنجی قبل از تبدیل برای اینکه اعتبارسنجی واقعاً کار کنه و خطای سرریز رخ نده، باید: --- ### 1. **نوع فیلد ورودی را از `int` به `string` تغییر بده در مدل ویو (ViewModel) یا مدل فرم، فیلد عددی را به
stringتبدیل کن تا اول اعتبارسنجی انجام بشه، بعد تبدیل:
[Required]
[RegularExpression(@"^\d{1,7}$", ErrorMessage = "عدد باید بین ۱ تا ۹۹۹۹۹۹۹ باشد.")]
public string QuantityInput { get; set; }
🔹 این یعنی فقط عددهای تا ۷ رقم مجاز هستن
🔹 هیچ تبدیل به intانجام نمیشه تا زمانی که خودت کنترل کنی --- ### 2. در کنترلر، تبدیل امن انجام بده
if (!ModelState.IsValid) { return View(model); // ✅ نمایش پیام خطا } if (!int.TryParse(model.QuantityInput, out int quantity)) { ModelState.AddModelError("QuantityInput", "عدد وارد شده معتبر نیست."); return View(model); } if (quantity < 1 || quantity > 1000000) { ModelState.AddModelError("QuantityInput", "عدد باید بین ۱ تا ۱,۰۰۰,۰۰۰ باشد."); return View(model); } // ✅ حالا میتونی با quantity ادامه بدی--- ### 3. در View از `Html.ValidationMessageFor` استفاده کن
<div class="form-group">
<label for="QuantityInput">تعداد</label>
<input asp-for="QuantityInput" class="form-control" />
<span asp-validation-for="QuantityInput" class="text-danger"></span>
</div>
---
## 🎯 نتیجه نهایی
- عدد خیلی بزرگ باعث خطای سرریز نمیشه چون اول بهصورت stringگرفته میشه - اعتبارسنجی کامل انجام میشه - پیام خطا بهصورت زیبا نمایش داده میشه - هیچ خطای
Unhandled Exceptionیا
OverflowExceptionنمیبینی
SmartReports.zip
حجم:
25.3M
پروژه داشبورد گزارشگیری و نمودارها
پروژه ASP.Net Core MVC (وب و سی شارپ)
پروژه داشبورد گزارشگیری و نمودارها
> پروژه : SmartReports - داشبورد گزارشگیری و نمودارها
---
## ✅ لیست کامل پروژه سوم و کارهایی که انجام دادیم
### 1. هدف پروژه
ساخت یک داشبورد گزارشگیری با تمرکز بر:
- سفارشها، مشتریان، محصولات
- نمایش نمودارهای آماری با Chart.js
- طراحی واکنشگرا با Bootstrap
- اجرای آفلاین با فایلهای محلی JS و CSS
---
### 2. ساختار پروژه
| بخش | توضیحات |
|-----|---------|
|
Controllers/ReportsController.cs| کنترلر اصلی گزارشها شامل اکشنهای روزانه، مشتریان، محصولات | |
Models/Report/*.cs| مدلهای گزارش مثل
DailyOrderReport,
TopProductReport,
CustomerActivityReport| |
Views/Reports/*.cshtml| ویوهای گزارشها و نمودارها | |
wwwroot/js/| فایلهای آفلاین: chart.js,
jquery v3.6.0.js,
bootstrap v4.6.2.js| |
wwwroot/css/| فایل آفلاین: bootstrap-v4.6.2.css | | Layout.cshtml | اضافه کردن فایلهای آفلاین و ساخت ظاهر حرفهای | | appsettings.json | اتصال به دیتابیس و تنظیمات پروژه | --- ### 3. اکشنهایی که نوشتیم #### 🔹
DailyOrders()- گروهبندی سفارشها بر اساس تاریخ - محاسبه تعداد سفارش و مجموع قیمت - استفاده از
Include(o => o.Products)- اضافه کردن
AsNoTracking()برای بهینهسازی - ارسال دادهها به View برای نمایش نمودار #### 🔹
TopProducts()- نمایش محصولات پرفروش بر اساس تعداد سفارش - ساخت مدل
TopProductReport- مرتبسازی نزولی بر اساس فروش #### 🔹
CustomerActivity()و
ActiveCustomers()- نمایش همه مشتریان و مشتریان فعال - محاسبه تعداد سفارش و آخرین تاریخ سفارش - ارسال عنوان پویا با ViewBag.Title --- ### 4. نمودارها با Chart.js - ساخت نمودار ستونی برای سفارشها - رنگبندی سفارشی ستونها - نمایش تعداد سفارش بالای هر ستون با
DataLabels- حذف مجموع از Tooltip و نمایش فقط مقدار هر ستون - عنوان پویا مثل:
نمودار تعداد سفارشها — مجموع: ۱۰ عدد--- ### 5. رفع خطاها و بهینهسازیها - اضافه کردن
AsNoTracking()برای جلوگیری از ردیابی اضافی - اصلاح
decimal(8,2)به
decimal(18,2)برای جلوگیری از خطای Overflow - بررسی خطای
OverflowExceptionو تبدیل امن عددهای بزرگ - استفاده از
stringیا
double?برای ورودی عددی با کنترل دستی - طراحی فوتر با Bootstrap که روی دکمهها نیفته - تنظیم فایلهای آفلاین برای اجرای بدون اینترنت --- ### 6. فایلهای آفلاین که استفاده کردی
<!-- CSS --> <link href="~/css/bootstrap-v4.6.2.css" rel="stylesheet" /> <!-- JS --> <script src="~/js/jquery v3.6.0.js"></script> <script src="~/js/bootstrap v4.6.2.js"></script> <script src="~/js/chart.js"></script>--- ## 🎯 نتیجه نهایی پروژه سوم - داشبورد گزارشگیری کامل و قابل توسعه - نمودارهای حرفهای و قابل شخصیسازی - اجرای آفلاین بدون نیاز به CDN - رعایت اصول معماری MVC و بهینهسازی EF Core - پایهای برای آموزش تولید محتوا و پروژههای واقعی
زمان:
حجم:
5.4M
ترجمه مقادیر فنی به متن فارسی در سی شارپ کالبد شکافی الگوی enum در ASP.Net Core MVC
ساخته شده از هوش مصنوعی
پروژه 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 میشه ✅ و خطای "نام محصول تکراری است" نمایش داده نمیشه --- 🎯 نتیجهگیری | وضعیت | آیا خطا نمایش داده میشود؟ | |-------|-----------------------------| | تغییر نام به نام محصول دیگری | ✅ بله، چون نام تکراری است و شناسه فرق دارد | | نام محصول تغییر نکرده | ❌ نه، چون خودش است و تکراری محسوب نمیشود |
پروژه ASP.Net Core MVC (وب و سی شارپ)
بیایم این سه اکشن مهم رو با مثالهای ساده و توضیح مرحلهبهمرحله بررسی کنیم تا دقیقاً بفهمی چه اتفاق
زمان:
حجم:
3.8M
مدیریت محصول در پروژه های وب رمزگشایی منطق C# برای ویرایش در ASP.Net Core MVC
ساخته شده از هوش مصنوعی
پروژه محصول و سفارش
https://eitaa.com/projectall/1200
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>--- این تغییرات باعث میشن کدت تمیزتر، قابل نگهداریتر و حرفهایتر باشه.