عملیات CRUD در Controller و View – جلسه ۵۰

CRUD Operations in ASP.NET Core MVC with EF Core - Session50

در این جلسه قصد داریم تا به پیاده سازی عملیات CRUD (خواندن، نوشتن، بروزرسانی و حذف) در Controller و View مربوط به وب اپلیکیشن ASP.NET Core با معماری MVC بپردازیم. همانگونه که مشاهده نمودید در جلسه 48ام ریپازیتوری مربوط به عملیات CRUD را (برای پایگاه داده SQL Server و داده‌های ایستا) پیاده‌سازی نمودیم. در این جلسه قصد داریم با استفاده از آن ریپازیتوری، عملیات CRUD را بروی وب اپلیکیشن خود توسعه دهیم.

پیاده‌سازی عملیات نوشتن بروی Controller و View در ASP.NET Core MVC

ما در جلسات گذشته در خصوص ایجاد اکشن متد در کنترلر و متدهای اجرای اکشن متد (GET و POST) مباحثی را ارائه نمودیم. در این جلسه قصد داریم به پیاده سازی عملیات نوشتن (Create) بپردازیم. برای این منظور ما به دو اکشن متد همنام با متدهای اجرایی Get و Post نیازداریم. در اکشن متد مربوطه با متد اجرایی Get، اطلاعات مورد نیاز View و فرم ثبت اطلاعات، فراخوانی خواهد شد و به نما ارسال میگردد. کاربر اطلاعات مورد نظر خود را درون فرم ثبت نموده و اطلاعات را توسط دکمه Submit به کنترلر ارسال میکند. پس از Post اطلاعات فرم به کنترلر، اکشن متد متناظر با متد اجرایی Post فراخوانی میگردد. در این اکشن متد بهتر است معتبر بودن اطلاغات فرم بررسی گردد و سپس اطلاعات مربوطه ثبت گردد و یا خطاهای مربوط به اعتبار سنجی نمایش داده شود.

مراحل پیاده سازی عملیات نوشتن (Create) در ASP.NET Core MVC:

  • ایجاد اکشن متد با متد اجرایی GET.
  • ارسال اطلاعات مورد نیاز فرم ثبت اطلاعات توسط اکشن متد ایجاد شده.
  • Post اطلاعات ثبت شده با زدن دکمه Submit به کنترلر.
  • ایجاد اکشن متد متناظر با متد اجرایی Post.
  • بررسی اعتبار اطلاعات پست شده به کنترلر و اکشن متد.
  • ثبت اطلاعات در رسانه ذخیره سازی در صورت معتبر بودن توسط ریپازیتوری.
  • و یا نمایش خطای اعتبار سنجی در صورت معتبر نبودن اطلاعات فرم.

ما در این دوره آموزشی اقدام به پیاده سازی عملیات CRUD بروی کنترلر و موجودیت Cost مینماییم. شما نیز میتوانید این عملیات را برای دیگر موجودیت ها به عنوان تمرین پیاده سازی نمایید. پس درون کنترلر CostController اکشن متدهای مربوطه را ایجاد مینماییم. البته قبل از آن جهت نمایش لیست Category، نیاز به ایجاد اینترفیس و ریپازیتوری برای موجودیت Category داریم. همچنین این سرویس باید در کلاس Startup ثبت گردد.

کد مربوط به ICategoryRepository:

    public interface ICategoryRepository
    {
        IEnumerable<Category> GetAllCategories();
    }
}

کد مربوط به SQLServerCategoryRepository:

    public class SQLServerCategoryRepository : ICategoryRepository
    {
        private readonly WebAppDBContext webAppDBContext;

        public SQLServerCategoryRepository(WebAppDBContext _webAppDBContext)
        {
            webAppDBContext = _webAppDBContext;
        }
        public IEnumerable<Category> GetAllCategories()
        {
            return webAppDBContext.Categories;
        }
    }

تعریف اینترفیس و ریپازیتوری در کلاس Startup متد ConfigureServices:

services.AddScoped<ICategoryRepository, SQLServerCategoryRepository>();

همچنین ما در جلسه 47ام بین موجودیتهای Cost و Category یک ارتباط یک-به-یک برقرار نمودیم. این ارتباط باید به یک رابطه یک-به-چند تغییر یابد. پس تغییرات زیر را بروی موجودیت Category اعمال میکنیم و فرمان Add-Migration و Update-Database را جهت همگام سازی پایگاه داده اجرا میکنیم.

    public class Category
    {
        [Key]
        public int ID { get; set; }
        [Required]
        public string CategoryName { get; set; }
        public string Description { get; set; }
        [Required]
        public CategoryActiveOptions Active { get; set; }
        public virtual List<Cost> Costs { get; set; }
    }

