
MVC مخفف Model View Controller است، MVC یک نوع از انواع معماری های برنامه نویسی برای طراحی نرم افزار می باشد. این معماری از سه بخش اصلی تشکیل شده است:
- Model
- View
- Controller
که در ادامه به معرفی تک تک این لایه ها می پردازیم.
اگر یک برنامه تحت وب که با این معماری نوشته شده است را در نظر بگیریم، شیوه کار به این صورت است:

- کاربر آدرس URL برنامه را در مرورگر وارد می کند. در این زمان یک درخواست http به برنامه مورد نظر ارسال می شود.
- Routing برنامه، درخواست کاربر را به متد مورد نظر در Controller برنامه ارسال می کند.
- Controller درخواست را با توجه به پارامتر های دریافت شده از کاربر بررسی می نماید. سپس برای دریافت اطلاعات مورد نیاز کاربر درخواست دریافت اطلاعات را توسط یک Query از Database دریافت می کند و آن را به شکل و شمایل مورد نیاز که در Model برنامه وجود دارد تبدیل می کند.
- پس از آن اطلاعات از نوع مدل مورد نیاز توسط کنترلر به View ارسال می شود.
- View بعد از دریافت اطلاعات، آنها را در بین کد های HTML رندر می کند و سپس در مرورگر کاربر به نمایش در می آید.
معرفی اجزاء MVC
Model
شامل کلاس هایی می شود که قرار است شی هایشان در برنامه استفاده شود و همچنین کلاس هایی که به ذخیره یا واکشی اطلاعات (مدیریت اطلاعات) از دیتابیس می پردازند.

View
با استفاده از اطلاعات دریافتی، به نمایش اطلاعات در بین کدهای HTML می پردازد و آن را آماده ی نمایش در مرورگر می کند.

Controller
لاجیک و منطق اصلی برنامه بر عهده ی controller می باشد.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class StudentController : Controller { public IActionResult Index() { //Some Code return View(); } public IActionResult Detail(int id) { //Some Code return View(); } } |
در کد بالا دو اکشن وجود دارند، به عنوان مثال اگر کاربر به آدرس https://localhost/student برود، اکشن Index اجرا می شود. اگر بخواهد به آدرس https://localhost/student/detail/2 برود، اکشن detail با آرگومان ورودی 2 اجرا خواهد شد.
راه اندازی MVC در NET Core.
برای راه اندازی آن کافی ست چند قدم برداشته شود:
- اگر از ورژن های قدیمی .net core استفاده می کنید باید کد ()AddMvc را به متد ConfigureService در Startup اضافه کنید:
1 2 3 4 5 6 |
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(Microsoft.AspNetCore.Mvc. CompatibilityVersion.Version_3_0); } |
در غیر این صورت کد زیر در متد ConfigureService فایل Startup.cs وجود خواهد داشت
1 2 3 4 5 |
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); } |
- در صورتی که موقع ایجاد پروژه NET Core حالت Empty یا Blank را انتخاب کرده باشید باید تنظیمات مربوط به EndPoint را انجام دهید در غیر این صورت کد های این تنظیمات از قبل نوشته شده اند. تنظیمات EndPoint تعیین می کنند که با توجه به URL درخواست شده، چه کنترلر و اکشنی اجرا شود.
1 2 3 4 5 6 7 8 |
app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); |
پیاده سازی Model
با یک مثال کاربردی پیاده سازی مدل را شرح می دهیم. فرض کنید که میخواهیم اطلاعات دانش آموزان را در پایگاه داده ذخیره و یا واکشی نماییم. بدین منظور در پوشه ی Model یک پوشه به نام Controller می سازیم و دو Class و یک Interface ایجاد می کنیم:

