ایمیل
admin [at] VaniaIT.Com
امور مشتریان
پرتال کاربران - CRM
تلفن
9122830795(98+)
طراحی وب سایت

آموزش Entity Framework 6 Code First – بخش دوم


در بخش اول مقاله آموزش Entity Framework 6 Code First به بررسی اجمالی فریمورک انتیتی پرداختیم و پروژه ی دانشگاهی وانیا را توسط زبان برنامه نویسی MVC.Net و فریمورک EF6 آغاز کردیم. و در همان ابتدا نیز سه دیتامدل را ایجاد و برای هر کدام کنترلر و ویو نیز توسط Scaffolding ایجاد کردیم و همچنین با دیتابیس محلی یا همان LocalDb و نحوه ی آدرس دهی آن آشنا شدیم. حال در این فصل قرار است کمی تغییرات در کنترلرها دهیم و به اصطلاح آن ها راسفارشی سازی کنیم و کمی هم پیغام های خطا را سازماندهی کنیم.برای شروع بهتر است از همین ویو که در بخش قبلی ایجاد شد شروع کنیم. به عنوان مثال فایل نمایش جزئیات دانشجویان در آدرس فیزیکی View>Students>Details.cshtml را باز کنید. میبینید که چند خطی کد وجود دارد که بیشتر اینها را در آموزش MVC 5 به زبان ساده برایتان تعریف کردیم.

@model VaniaUniversity.Models.Student

@{
    ViewBag.Title = "Details";
}

<h2>Details</h2>

<div>
    <h4>Student</h4>
    <hr />
    <dl class="dl-horizontal">
        <dt>
            @Html.DisplayNameFor(model => model.LastName)
        </dt>

        <dd>
            @Html.DisplayFor(model => model.LastName)
        </dd>

        <dt>
            @Html.DisplayNameFor(model => model.FirstName)
        </dt>

        <dd>
            @Html.DisplayFor(model => model.FirstName)
        </dd>

        <dt>
            @Html.DisplayNameFor(model => model.EnrollmentDate)
        </dt>

        <dd>
            @Html.DisplayFor(model => model.EnrollmentDate)
        </dd>

    </dl>
</div>
<p>
    @Html.ActionLink("Edit", "Edit", new { id = Model.ID }) |
    @Html.ActionLink("Back to List", "Index")
</p>

 

حال کدهای زیر را به آن اضافه خواهیم کرد

@model VaniaUniversity.Models.Student

@{
    ViewBag.Title = "Details";
}

<h2>Details</h2>

<div>
    <h4>Student</h4>
    <hr />
    <dl class="dl-horizontal">
        <dt>
            @Html.DisplayNameFor(model => model.LastName)
        </dt>

        <dd>
            @Html.DisplayFor(model => model.LastName)
        </dd>

        <dt>
            @Html.DisplayNameFor(model => model.FirstName)
        </dt>

        <dd>
            @Html.DisplayFor(model => model.FirstName)
        </dd>

        <dt>
            @Html.DisplayNameFor(model => model.EnrollmentDate)
        </dt>

        <dd>
            @Html.DisplayFor(model => model.EnrollmentDate)
        </dd>
        <dt>
            @Html.DisplayNameFor(model => model.Enrollments)
        </dt>
        <dd>
            <table>
                <tr>
                    <th>نام دوره</th>
                    <th>درجه بندی</th>
                </tr>
                @foreach (var item in Model.Enrollments)
                {
                    <tr>
                        <td>@Html.DisplayFor(modelItem => item.Course.Title)</td>
                        <td>@Html.DisplayFor(modelItem => item.Grade)</td>
                    </tr>
                }
            </table>

        </dd>

    </dl>
</div>
<p>
    @Html.ActionLink("ویرایش", "Edit", new { id = Model.ID }) |
    @Html.ActionLink("بازگشت به لیست کلی دانشجویان", "Index")
</p>

برای اینکه کدهای مرتب شده داشته باشیم یکبار CTRL + K  + D را میزنیم سپس پروژه را بیلد و در نهایت اجرا می کنیم. وارد لیست دانشجویان می شویم و یکی از دانشجویان را انتخاب و بر روی گزینه ی Details روبروی آن کلیک می نماییم (شکل شماره ۲-۲۵) [caption id="attachment_2158" align="alignnone" width="595"]

۰۲۵

شکل شماره ۲-۲۵[/caption]

و وقتی وارد جزئیات شدیم مشاهده می کنیم که دیتا مدل دیگری نیز به این دیتا مدل اضافه شده است و نام دوره های این دانشجو به همراه گرید به نمایش در می آید ( مانند شکل ۲-۲۶ )

 

[caption id="attachment_2159" align="alignnone" width="497"]

۰۲۶

شکل شماره ۲-۲۶[/caption]

البته شما می توانید با کمی HTML و CSS و همچنین تکنولوژی Bootstrap کمی به ظاهر این صفحه برسید!