ضمنا جهت دسترسی به لیست Category میبایست ICategoryRepository را به CostSntroller اضافه نماییم.

    public class CostController : Controller
    {
        private readonly ICostRepository costRepository;
        private readonly ICategoryRepository categoryRepository;

        public CostController(ICostRepository _costRepository, ICategoryRepository _categoryRepository)
        {
            costRepository = _costRepository;
            categoryRepository = _categoryRepository;
        }
    }

حال می‌توانید قطعه کد مربوط به اکشن متد Create با متد اجرایی GET را در ادامه مشاهده نمایید.

        [HttpGet]
        public IActionResult Create()
        {
            var Categories = categoryRepository.GetAllCategories();
            List<SelectListItem> CatList = new();
            foreach (var category in Categories)
            {
                CatList.Add(new SelectListItem(category.CategoryName, category.ID.ToString()));
            }
            ViewBag.Categories = CatList;
            return View();
        }

وقطعه کد مربوط به اکشن متد Create با متد اجرایی Post که در زمان پست فرم به کنترلر، اجرا میگردد.

    public IActionResult Create(CreateCostViewModel model)
    {
        if (ModelState.IsValid)
        {
            Cost cost = new()
            {
                Amount = model.Amount,
                Comment = model.Comment,
                RegisteredDate = model.RegisteredDate,
                CategoryID = model.CategoryID,
                PaymentMethod = model.PaymentMethod
            };
            costRepository.Create(cost);
            return RedirectToAction("Index");
        }
        var Categories = categoryRepository.GetAllCategories();
        List<SelectListItem> CatList = new();
        foreach (var category in Categories)
        {
            CatList.Add(new SelectListItem(category.CategoryName, category.ID.ToString()));
        }
        ViewBag.Categories = CatList;
        return View();
    }

همچنین نما مربوط به اکشن متد Create را با استفاده از مطالبی که در جلسات 44 و 29 بررسی نمودیم، ایجاد میکنیم.

@model CreateCostViewModel
@{
    ViewBag.Title = "Create Cost";
}
<form asp-controller="Cost" asp-action="Create" method="post" class="mt-5 offset-3 col-6 border border-primary pt-2 pb-2 px-2 py-2">
    <div>
        <label asp-for="Amount" class="form-label"></label>
        <input asp-for="Amount" class="form-control" />
        <span asp-validation-for="Amount" class="text-danger"></span>
    </div>
    <div>
        <label asp-for="RegisteredDate" class="form-label"></label>
        <input asp-for="RegisteredDate" class="form-control" />
        <span asp-validation-for="RegisteredDate" class="text-danger"></span>
    </div>
        <div>
        <label asp-for="Comment" class="form-label"></label>
        <input asp-for="Comment" class="form-control" />
        <span asp-validation-for="Comment" class="text-danger"></span>
    </div>
    <div>
        <label asp-for="CategoryID" class="form-label"></label>
        <select asp-for=CategoryID asp-items="@ViewBag.Categories" class="form-select"></select>
    </div>
    <div>
        <label asp-for="PaymentMethod" class="form-label"></label>
        <select asp-for="PaymentMethod" class="form-select" asp-items="Html.GetEnumSelectList<PaymentMethods>()"></select>
    </div>
    <div><button type="submit" class="btn btn-primary mt-2">Create</button></div>
</form>

نمایش لیست رکوردها در ASP.NET Core MVC

عملیات نمایش لیست رکورد را قبلا در جلسه 22 پیاده سازی نموده ایم. در این جلسه هم با استفاده از مطالب همان جلسه، به ایجاد اکشن متد و نمای مربوط به لیست رکوردهای ثبت شده خواهیم پرداخت. ابتدا برای نمایش لیست رکوردها نیاز میباشد تا تمامی رکوردهای مورد نیاز را از فضای ذخیره سازی فراخوانی نماییم. این فرایند باید در اکشن متد با متد اجرایی Get صورت بگیرد. سپس اطلاعات مورد نیاز را به نمای متناظر ارسال میکنیم. در نما، بوسیله حلقه foreach تمامی رکوردها را با قالب مورد نظر نمایش میدهیم.

مراحل پیاده سازی خواندن و نمایش لیست رکوردهای ثبت شده:

  • ایجاد اکشن متد با متد اجرایی GET.
  • واکشی اطلاعات مورد نیاز به کمک ریپازیتوری.
  • ارسال اطلاعات واکشی شده به View.
  • نمایش تمامی رکوردها با استفاده از حلقه foreach.