کلاس StudentModel را به شکل زیر می نویسیم:
1 2 3 4 5 6 |
public class StudentModel { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } |
اینترفیس IstudentRepository را به شکل زیر می نویسیم و قرار است تنها یک کار را انجام دهد. یعنی در ازای یک شناسه، مشخصات دانش آموز متناظر را تحویل دهد.
1 2 3 4 |
interface IStudentRepository { string GetStudent(int id); } |
کلاس MockStudentRepository که اینترفیس را پیاده سازی می کند به شکل زیر خواهد بود.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class MockStudentRepository:IStudentRepository { List students; public MockStudentRepository() { students = new List() { new StudentModel(){Id=1,FirstName="Marjan",LastName="Saeedi"}, new StudentModel(){Id=2,FirstName="Ebrahim",LastName="Mohseni"}, new StudentModel(){Id=3,FirstName="Mohsen",LastName="Marjani"} }; } public string GetStudent(int id) { return students.FirstOrDefault(x => x.Id == id).LastName; } } |
البته کلاس بالا به صورت استاتیک اطلاعات مربوط به دانش آموزان را ایجاد و ذخیره کرده است. بعدا چگونگی کار با دیتابیس و استفاده از EntityFramework Coreشرح داده خواهد شد.
استفاده از Model در Controller
استفاده از Model در Controller به شیوه ی Constructor injection به شکل زیر خواهد بود:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class StudentController : Controller { IStudentRepository _studentRepository; public StudentController(IStudentRepository studentRepository) { _studentRepository = studentRepository; } public IActionResult Index() { //Some Code return View(); } public string Detail(int id) { return _studentRepository.GetStudent(id); } } |
اگر بخواهیم در این مرحله برنامه را اجرا کنیم به ارور بر میخوریم:

این خطا به دلیل اینکه تعیین نکردیم که از کدام پیاده سازی از اینترفیس IStudentRepository استفاده کند رخ داده است. برای حل این مشکل به سراغ کلاس Startup می رویم و در متد ConfigureServices کد زیر را اضافه می کنیم:
1 2 |
//Add Singleton services.AddSingleton<IStudentRepository, MockStudentRepository>(); |
کد بالا تعیین می کند که در هرجایی از برنامه، هنگام استفاده از اینترفیس IStudentRepository ، کلاس MockStudentRepository که این اینترفیس را پیاده سازی کرده است استفاده شود. دو سوال پیش می آید، اول اینکه چطور این کار انجام می شود؟ دوم آنکه AddSingleton چیست؟
در پاسخ به این سوال باید گفت که در .NET Core از مفهومی به نام IoC Container استفاده می شود.
IoC Container
مخفف Inversion of Control Container است و فریمورکی برای انجام Dependency Injection است. در این فریمورک امکان تنظیم اولیه وابستگیهای سیستم وجود دارد. برای مثال زمانیکه برنامه از یک IoC Container، نوع اینترفیس خاصی را درخواست میکند، این فریم ورک با توجه به تنظیمات اولیهاش، کلاسی مشخص را بازگشت خواهد داد. قبل از ظهور .NET Core برنامه نویسان مجبور بودند آن را در قالب افزونه به پروژه اضافه کنند اما در .NET Core نیازی به انجام این کار نیست چون IoC Container را در دل خود دارد.
IoC Container سه دوره ی حیات دارد که عبارت اند از:
- AddSingleton
- AddScoped
- AddTransient
AddSingleton
با استفاده از AddSingleton، تنها یک نمونه از کلاس وجود خواهد داشت. این نمونه در اولین درخواست سرویس ساخته می شود و در تمام HTTP Request ها و در تمام طول عمر نرم افزار استفاده می شود.
AddScoped
با استفاده از AddScoped تنها یک نمونه از کلاس در یک HTTP Request وجود خواهد داشت. اما به ازای هر HTTP Request ، یک نمونه جدید از کلاس ساخته می شود. فرقی نمیکند که در یک HTTP Request یکبار یا چند بار از آن کلاس استفاده شود. با AddScoped یک نمونه از کلاس در تمام قسمت های یک HTTP Request وجود خواهد داشت. این نوع از دوره حیات برای شی های Entity Framework DbContext مناسب است (الگوی Unit of Work)
AddTransient
با استفاده از AddTransient به ازای هر بار استفاده از کلاس، یک نمونه جدید از آن ساخته می شود. فرقی نمیکند که در یک HTTP Request بخواهیم از آن استفاده کنیم یا نه. به زبان ساده تر، هر بار که استفاده از یک سرویس درخواست شود یک نمونه ساخته می شود و برای سرویس ها lightweight, stateless (بی وضعیت و سبک) مناسب است.
می توان به عنوان مثال، در داخل کنترلر Home در اکشن Index هم از تزریق، از طریق سازنده کلاس و هم تزریق، از طریق پارامتر استفاده کرد. نیازی نیست که به صورت دستی یک نمونه از آنها ساخته شود و آنها خیلی ساده در کنترلر موجود هستند.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class HomeController : Controller { private readonly IStudentRepository _repository; public HomeController(IStudentRepository rRepository) { _repository = rRepository; } public IActionResult Index([FromServices] IStudentRepository repository) { var student1 = _repository.GetStudent(1); // constructor injected var student2 = repository.GetStudent(2); // parameter injected return View(); } } |
وقتی بخواهیم از Scoped در middleware استفاده کنیم ، باید سرویس را به متد Invoke یا InvokeAsync تزریق کنیم و نباید از طریق constructor injection ( تزریق در سازنده) این کار را انجام دهیم چون سرویس را مجبور به رفتاری مانند Singleton میکند.
پیاده سازی Controller
با یک مثال کاربردی controller توضیح داده خواهد شد. همانطور که قبلا بیان شد لاجیک و منطق اصلی برنامه بر عهده ی Controller است و تمام درخواست های HTTP را مدیریت می کند. قرار است در این مثال تمام اطلاعات student را واکشی شود و در قسمت View ( که بعدا توضیح داده خواهد شد) آن را نشان دهیم. به Controller ای که قبلا داشتیم اکشن زیر را اضافه می کنیم:
1 2 3 4 5 |
public IActionResult Details(int id) { StudentModel model = _studentRepository.GetStudent(id); return View(model); } |
اگر الان برنامه را اجرا کنیم به خطا بر می خوریم چون هیچ View ای با نام Details وجود ندارد.

دقت کنید که return View به طور پیش فرض به دنبال view ای همنام با متد خود می گردد. اگر بخواهیم سفارشی سازی کنیم باید تغییراتی در کد اعمال کنیم که بعدا توضیح داده خواهد شد.
پیاده سازی View
برای حل مشکلی که پیش آمد در پوشه ی Views ما یک پوشه به نام controller مد نظرمان ایجاد می کنیم و در داخل آن فایل Razor همنام با View مورد نظرمان ایجاد می کنیم. ( منظور آن view ای ست که در کنترلر به آن ارجاع داشتیم)

در Details.cshtml کدهایی که از قبل وجود داشت را پاک می کنیم. با نوشتن کلمه ی html و فشردن tab ، به نمونه کد html دست پیدا می کنیم، بعد از آن باید کد های razor را بنویسیم( منظور کد های سی شارپ داخل html هستند)
راه های انتقال داده ها از Controller به View
چندین راه برای انتقال داده ها از Controller به View وجود دارند که عبارت اند از:
- ViewData
- ViewBag
- Strongly Typed View
ViewData
در این روش باید کلید هایی را ایجاد کنیم که در razor بتوانیم به آن دسترسی داشته باشیم. در کد زیر کد های کنترلر تغییر پیدا کرده اند و کلید هایی به آن اضافه شده اند:
1 2 3 4 5 6 7 |
public IActionResult Details(int id) { StudentModel model = _studentRepository.GetStudent(id); ViewData["Student"] = model; ViewData["PageTitle"] = "Student Details"; return View(); } |
بعد از آن در view می نویسیم:

با اجرای برنامه، خروجی به شکل زیر خواهد بود:

ViewData روش مورد اطمینانی نیست چون:
- چون ضعیف و Weakly typed object است.
- از کلید های string به برای ذخیره و یا دریافت اطلاعات استفاده می کند.
- به صورت اتوماتیک در هنگام runtime پردازش می شود به همین دلیل اگر کلید یک ViewData را اشتباه بنویسیم، هیچ خطای سینتکسی بروز پیدا نمی کند و فقط در هنگام runtime خطا ایجاد می شود.
ViewBag
ViewBag هم مانند ViewData است با این تفاوت که از داده های داینامیک استفاده می کند. به تغییرات متد Details در کنترلر دقت کنید:
1 2 3 4 5 6 7 |
public IActionResult Details(int id) { StudentModel model = _studentRepository.GetStudent(id); ViewBag.Student = model; ViewBag.PageTitle = "Student Details"; return View(); } |
و در View :

اگر دقت کنید متوجه می شوید که در هنگام استفاده از ViewBag احتیاجی به cast نیست.
Strongly Typed View
دو روش قبلی ViewBag و ViewData دارای مشکلاتی هستند. به عنوان مثال در کد های Razor دارای Intellisense نیستند و اگر اشتباه نوشته شده باشند فقط در هنگام runtime به خطا بروز پیدا می کند. برای رفع این مشکل از روش دیگری استفاده می شود. در کنترلر، یکی از overload های View این است که می توان به آن یک Model را پاس داد.
1 2 3 4 5 6 |
public IActionResult Details(int id) { StudentModel model = _studentRepository.GetStudent(id); ViewBag.PageTitle = "Student Details"; return View(model); } |
در کد های Razor به جای کد های قبلی باید اینگونه نوشت:

راه های دسترسی به View های مختلف
- اگر در کنترلر بخواهیم به view ای غیر از view ای که همنام متد است دسترسی داشته باشیم کافیست نام آن را بنویسیم:
1 2 3 4 5 6 |
public IActionResult Details(int id) { StudentModel model = _studentRepository.GetStudent(id); //Some Code return View("Test"); } |
در این صورت به جای Details ، ویو ای با نام Test اجرا می شود.

- اگر ویو مدنظر در پوشه ی جداگانه ای باشد:

در این صورت در کنترلر به روش زیر می نویسیم:
1 2 3 4 5 6 |
public IActionResult Details(int id) { StudentModel model = _studentRepository.GetStudent(id); //Some Code return View("OtherView/Test.cshtml"); } |
توجه کنید که چون آدرس دهی فوق مطلق است و نه نسبی، در این صورت باید پسوند فایل حتما نوشته شود.
- آدرس دهی نسبی
1 2 3 4 5 6 |
public IActionResult Details(int id) { StudentModel model = _studentRepository.GetStudent(id); //Some Code return View("../OtherView/Test.cshtml"); } |
- می توان هم آدرس و هم دیتا به View داد:
1 2 3 4 5 6 |
public IActionResult Details(int id) { StudentModel model = _studentRepository.GetStudent(id); //Some Code return View("~/Views/Student/Details.cshtml",model); } |
View Model
در روش قبلی می توانستیم مدل را به راحتی بفرستیم ولی برای پاس دادن Page Title یا هر property دیگری به View مجبور بودیم که از viewData و یا ViewBag استفاده کنیم که با مشکلاتی همراه بودند وهمانند strongly typed view به صورت داینامیک نبودند. برای حل این مشکل از یک viewModel استفاده می کنیم. بدین منظور ابتدا یک پوشه به نام ViewModels ایجاد و سپس یک کلاس viewModel در آن به شکل زیر می نویسیم:
1 2 3 4 5 |
public class StudentDetailsViewModel { public StudentModel student { get; set; } public string PageTitle { get; set; } } |
و در کنترلر تغییرات زیر را اعمال می کنیم:
1 2 3 4 5 6 7 8 9 |
public IActionResult Details(int id) { StudentDetailsViewModel model = new StudentDetailsViewModel { student = _studentRepository.GetStudent(id), PageTitle = "Student Details" }; return View(model); } |
و کدهای Razor View به شکل زیر خواهد بود.