اما این کدهای جدیدی که اضافه کردیم چیست و چه کاری انجام میدهد؟! ما از یک حلقه ی تکرار foreach استفاده کردیم تا به ازای هر داده ای از دیتا مدل Enrollment برایمان در این صفحه نمایش داشته باشد. اما ممکن است این جدول حاوی هزاران داده باشد، آیا باید همه را نمایش دهد؟! ممکن است این دیتامدل دارای چندین ستون و فیلد دیگر نیز باشد، تکلیف چیست؟ خیر، لازم نیست همه ی فیلدهای و دیتاها را نمایش دهد. ما خواستیم تا فقط دو فیلد (فعلاً هین دو فیلد را نیز بیشتر ندارد) Title و Grade را برایمان نمایش دهد. حال چگونه می فهمد که کدام اطلاعات را چونه بیاورد. بیایید قبل از اینکه مبحث پیچیده شود نگاهی به جداول بیاندازیم تا مفهوم برایمان روشن شود.

در خط ۴۸ مشخص کردیم که از دیتامدل Course برایمان title  را نمایش بده. این شما و این هم دیتابیس پروژه ی دانشگاه وانیا و جدول Course

[caption id="attachment_2161" align="alignnone" width="405"]

۰۲۷

شکل شماره ۲-۲۷[/caption]

همانطور که میبینید هر دوره ای یک آی دی منحصر به فرد دارد. مثلاً کد درس برنامه نویسی ۲۰۲۱ است و کد درس مردم شناسی۴۰۲۲ و ....

خب نگاهی به جدول Enrollment بیاندازیم

[caption id="attachment_2162" align="alignnone" width="461"]

۰۲۸

شکل شماره ۲-۲۸[/caption]

ملاحظه می کنید که در اینجا ما تنها کد درس را داریم و کد دانشجو. این اطلاعات را در بخش نخست آموزش به عنوان seed وارد کردیم که البته بنده کمی نیز در آن تغییر ایجاد کردم. در آینده ی نزدیک نیز خواهیم آموخت چگونه از اطلاعات واقعی بهره مند شویم. قعلاً قدم دوم است!

برگردیم به سراغ توضیحات؛ توس دو شکل شماره ۲۷ و ۲۸ متوجه شدیم اطلاعات دانشجوی شماره ۱ را که ما جزئیاتش را کلیک کردیم ، از کجا می خواند. در خصوص خط ۴۹ نیز به همین ترتیب است. از کلاس Enrollment این را فراخوانی می کند.

اما اینکه کل این مراحل جزئیات را از کجا می خواند مهمتر است. مطمئناً می دانید در MVC برای اینکه یوزر بتواند یک View داشته باشید باید اطلاعات آن ویو را از یک Controller بخواند که این کنترلر نیز توسط اطلاعات دیتامدلی ساخته می شود که Model نامیده می شود. ما در بخش دوم دیتا مدل ساختیم و سپس به کمک Scaffold وارد این View شدیم! پس کنترلر کجاست؟ وارد فولدر کنترلر شده و سپس StudentsController.cs را باز کنید. میبینید که تمامی اعمال CRUD و صفحه ی اصلی این کلاس به صورت کاملاً اتوماتیک ایجاد شده اند.

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using VaniaUniversity.DAL;
using VaniaUniversity.Models;

namespace VaniaUniversity.Controllers
{
    public class StudentsController : Controller
    {
        private SchoolContext db = new SchoolContext();

        // GET: Students
        public ActionResult Index()
        {
            return View(db.Students.ToList());
        }

        // GET: Students/Details/5
        public ActionResult Details(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Student student = db.Students.Find(id);
            if (student == null)
            {
                return HttpNotFound();
            }
            return View(student);
        }

        // GET: Students/Create
        public ActionResult Create()
        {
            return View();
        }