در این دوره آموزشی قصد داریم تا لیست رکوردهای ثبت شده موجودیت Cost را درون یک جدول نمایش دهیم. به این منظور یک اکشن متد با نام Index با متد اجرایی Get درون کنترلر CostController ایجاد میکنیم. سپس درون آن با استفاده از ریپازیتوری Cost اطلاعات مورد نظر را فراخوانی نموده و به View ارسال میکنیم.

        [HttpGet]
        public IActionResult Index()
        {
            var costs = costRepository.GetAllCost();
            return View(costs);
        }

سپس درون View همانگونه که اشاره شد، اقدام به نمایش اطلاعات درون یک جدول میکنیم. همچنین برای هر رکورد سه دکمه علمیاتی برای نمایش جزئیات، ویرایش و حذف رکورد قرار میدهیم.

@model IEnumerable<Cost>

@{
    ViewBag.Title = "Cost List";
}
<div class="row">
    <div class=col-3>
        <a asp-controller="cost" asp-action="create" class="btn btn-primary mx-2 my-2">Create New Cost</a>
    </div>
</div>
<table class="table table-dark">
    <thead>
        <tr>
            <th>ID</th>
            <th>Amount</th>
            <th>Category</th>
            <th>Comment</th>
            <th>Payment Method</th>
            <th colspan="3" class="text-center">Actions</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var cost in Model)
        {
            <tr>
                <td>@cost.ID</td>
                <td>@cost.Amount</td>
                <td>@cost.CategoryID</td>
                <td>@cost.Comment</td>
                <td>@cost.PaymentMethod</td>
                <form asp-controller="cost" asp-action="delete" asp-route-id="@cost.ID" method="post">
                <td><a class="btn btn-primary d-block" asp-controller="cost" asp-action="detail" asp-route-id="@cost.ID">View</a></td>
                <td><a class="btn btn-info btn-block d-block" asp-controller="cost" asp-action="update" asp-route-id="@cost.ID">Edit</a></td>
                <td><button class="btn btn-danger btn-block d-block" type="submit">Delete</button></td>
                </form>
            </tr>
        }
    </tbody>
</table>

همچنین منوی وب اپلیکشن خود را جهت دسترسی به اکشن متدهای ایجاد شده بروزرسانی میکنیم. این منو در نمای _MainLayout.cshtml در مسیر Views/Shared قرار دارد.

        <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
            <div class="container-fluid">
                <a class="navbar-brand" asp-controller="home" asp-action="index">
                    <img src="~/images/cost.png" alt="" width="82" height="51">
                </a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                        <li class="nav-item">
                            <a class="nav-link" aria-current="page" asp-controller="home" asp-action="index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" asp-controller="cost" asp-action="index">Cost List</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>

پیاده‌سازی عملیات نوشتن بروی Controller و View در ASP.NET Core MVC

برای نمایش جزئیات یک رکورد، ما به یک اکشن متد با متد اجرایی GET نیاز داریم. درون آن اکشن متد، رکورد مورد نظر با استفاده از یک فیلد شاخص و با کمک ریپازیتوری فراخوانی میگردد. سپس اطلاعات مربوط به جزئیات رکورد، به View جهت نمایش ارسال میگردد.

مراحل پیاده سازی خواندن (Read) و نمایش جزئیات یک رکورد در ASP.NET Core MVC:

  • ایجاد اکشن متد با متد اجرایی GET با مقدار ورودی مناسب جهت جستجوی رکورد مورد نظر (مانند ID رکورد).
  • واکشی رکوردمورد نیاز به کمک ریپازیتوری و مقدار موردی.
  • ارسال اطلاعات واکشی شده به View.
  • نمایش اطلاعات درون View.

ما در ادامه عملیات CRUD بروی پروژه خود، به فراخوانی رکورد Cost با ID مورد نظر خواهیم پرداخت. همانگونه که در قطغه کد مربوط به نمای Index مشاهده میکنید، برای نمایش جزئیات یک رکورد Cost، اکشن متد Detail فراخوانی میگردد. ضمنا ID رکورد توسط asp-route-id به اکشن متد ارسال میگردد. در در نهایت رکورد فراخوانی شده جهت نمایش به View ارسال میگردد.

        [HttpGet]
        public IActionResult Detail(int id)
        {
            var cost = costRepository.GetCostByID(id);
            return View(cost);
        }

