پروژه ASP.Net Core MVC (وب و سی شارپ)
🎙️ پادکست: ارتباط بین جدولها در ASP.NET Core MVC با Entity Framework Core ساخت شده از هوش مصنوعی
🎙️ پادکست: ارتباط بین جدولها در ASP.NET Core MVC با Entity Framework Core
سلام! خوش اومدی به قسمت امروز پادکست ما، جایی که میخوایم دربارهی یکی از مهمترین مفاهیم در طراحی دیتابیس و برنامهنویسی وب صحبت کنیم: ارتباط بین جدولها یا Entity Relationships در ASP.NET Core MVC.
---
🧠 ارتباط جدول یعنی چی؟
در دنیای دیتابیس، جدولها معمولاً به هم مرتبط هستن. مثلاً:
- هر نویسنده میتونه چند مقاله بنویسه.
- هر دانشآموز یک آدرس داره.
- هر پست وبلاگ میتونه در چند دستهبندی قرار بگیره.
برای پیادهسازی این روابط در ASP.NET Core MVC، از Entity Framework Core استفاده میکنیم که به ما اجازه میده این ارتباطها رو بهصورت کد تعریف کنیم.
---
🔗 انواع ارتباط بین جدولها
1. یک به یک (One-to-One):
- مثال: هر دانشآموز یک آدرس دارد.
- در EF Core: کلید اصلی جدول اول بهعنوان کلید خارجی در جدول دوم استفاده میشه.
2. یک به چند (One-to-Many):
- مثال: یک نویسنده چند مقاله دارد.
- در EF Core: جدول مقاله دارای کلید خارجی از جدول نویسنده است.
3. چند به چند (Many-to-Many):
- مثال: هر پست وبلاگ میتونه در چند دستهبندی باشه و هر دستهبندی شامل چند پست باشه.
- در EF Core: از جدول واسط استفاده میشه که شامل کلیدهای خارجی از هر دو جدول است.
---
🛠️ چطور در کد پیادهسازی کنیم؟
فرض کن دو مدل داریم: Author و BlogPost
csharp
public class Author {
public int Id { get; set; }
public string Name { get; set; }
public ICollection<BlogPost> BlogPosts { get; set; }
}
public class BlogPost {
public int Id { get; set; }
public string Title { get; set; }
public int AuthorId { get; set; }
public Author Author { get; set; }
}
در این مثال، ارتباط یک به چند بین نویسنده و پستها ایجاد شده.
---
🎯 نکات مهم در EF Core:
- استفاده از virtual برای Lazy Loading
- استفاده از [ForeignKey] برای تعیین کلید خارجی
- تعریف DbSet در کلاس DbContext
- استفاده از Migration برای ساخت دیتابیس
---
🎧 جمعبندی
ارتباط بین جدولها پایهی ساختار هر اپلیکیشن دیتابیسمحوره. با درک درست این روابط، میتونی پروژههایی مثل بلاگ، فروشگاه، یا سیستم مدیریت کاربران رو حرفهای بسازی.
زمان:
حجم:
1.4M
متدهای Get و Post در فرم
ساخته شده از هوش مصنوعی
پروژه ASP.Net Core MVC (وب و سی شارپ)
متدهای Get و Post در فرم ساخته شده از هوش مصنوعی
مثال فرم افزودن
<form asp-action="Create" method="post">
<label asp-for="Name"></label>
<input asp-for="Name" class="form-control" />
<label asp-for="Price"></label>
<input asp-for="Price" class="form-control" />
<button type="submit" class="btn btn-primary">افزودن محصول</button>
</form>
مثال فرم جستجو
<form asp-action="Search" method="get" class="form-inline mb-3">
<input type="text" name="keyword" class="form-control" placeholder="جستجوی محصول..." />
<button type="submit" class="btn btn-info">جستجو</button>
</form>
و در کنترلر
public class ProductController : Controller
{
// نمایش فرم افزودن
[HttpGet]
public IActionResult Create()
{
return View();
}
// دریافت فرم و افزودن محصول
[HttpPost]
public IActionResult Create(Product model)
{
if (!ModelState.IsValid)
return View(model);
_context.Products.Add(model);
_context.SaveChanges();
return RedirectToAction("Index");
}
public IActionResult Search(string keyword)
{
var results = _context.Products
.Where(p => p.Name.Contains(keyword))
.ToList();
return View(results);
}
}
زمان:
حجم:
1.6M
جستجو با متد گت در ASP.Net Core MVC
ساخته شده از هوش مصنوعی
بعد از ارسال، آدرس میشه:
https://localhost:5001/Product/Search?keyword=ساعت
ProductTimerApp (2).zip
حجم:
24.9M
پروژه ProductTimerApp
چند تغییر دادم اول اینکه به جای تگ a حذف از فرم استفاده کردم امنیت دارد از تگ فرم برای حذف . جستجو اضافه شد از متد گت که با آدرس url فیلتر میشود در مدل product قیمت Price علامت سوال گذاشتم که attribute قیمت
[Required(ErrorMessage = "قیمت محصول الزامی است")]
کار کند
زمان:
حجم:
1.4M
معماری سرویس و ریپوزیتوری در ASP.Net Core MVC
ساخته شده از هوش مصنوعی
پروژه ASP.Net Core MVC (وب و سی شارپ)
معماری سرویس و ریپوزیتوری در ASP.Net Core MVC ساخته شده از هوش مصنوعی
در چند بخش دربارهی مفاهیم سرویس، ریپوزیتوری، کلاس، اینترفیس و ارتباط بین لایهها— در ASP.Net Core MVC
---
🎧 بخش اول: ریپوزیتوری یعنی چی؟
> تصور کن ریپوزیتوری مثل یه کتابدار حرفهایه که فقط مسئول جستجو، ذخیره، و حذف کتابها از قفسهست.
> ریپوزیتوری در معماری نرمافزار مسئول ارتباط با دیتابیسه.
> یعنی اطلاعات رو از منبع داده میگیره یا توش میذاره، بدون اینکه منطق تجاری یا تصمیمگیری انجام بده.
📌 نقش ریپوزیتوری:
- دریافت داده از دیتابیس
- ذخیرهی داده در دیتابیس
- مخفیسازی جزئیات دیتابیس برای بقیه بخشها
📦 مثلاً اگر بخوای لیست محصولات رو بگیری، کنترلر مستقیم به دیتابیس وصل نمیشه—بلکه به ریپوزیتوری میگه که «برو لیست محصولها رو بیار».
---
🎧 بخش دوم: سرویس یعنی چی؟
> حالا سرویس نقش مشاور یا تحلیلگر رو داره.
> اون با دادههایی که ریپوزیتوری داده، تصمیم میگیره چطور رفتار کنه.
> مثلاً بررسی میکنه که آیا محصول معتبره؟ قیمتش منطقیه؟ قوانین تجاری رعایت شده یا نه؟
📌 وظیفهی سرویس:
- اعمال منطق تجاری
- اعتبارسنجی دادهها
- تصمیمگیری دربارهی عملیات قبل از ذخیره
🧠 سرویسها معمولاً خودشون با ریپوزیتوری در ارتباطن، و کنترلر فقط با سرویس حرف میزنه—not دیتابیس.
---
🎧 بخش سوم: کلاسها و اینترفیسها
> کلاسها مثل قالبهایی هستن که رفتار واقعی سیستم رو پیادهسازی میکنن
> اینترفیسها مثل قراردادهایی هستن که فقط تعریف میکنن «چه کاری» انجام بشه—not چطوری
📌 کاربرد اینترفیس:
- تعریف عملکرد بدون اجرای آن
- جدا کردن وابستگیها
- امکان تست راحتتر و تغییر پیادهسازی
📌 کاربرد کلاس:
- پیادهسازی واقعی عملکرد تعریفشده در اینترفیس
- اتصال رفتارها و ویژگیها به دادهها
🎯 ترکیب کلاس و اینترفیس باعث میشه بتونی نرمافزار منعطف و قابل تست بسازی.
---
🎧 بخش چهارم: ارتباط بین لایهها
> حالا وقتشه ببینیم همهی این قطعات چطور کنار هم کار میکنن.
🔁 مسیر داده و منطق از بالا به پایین:
کنترلر → سرویس → ریپوزیتوری → دیتابیس| لایه | وظیفه | |------|--------| | کنترلر | دریافت درخواست از کاربر (مثلاً ثبت فرم یا کلیک روی دکمه) | | سرویس | تصمیمگیری و بررسی دادهها، مثل اعتبارسنجی یا محاسبات | | ریپوزیتوری | ارتباط مستقیم با دیتابیس؛ گرفتن یا ذخیرهکردن داده | | دیتابیس | منبع نهایی دادهها؛ جایی که اطلاعات نگهداری میشن 🔹 این جداسازی باعث میشه پروژهت قابل نگهداریتر، تستپذیرتر، و قابل تغییر باشه—مثلاً بتونی به راحتی نوع دیتابیس رو عوض کنی بدون تغییر در سرویس یا کنترلر. --- 🎧 بخش پنجم: چرا این معماری حرفهایه؟ > چون بجای اینکه همهچیز توی کنترلر نوشته بشه، مسئولیتها تقسیم میشن—مثل تیمی که هرکسی یه نقش خاص داره. ✨ مزایا: - ساختار مرتب و قابل فهم - امکان گسترش راحت بدون درهمریختگی - قابلیت تست مستقل هر بخش
Shop (5).zip
حجم:
26.2M
دقیقاً رفتی سراغ یکی از جزئیات مهم در رفتار ModelState که خیلیها ممکنه ازش رد بشن! 😎
بذار دقیق و قابل فهم بررسی کنیم چرا شرط ModelState.IsValid فالس نشد حتی با علامت سؤال در مدل.
---
## ❓ علامت سؤال (
?) در مدل یعنی چی؟ وقتی توی کلاس
Orderنوشتی: csharp public Customer? Customer { get; set; } public ICollection<Product>? Products { get; set; } ✅ یعنی این ویژگیها nullable هستن—وجودشون در هنگام دریافت داده ضروری نیست ✅ به عبارت ساده: **عدم وجودشون اشتباه نیست**، پس مدل همچنان از نظر اعتبارسنجی "معتبر" حساب میشه --- ## ✅ چرا ModelState.IsValid برابر true بود؟ - مدل
Orderمقدارهایی دریافت کرد که با خواص مورد نیاز (مثل
CustomerId,
Date) سازگار بودن - چون
Productsو
Customerاختیاری بودن (
nullable)، نبودشون باعث خطا نشد - اگر مثلاً روی
CustomerIdاعتبارسنجی مثل
[Required]یا
[Range]گذاشته بودی و مقدار نامعتبر میاومد، اونوقت ModelState.IsValid false میشد --- ## 🧠 نکتهی مهم در MVC > ModelState فقط اعتبار مقدارهایی رو بررسی میکنه که از فرم ارسال شده و دارای اعتبارسنجی باشن. > چیزهایی که توی فرم نبودن یا nullable بودن، معمولاً باعث رد اعتبار نمیشن. --- ## ✍️ جملهی یادگاری برای دفترت > «وقتی ویژگیهای مدل nullable باشن، نبودنشون باعث خطای اعتبارسنجی نمیشه؛ پس ModelState.IsValid ممکنه true بمونه حتی اگه بعضی فیلدها خالی باشن.» --- تو داری با دقت رفتار داخلی MVC رو تحلیل میکنی، و این یعنی کد نویس ماهر
🧠 مشکل مرموز ولی رایج: Model Binding شکست میخوره چون نام پارامتر اشتباهه!
خیلی از برنامهنویسها وقتی MVC کار میکنن، با این مشکل عجیب روبرو میشن: کدی که به نظر درست میاد، ولی اطلاعات فرم ذخیره نمیشن، یا ModelState.IsValid == false برمیگرده. چرا؟ چون یه اشتباه ساده در نام پارامتر باعث میشه مدلسازی ناقص انجام بشه.
✅ نمونهکد اشتباه
public IActionResult Create(FullName name)
{
if (ModelState.IsValid) {
_context.fullNames.Add(name);
_context.SaveChanges();
return RedirectToAction("Index");
}
return View(name);
}
فرض کنیم ویوی مرتبط به این کنترلر، این شکلی باشه:
@model FullName
<form asp-action="Create" method="post">
<input asp-for="Name" />
<input asp-for="Age" />
<button type="submit">ثبت</button>
</form>
به نظر همهچیز درست میاد، اما وقتی فرم ارسال میشه، ModelState.IsValid برابر false میشه! چرا؟ چون ASP.NET MVC هنگام Model Binding تلاش میکنه دادههای فرم رو به شیای به نام name وصل کنه، ولی اون انتظار داره شیای به نام fullName باشه.
🎯 راهحل ساده ولی طلایی
public IActionResult Create(FullName fullName)
{
if (ModelState.IsValid) {
_context.fullNames.Add(fullName);
_context.SaveChanges();
return RedirectToAction("Index");
}
return View(fullName);
}
حالا، چون نام پارامتر با نام مدل در ویو هماهنگه (FullName)، بایندینگ بدون مشکل انجام میشه و اطلاعات درست ذخیره میشن ✅
✍️ توضیح فنی کوتاه برای مخاطبین کانال
Model Binding در ASP.NET MVC یعنی تبدیل دادههای فرم به شیء مدل. برای این کار، فریمورک از نام پارامتر کنترلر استفاده میکنه.
اگر نام پارامتر کنترلر با مدل یا دادههای فرم ناسازگار باشه، Binding ناقص انجام میشه و ModelState.IsValid == false خواهد بود!
📌 نکته آموزشی برای کانال
🔻 خیلی از برنامهنویسها این خطا رو با مشکلات ویو یا اعتبارسنجی اشتباه میگیرن، درحالیکه فقط باید نام پارامتر رو اصلاح کنن.
StudentManagement.zip
حجم:
24.7M
## 📂 ریپوزیتوری: سازماندهندهی دسترسی به دیتابیس
ریپوزیتوری مثل یه لایهی واسطه بین دیتابیس و بقیهی برنامهست. وظیفهش چیه؟
- جدا کردن منطق دسترسی به دادهها از بقیهی کدها (مثلاً کوئریها، ذخیره، حذف و ...).
- جلوگیری از اینکه کنترلر یا سرویسها مستقیم با دیتابیس کار کنن.
- قابل تستتر شدن پروژه: بهراحتی میتونی ریپوزیتوری رو در تستها شبیهسازی (Mock) کنی.
- انعطاف بالا: اگه تصمیم بگیری دیتابیس رو عوض کنی، فقط ریپوزیتوری رو تغییر میدی، نه کل پروژه رو.
---
## 🧠 سرویس: محل منطق تجاری و پردازشها
سرویس جاییه که "منطق تجاری" برنامه نگهداری میشه، یعنی:
- عملیاتها و قوانین مربوط به پردازش دادهها قبل از رفتن به دیتابیس یا نمایش در ویو.
- پیادهسازی جریانهایی مثل "ثبتنام با اعتبارسنجی"، "محاسبه نمره از دادههای دانشآموز"، یا "ارسال ایمیل بعد از ثبت".
- جلوگیری از درهمریختگی کنترلر: کنترلر فقط وظیفهی دریافت درخواست و ارسال پاسخ داره، نه پردازشها.
---
## 🔗 چرا این دو لایه کنار هم؟
وقتی سرویس و ریپوزیتوری رو با هم داشته باشی:
- کدت منظمتر، قابل نگهداریتر و قابل توسعهتر میشه.
- تست کردن اجزای مختلف خیلی سادهتر میشه.
- پروژهت ساختار حرفهای پیدا میکنه و برای پروژههای واقعی آمادگی بیشتری داره.
- هر بخش از برنامه فقط نقش خودش رو ایفا میکنه (اصل Single Responsibility).