        // POST: Students/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create([Bind(Include = "ID,LastName,FirstName,EnrollmentDate")] Student student)
        {
            if (ModelState.IsValid)
            {
                db.Students.Add(student);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            return View(student);
        }

        // GET: Students/Edit/5
        public ActionResult Edit(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Student student = db.Students.Find(id);
            if (student == null)
            {
                return HttpNotFound();
            }
            return View(student);
        }

        // POST: Students/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(Include = "ID,LastName,FirstName,EnrollmentDate")] Student student)
        {
            if (ModelState.IsValid)
            {
                db.Entry(student).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(student);
        }

        // GET: Students/Delete/5
        public ActionResult Delete(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Student student = db.Students.Find(id);
            if (student == null)
            {
                return HttpNotFound();
            }
            return View(student);
        }

        // POST: Students/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            Student student = db.Students.Find(id);
            db.Students.Remove(student);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}

 

[icon_list icon="fa-list"]

  • در خط ۱۶ یکی Instance از دیتابیس گرفته شده است تا در صورت لزوم این Instance را فراخوانی نموده تا اعمال CRUD را انجام دهد.
  • خطوط ۱۹ تا ۲۲ مربوط به صفحه ی اصلی این کنترلر می باشد که لیستی از تمامی داده ها را نیز در این صفحه نمایش می دهد. و اگر وارد ویو این اکشن شوید می بینید که لینکی به اکشن Create نیز داده شده است
  • خطوط ۲۵ الی ۳۵ در متدِ GET اکشنِ Details ما شاهد اطلاعات یک دانشجو هستیم که به ویو باز می گرداند. مثلاً کاربر با آی دی شماره ۵ و اگر این آی دی وجود نداشته باشد ما را به صفحه ی HTTPNotFound راهنمایی می کند. که شما می توانید این صفحه را نیز اختصاصی و سفارشی سازی نمایید
  •  در خطوط ۴۸ الی ۶۰ نیز توسط متدِ Post، اکشن Create اعمال مرتبط با ایجاد دانشجوی جدید را انجام می دهد.
  • در خط ۵۰ توسط تابع مدل بایندر که مرتبط با توابع MVC.Net می باشد به دیتابیس وصل شده و اطلاعات را در آن ذخیره می کنیم. که این بایند کردن به فیلد های نام و نام خانوادگی و تاریخ می باشد. اگر دقت داشته باشید ما در دیتامدل این کلاس یعنی Student یک فیلد دیگر یعنی ID نیز داشتیم که اینجا انرا فراخوانی نکردیم، زیرا این فیلد را خود دیتابیس ایجاد و به ازای هر دیتای جدید یک عدد به آن اضافه می کند.
  • خط ۵۴ می گوید که این اطلاعات را اضافه کن
  • خط ۵۵ نیز می گوید این اطلاعات را در دیتابیس ذخیره کن
  • و در نهایت پس از اعمال بالا در خط ۵۶ به Index همین کنترلر برگرد. شما می توانید یک صفحه یا اکشن درست کنید و به کاربر بگویید که اطلاعات با موفقیت ذخیره شد و کاربر را به آن صفحه هدایت کنید
  • تمامی این چند خطی که توضیح داده شد در خصوص دو اکشن  Edit و Deleted  نیز صدق می کند که مفصل تر درباره ی متدها و پارامترها و توابع صحبت خواهیم کرد
  • همانگونه که مشاهده می کنیددر خط ۹ و ۱۰ دو فضای نامی که برای این کلاس لازم می باشد را خود ویژوال برایتان اضافه کرده است!

[/icon_list]

حال برای اینکه کمی با این اکشن ها و ویو ها آشنا شوید و قدرت انتیتی را درک کنید می خواهیم کمی تغییرات در برنامه بدهیم. دو اکشن ایجادو به روز رسانی تنها توسط یک اکشن انجام میگرفتند اما اکشن حذف نیازمند دو اکشن مجزای get و Post می باشد. متدی که درخواست را فراخوانی می کند توسط متد Get صورت می گیرد و متد Post که وظیفه ی انجام این درخواست را دارد. به صورت پیش فرض نیز هر دو به درستی عمل می کنند. همانطور که میدانید در برنامه نویسی راه های مختلفی برای یک نتیجه ی یکسان وجود دارد و ما در اینجا نیز از روش دیگری نیز میخواهیم این دو را چک کنیم. یعنی حلقه ی try - catch،  یعنی می خواهیم در کنترلر فوق و خطوط ۹۴ الی ۱۰۶ کمی تغییرات ایجاد کنیم. ابتدا کدهای اصلی را یکبار دیگر ببنیم

        // GET: Students/Delete/5
        public ActionResult Delete(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Student student = db.Students.Find(id);
            if (student == null)
            {
                return HttpNotFound();
            }
            return View(student);
        }

حال نوبت تغییرات است. کدهای زیر را جایگزین نموده تا توضیح دهیم

        // GET: Students/Delete/5
        public ActionResult Delete(int? id , bool? saveChangeError=false)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            if(saveChangeError.GetValueOrDefault())
            {
                ViewBag.ErrorMessage = "آیا مطئن هستید که می خواهید این رکورد را حذف نمایید؟";
            }
            Student student = db.Students.Find(id);
            if (student == null)
            {
                return HttpNotFound();
            }
            return View(student);
        }

همانگونه که ملاحظه می کنید ما یک خط اضافه کردیم قبل از ذخیره ی تغییرات که اگر این IF را سپری کرد آنوقت رکورد را حذف کن. اما این رخداد در متد GET رخ می دهد. هنوز ما کاری بر روی متد Post انجام نداده ایم. در این متد نیز ما اعمالی را که می خواهیم انجام دهیم در یک Try-Catch قرار می دهیم

        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            try
            {
                Student student = db.Students.Find(id);
                db.Students.Remove(student);
                db.SaveChanges();
            }
            catch (DataException)
            {
                return RedirectToAction("Delete", new { id = id, saveChangesError = true});
            }
            return RedirectToAction("Index");
        }

ما ابتدا آمدیم آنچیزی را که از قبل خود ویژوال برایمان ایجاد کرده بود را در داخل یک حلقه ی try کپی پیست کردیم. سپس در حلقه ی catch آن در متد Post گفتیم تغییرات را ذخیره و به ایندکس برگرد.

 / پایان آموزش Entity Framework 6 Code First – بخش دوم /