درون نما View اطلاعات درون یک کارت با استفاده از کلاسهای Bootstrap نمایش داده می‌شود. کدهای این نما در جلسات گذشته و استفاده از Bootstrap در جلسه 27 مورد بررسی قرار گرفته است.

@model Cost
@{
    ViewBag.Title = "Cost Detail";
}
<div class="row">
    <div class="col-lg-4 offset-lg-4 col-md-6 offset-md-3 col-12">
        <div class="card">
            <div class="card-header">
                Detail Cost ID @Model.ID
            </div>
            <div class="card-body">
                <p class="card-text">Amount: @Model.Amount</p>
                <p class="card-text">Category: @Model.CategoryID</p>
                <p class="card-text">Comment: @Model.Comment</p>
                <p class="card-text">Payment Method: @Model.PaymentMethod</p>
                <a class="btn btn-primary" asp-controller="cost" asp-action="index">Cost List</a>
            </div>
        </div>
    </div>
</div>

پیاده‌سازی عملیات بروزرسانی بروی Controller و View در ASP.NET Core MVC

برای بروزرسانی یک رکورد، ابتدا نیاز به یک اکشن متد با روش اجرایی GET خواهیم داشت. سپس درون اکشن متد ایجاد شده اقدام به واکشی اطلاعات رکورد مورد نظر نموده و اطلاعات رکورد و در صورت نیاز اطلاعات جانبی را به View ارسال میکنیم. اطلاعات درون فرم ایجاد شده در View نمایش داده میشود و شاخصه یکتای رکورد (مانند ID) درون یک Input مخفی ذخیره میگردد. سپس با استفاده از دکمه با متد Submit اطلاعات به کنترلر، Post میگردد. درون اکشن متد با روش اجرایی Post، اطلاعات در صورت معتبر بودن به ریپازیتوری، جهت ثبت تغییرات ارسال میگردند. همچنین در صورت معتبر نبودن اطلاعات، فرم جهت نمایش خطای اعتبارسنجی مجددا فراخوانی خواهد شد.

مراحل پیاده سازی عملیات بروزرسانی (Update) در ASP.NET Core MVC:

  • ایجاد اکشن متد با متد اجرایی GET.
  • جستجوی رکورد مورد نظر در رسانه ذخیره سازی و واکشی اطلاعات توسط ریپازیتوری.
  • ارسال اطلاعات واکشی شده و دیگر اطلاعات مورد نیاز فرم بروزرسانی اطلاعات.
  • ایجاد اکشن متد متناظر با متد اجرایی Post.
  • بررسی اعتبار اطلاعات پست شده به کنترلر و اکشن متد.
  • ثبت اطلاعات در رسانه ذخیره سازی در صورت معتبر بودن.
  • نمایش خطای اعتبار سنجی در صورت معتبر نبودن اطلاعات فرم.

ما در ادامه پیاده سازی عملیات CRUD بروی وب اپلیکشن خود، به پیاده سازی عملیات بروزرسانی اطلاعات رکورد Cost خواهیم پرداخت. همانگونه که در نمای Index مشاهده میکنیم، اکشن متد Update از کنترلر CostController برای این منظور فراخوانی می‌شود. همچنین ID رکورد مورد نظر، توسط تگ هلپر asp-route-id به اکشن متد ارسال میگردد.

        [HttpGet]
        public IActionResult Update(int id)
        {
            var cost = costRepository.GetCostByID(id);
            var Categories = categoryRepository.GetAllCategories();
            List<SelectListItem> CatList = new();
            foreach (var category in Categories)
            {
                CatList.Add(new SelectListItem(category.CategoryName, category.ID.ToString()));
            }
            ViewBag.Categories = CatList;
            return View(cost);
        }

همچنین پس از Post اطلاعات، اکشن متد Update با روش اجرایی Post فراخوانی و اجرا میگردد.

        [HttpPost]
        public IActionResult Update(Cost model)
        {
            if (ModelState.IsValid)
            {
                costRepository.Update(model);
                return RedirectToAction("Index");
            }
            var cost = costRepository.GetCostByID(model.ID);
            var Categories = categoryRepository.GetAllCategories();
            List<SelectListItem> CatList = new();
            foreach (var category in Categories)
            {
                CatList.Add(new SelectListItem(category.CategoryName, category.ID.ToString()));
            }
            ViewBag.Categories = CatList;
            return View(cost);
        }

در قطعه کد زیر، نمای مربوط به عملیات بروزرسانی قابل مشاهده میباشد که بسیار به کد نمای Create شباهت دارد.

@model Cost
@{
    ViewBag.Title = "Create Cost";
}
<form asp-controller="Cost" asp-action="Update" method="post" class="mt-5 offset-3 col-6 border border-primary pt-2 pb-2 px-2 py-2">
    <input  hidden asp-for="ID"/>
    <div>
        <label asp-for="Amount" class="form-label"></label>
        <input asp-for="Amount" class="form-control" />
        <span asp-validation-for="Amount" class="text-danger"></span>
    </div>
    <div>
        <label asp-for="RegisteredDate" class="form-label"></label>
        <input asp-for="RegisteredDate" class="form-control" />
        <span asp-validation-for="RegisteredDate" class="text-danger"></span>
    </div>
        <div>
        <label asp-for="Comment" class="form-label"></label>
        <input asp-for="Comment" class="form-control" />
        <span asp-validation-for="Comment" class="text-danger"></span>
    </div>
    <div>
        <label asp-for="CategoryID" class="form-label"></label>
        <select asp-for=CategoryID asp-items="@ViewBag.Categories" class="form-select"></select>
    </div>
    <div>
        <label asp-for="PaymentMethod" class="form-label"></label>
        <select asp-for="PaymentMethod" class="form-select" asp-items="Html.GetEnumSelectList<PaymentMethods>()"></select>
    </div>
    <div class="row mt-2">
        <div class="col-3 d-grid">
            <button type="submit" class="btn btn-primary">Edit</button>
        </div>
        <div class="col-3 d-grid">
            <a asp-action="index" class="btn btn-info">Cost List</a>
        </div>
    </div>
</form>

پیاده‌سازی عملیات حذف بروی Controller و View در ASP.NET Core MVC

برای پیاده سازی عملیات حذف رکورد از رسانه‌ی ذخیره سازی، میتوان از روش اجرایی Get و Post استفاده نمود. ولی روش Get روش ایمنی نمیباشد. زیرا در صورت پیاده سازی این عملیات با روش Get وب اپلیکشن ما در برابر حمله Cross-Site Request Forgery (CSRF) ایمن نخواهد بود. در نتیجه کاربران و یا کسانی که قصد تخریب اطلاعات وب اپلیکشن شما را دارند میتوانند اقدام به حذف رکوردها بوسیله URL Query String و یا ایجاد لینک‌های مخرب نماید.

چرا حذف اطلاعات بوسیله متد HTTPGet روش ایمنی نمیباشد؟

متد HttpGet در برابر حمله Cross-Site Request Forgery (CSRF) ایمن نمی‌باشد. در نتیجه کاربران و یا کسانی که قصد تخریب اطلاعات وب اپلیکشن شما را دارند میتوانند اقدام به حذف رکوردها بوسیله URL Query String و یا ایجاد لینک‌های مخرب نماید.

پس برای پیاده سازی عملیات حذف، بهتر است از متد با روش اجرایی Post استفاده نماییم. در نتیجه دکمه حذف میبایست اقدام به Submit اطلاعات فرمی نماید که ID رکورد مورد نظر را به کنترل Post مینماید.

ما در وب اپلیکیشن خود، همانگونه که در نمای Index مشاهده میکنید، دکمه حذف را از نوع Submit قرار دادیم. این دکمه فرم مورد نظر را به کنترلر Post می‌نماید. در این مثال فرم به کنترلر CostController و اکشن متد Delete پست میگردد. همچنین ID رکورد Cost توسط asp-route-id به کنترلر پست میگردد. سپس درون کنترلر با استفاد ه از ریپازیتوری، عملیات حذف انجام میگردد.

        [HttpPost]
        public IActionResult Delete(int id)
        {
            costRepository.Delete(id);
            return RedirectToAction("index");
        }

در صورت نیاز به جزئیات بیشتر، میتوانید ویدئو آموزشی این جلسه را تماشا نمایید. همچنین برای آگاهی از جلسات بعدی این دوره آموزشی، ما را در اینستاگرام، تلگرام، یوتیوب و آپارات دنبال کنید. ضمنا لیست کامل جلسات در این قسمت در دسترس شما میباشد و سورس کد این جلسه را میتوانید از GitHub ما دانلود نمایید.

تماشای ویدیو در یوتیوب ما

دانلود اسلایدهای آموزشی این جلسه از اینجا

برچسب ها

0 0 رای ها
امتیازدهی به مقاله
اشتراک در
اطلاع از
guest
0 نظرات
بازخورد (Feedback) های اینلاین
مشاهده همه دیدگاه ها
0
افکار شما را دوست داریم، لطفا نظر دهید.x