معرفی Data Entry Screen و اتصال دیتابیس در MVC

معرفی Data Entry Screen

در این سری آموزشی، با معرفی Data Entry Screen و اتصال دیتابیس پروژه را یک مرحله دیگر پیش می بریم.همانطور که در جلسه پیش هم یادآوری کردیم، فرض ما بر این است که جلسات قبلی را به خوبی مطالعه کرده اید. در روز دوم، پروژه ای را با یک grid و لیستی از Employee ایجاد کرده و گام به گام تکمیل کردیم.

در روز سوم، با معرفی لایه دسترسی داده و data entry screen پروژه را یک مرحله جلوتر می بریم.

فهرست مطالب:

  • Data Access Layer (لایه دسترسی داده)
    • به زبان ساده Entity Framework چیست؟
    • روش Code First چیست؟
  • تمرین هشتم – اضافه کردن Data Access Layer به پروژه
    • پرسش و پاسخ در تمرین هشتم
  • ساماندهی به کدها و بخش های پروژه
  • تمرین نهم – ایجاد Data Entry Screen
    • پرسش و پاسخ در تمرین نهم
  • تمرین دهم – دریافت داده های post شده در سمت سرور/Controller ها
    • پرسش و پاسخ در تمرین دهم
  • تمرین یازدهم – دکمه های Reset و Cancel
    • پرسش و پاسخ در تمرین یازدهم
  • تمرین دوازدهم – ذخیره رکوردها در دیتابیس و به روزرسانی Grid
  • تمرین سیزدهم – اضافه کردن اعتبارسنجی سمت سرور
    • اتصال دهنده مدل (Model Binder) چگونه با انواع داده اولیه کار می کند؟
    • اتصال دهنده مدل (Model Binder) چگونه با کلاس ها کار می کند؟
    • پرسش و پاسخ در تمرین سیزدهم
  • تمرین چهاردهم – اعتبارسنجی سفارشی سمت سرور
  • جمع بندی

Data Access Layer

درواقع، پروژه های واقعی بدون دیتابیس ناقص هستند. در پروژه ای که تا اینجا باهم کار کردیم هنوز حرفی از دیتابیس نزدیم. تمرین اول این جلسه کاملا مربوط به دیتابیس و لایه دیتابیس می باشد.

در اینجا از Sql Server و Entity Framework به ترتیب برای ایجاد دیتابیس و لایه دسترسی داده استفاده می کنیم.

به زبان ساده Entity Framework چیست؟

یک ابزار ORM است. همانطور که می دانید، ORM کوتاه شده عبارت Object Relational Mapping (نگاشت رابطه ای اشیا) می باشد.

در پایگاه داده ای رابطه ای، از اصطلاحات Table(جدول) و Column(ستون) استفاده می کنیم همانطور که در دنیای .net (دنیای مبتنی بر شی گرایی) نیز از اصطلاحاتی مانند کلاس و اشیا و property ها استفاده می کنیم.

زمانی که درباره هر اپلیکیشنی که با داده ها کار می کند، فکر می کنیم با دو مسئله مواجه می شویم:

  • نوشتن کد اتصال به دیتابیس (که به آن Data Access Layer یا Database logic می گویند.)
  • نوشتن کد برای نگاشت داده های دیتابیس به داده های مبتنی بر شی گرایی یا برعکس

ORM ابزاری است که این دو مورد را به طور اتوماتیک انجام می دهد. Entity framework ابزار ORM مربوط به مایکروسافت می باشد.

روش Code First چیست؟

در Entity framework می توانیم یکی از روش های زیر را دنبال کنیم:

  • روش Database First – دیتابیس را با جدول ها، ستون ها، رابطه ها و … ایجاد می کنیم. Entity framework کلاس های Model مربوطه (موجودیت های Business) و کد مربوط به لایه دسترسی داده را تولید می کند.
  • روش Model First – در این روش کلاس های Model و رابطه بین آنها با استفاده از طراح Model در ویژوال استودیو به طور دستی ایجاد می شود و Entity framework لایه دسترسی داده و جدول ها، ستون ها و رابطه های دیتابیس را به طور اتوماتیک ایجاد می کند.
  • روش Code First – در این روش کلاس های POCO به صورت دستی ایجاد می شوند. ارتباط بین این کلاس ها به وسیله کد تعریف می شود. زمانی که اپلیکیشن برای اولین بار اجرا می شود، Entity framework لایه دسترسی داده و دیتابیس را به همراه جدول ها، ستون و رابطه ها به طور اتوماتیک در سرور دیتابیس تولید می کند.

کلاس POCO چیست؟

POCO کوتاه شده عبارت “Plain Old CLR objects” می باشد. کلاس های POCO درواقع کلاس های ساده .Net هستند که ما ایجاد می کنیم. درمثال قبلی ما، کلاس Employee یک کلاس ساده POCO می باشد.

تمرین هشتم – اضافه کردن لایه دسترسی داده به پروژه

گام ۱: ایجاد دیتابیس

Sql Server را باز کرده و یک دیتابیس جدید به نام “SaleERPDB” ایجاد می کنیم.

 

گام ۲: ایجاد رشته اتصال (ConnectionString )

فایل Web.config را باز کرده و در بخش Configuration بخش زیر را اضافه می کنیم.

<connectionStrings>
<add connectionString="Data Source=(local);Initial Catalog=SalesERPDB;Integrated Security=True"
        name="SalesERPDAL"       
        providerName="System.Data.SqlClient"/>
</connectionStrings>

گام ۳: اضافه کردن رفرنس Entity framework

روی نام پروژه راست کلیک کرده و Manage Nuget packages را انتخاب می کنیم. Entity framework را جستجو کرده و روی install کلیک می کنیم.

گام ۴: ایجاد لایه دسترسی داده

یک فولدر جدید به نام “DataAccessLayer” در فولدر ریشه ایجاد کرده و در داخل آن کلاس جدیدی به نام “SalesERPDAL” ایجاد می کنیم.

عبارت using را مانند زیر در بالای کلاس قرار می دهیم.

using System.Data.Entity;

برای کلاس “SalesERPDAL” از کلاس DbContext ارث بری می کنیم.

public class SalesERPDAL: DbContext
{
}

گام ۵: ایجاد فیلد کلید اصلی برای کلاس employee

کلاس Employee را باز کرده و عبارت using را در بالای آن قرار می دهیم.

using System.ComponentModel.DataAnnotations;

ویژگی EmployeeId را به کلاس Employee اضافه کرده و آن را به عنوان صفت [Key] مشخص می کنیم.

public class Employee
{
[Key]
public int EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Salary { get; set; }
}

گام ۶: تعریف نگاشت (mapping)

عبارت using زیر را در بالای کلاس “SalesERPDAL” قرار می دهیم.

using WebApplication1.Models;

متد “OnModelCreating” موجود در کلاس SalesERPDAL را به صورت زیر Override می کنیم.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<employee>().ToTable("TblEmployee");
base.OnModelCreating(modelBuilder);
}

توجه داشته باشید که در کد بالا، “TblEmployee” نام جدول را نشان می دهد که به طور اتوماتیک در زمان اجرا ایجاد می شود.

گام ۷: ایجاد property برای نگه داشتن Emloyee ها در دیتابیس

به صورت زیر، یک property جدید به نام “Employee” در کلاس “SalesERPDAL” ایجاد می کنیم.

public DbSet<employee> Employees{get;set;}

DbSet همه employee هایی که قابل پرس و جو (query زدن) از دیتابیس هستند، نشان می دهد.

گام ۸: تغییر کد Business Layer و دریافت داده از دیتابیس

کلاس EmployeeBusinessLayer را باز کرده و عبارت using زیر را در آن قرار می دهیم.

using WebApplication1.DataAccessLayer;

حال متد GetEmployees موجود در کلاس را به شکل زیر تغییر می دهیم.

public List<employee> GetEmployees()
{
SalesERPDAL salesDal = new SalesERPDAL();
return salesDal.Employees.ToList();
{

گام ۹: اجرا و تست برنامه

کلید F5 را زده و برنامه را اجرا می کنیم

در حال حاضر، دیتابیس ما خالی است و هیچ رکوردی به آن اضافه نکرده ایم بنابراین یک Grid خالی می بینیم.

دیتابیس را بررسی می کنیم. جدولی با نام TblEmployee با همه ستون های موردنظر خود را مشاهده می کنیم.

گام ۹: وارد کردن داده های آزمایشی

چند داده آزمایشی به جدول TblEmployee اضافه می کنیم.

گام ۱۰: تست و اجرای برنامه

با کلید F5 دوباره برنامه را اجرا می کنیم.

همانطور که مشاهده می کنید، این بار رکوردها در Grid نمایش داده شدند.

پرسش و پاسخ در تمرین هشتم:

DbSet چیست؟

DbSet مجموعه ای از تمام موجودیت هایی است که قابل کوئری زدن در دیتابیس هستند. زمانی که ما یک کوئری Linq می نویسیم، شی DbSet دوباره به کوئری تبدیل می شود و در دیتابیس اجرا می شود.

در مثال ما، “Employee” یک DbSet است که تمام موجودیت های “Employee” را که می توان با کوئری از دیتابیس استخراج کرد، نگه می دارد. هر بار که ما بخواهیم به “Employee” ها دسترسی پیدا کنیم، تمام رکوردهای داخل جدول “TblEmployee” را دریافت کرده و آنها را به اشیای “Employee” تبدیل می کند و مجموعه آنها را برمی گرداند.

Connection string و لایه دسترسی داده چگونه به هم متصل می شوند؟

نگاشت براساس نام انجام می شود. در مثال ما، نام connection string و کلاس Data Access Layer به طور یکسان “SalesERPDAL” از این رو به طور اتوماتیک باهم نگاشت می شوند.

آیا می توانیم نام connection string را تغییر دهیم؟

بله، در این مورد باید به صورت زیر یک سازنده در کلاس Data Access Layer ایجاد کنیم.

public SalesERPDAL():base("NewName")

{

}

 

ساماندهی کدها و بخش های پروژه

برای ساماندهی کدها و معنی بخشیدن به برخی از آنها تغییرات زیر را انجام می دهیم:

گام ۱: تغییر نام

  • تغییر نام “TestController” به “EmployeeController”
  • تغیر نام متد “GetView” به “Index”
  • فولدر “Test” (در فولدر Views) به “Employee”
  • تغییر نام “MyView” به “Index”

گام ۲: حذف ویژگی UserName از کلاس EmployeeListViewModel

گام ۳: حذف UserName از View

فایل Views/Employee.Index.cshtml را باز کرده و UserName را از آن حذف می کنیم.

به زبان ساده تر، بلاک کد زیر را حذف می کنیم:

Hello @Model.UserName

<hr />

 

گام ۲: تغییر متد Index در EmployeeController

به همین ترتیب، کد داخل متد Index در EmployeeController را تغییر می دهیم:

public ActionResult Index()

{

    &hellip;&hellip;

    &hellip;&hellip;

    &hellip;&hellip;

    employeeListViewModel.Employees = empViewModels;

    //employeeListViewModel.UserName = "Admin";-->Remove this line -->Change1

    return View("Index", employeeListViewModel);//-->Change View Name -->Change 2

}

 

حال آدرسی که در زمان اجرا باید وارد شود به این صورت خواهد بود:

"…./Employee/Index"

تمرین نهم – ایجاد Data Entry Screen

گام ۱: ایجاد action method

یک action method جدید به نام “AddNew”  در EmployeeController اضافه می کنیم.

public ActionResult AddNew()

{

    return View("CreateEmployee");

}

 

گام ۲: ایجاد View

یک view به نام “CreateEmployee” درون فولدر Views/Employee ایجاد می کنیم.

@{

    Layout = null;

}

<!DOCTYPE html>

<html>

    <head>

      <meta name="viewport" content="width=device-width" />

      <title>CreateEmployee</title>

    </head>

    <body>

      <div>

         <form action="/Employee/SaveEmployee" method="post">

            First Name: <input type="text" id="TxtFName" name="FirstName" value="" /><br />

            Last Name: <input type="text" id="TxtLName" name="LastName" value="" /><br />

            Salary: <input type="text" id="TxtSalary" name="Salary" value="" /><br />

            <input type="submit" name="BtnSave" value="Save Employee" />

            <input type="button" name="BtnReset" value="Reset" />

         </form>

      </div>

    </body>

</html>

 

گام ۳: ایجاد یک لینک در Index View

فایل Index.cshtml را باز کرده و یک hyperlink که به آدرس AddNew اشاره دارد، اضافه می کنیم.

<ahref="/Employee/AddNew">Add New</a>

گام ۴: تست و اجرای برنامه

کلید F5 را فشار داده و برنامه را اجرا می کنیم.

پرسش و پاسخ در تمرین نهم:

دلیل استفاده از تگ form چیست؟

در روز اول از این سری آموزشی متوجه شدیم که دنیای وب از برنامه نویسی رویدادی پیروی نمی کند، بلکه به روش درخواست-پاسخ عمل می کند. کاربر درخواستی می فرستد و سرور پاسخ آن را ارسال می کند. تگ form یکی از روش های ارسال درخواست در HTML می باشد. به محض اینکه دکمه تأیید در تگ form کلیک شود، درخواستی برای URL ای که در action attribute مشخص شده، ارسال می شود.

Method attribute در تگ form چیست؟

نوع درخواست را مشخص می کند. درخواست ارسال شده می تواند یکی از انواع زیر باشد:

Get، Post، Put و Delete

طبق هر استاندارد تحت وبی ما باید به این ترتیب استفاده کنیم:

  • Get -> زمانی که می خواهیم چیزی دریافت کنیم.
  • Post -> زمانی که می خواهیم چیزی ایجاد کنیم.
  • Put -> زمانی که می خواهیم چیزی را به روزرسانی کنیم.
  • Delete -> زمانی که می خواهیم چیزی را حذف کنیم.

ایجاد درخواست با استفاده از تگ form چه تفاوتی با ارسال درخواست از طریق آدرس بار مرورگر یا لینک دارد؟

زمانی که درخواست با کمک تگ form ایجاد می شود، مقادیر همه کنترل های ورودی برای پردازش درخواست ارسال می شوند.

درباره checkbox، radio button و Dropdown چطور؟ آیا مقادیر این کنترل ها نیز ارسال می شوند؟

بله، تمام کنترل های ورودی (انواع ورودی = text، radio و checkbox) و همچنین dropdown (که به صورت یک عنصر “Select” نمایش داده می شود.)

این مقادیر چگونه به سرور ارسال می شوند؟

زمانی که درخواست ها از نوع Get، Put و Delete باشد مقادیر به عنوان پارامترهای Query string ارسال می شوند.

اما زمانی که درخواست Post باشد، مقادیر به عنوان داده های post شده ارسال می شوند.

هدف از استفاده از صفت name در کنترل های ورودی چیست؟

همانطور که پیش از این گفته شد، زمانی که دکمه submit کلیک می شود، مقادیر همه کنترل های ورودی به همراه درخواست ارسال می شود. بنابراین سرور در یک لحظه بیش از یک مقدار را دریافت می کند. برای اینکه هر یک از مقادیر را به طور جداگانه بررسی کند، زمان ارسال مقادیر، هر یک با یک کلید ارسال می شوند که همان صفت “name” است.

آیا صفت name و Id کاربرد یکسان دارند؟

خیر، همانطور که در سوال قبلی گفته شد، زمانی که یک درخواست ارسال می شود صفت “name” به طور داخلی توسط HTML استفاده می شود در حالی که صفت “Id” توسط برنامه نویسان برای برخی کارهای داینامیک در جاوا اسکریپت استفاده می شود.

تفاوت بین “input type=submit” و “input type=button” چیست؟

دکمه Submit به طور خاص زمانی استفاده می شود که بخواهیم درخواستی برای سرور ارسال نماییم درحالی که دکمه ساده برای اجرای برخی رویدادهای مخصوص سمت کلاینت استفاده می شود. دکمه ساده به خودی خود نمی تواند کاری انجام دهد.

تمرین دهم – دریافت داده های Post شده در سمت سرور/Controllers

گام ۱: ایجاد متد SaveEmployee

در Employee Controller، متدی با نام “SaveEmployee” ایجاد می کنیم.

 

public string SaveEmployee(Employee e)

{

   return e.FirstName + "|"+ e.LastName+"|"+e.Salary;

}

 

گام ۲: تست و اجرا

پرسش و پاسخ در تمرین دهم:

چگونه مقادیر Textbox در شی Employee داخل متد به روزرسانی می شود؟

در ASP.Net MVC مفهمومی به نام Model Binder داریم.

  • هر بار که یک درخواست به متدی که شامل پارامتر است ایجاد می شود، Model Binder به طور اتوماتیک اجرا می شود.
  • Model Binder برای همه پارامترهای اولیه یک متد، تکرار می شود و سپس نام پارامتر را با کلید هریک از داده های ورودی مقایسه می کند (داده های ورودی همان داده های post شده یا query string می باشد). زمانی که داده مربوطه پیدا شد، داده ورودی مربوطه به پارامتر مرتبط به آن تخصیص داده می شود.
  • پس از آن Model Binder بر روی هر property و پارامترهای کلاس نیز تکرار شده و نام property را با کلید داده های ورودی مقایسه می کند. زمانی که داده موردنظر یافت شد، مقدار ورودی مربوطه به پارامتر موردنظر تخصیص داده می شود.

زمانی که دو پارامتر به این صورت تعریف شده باشند، “Employee e” و “string FirstName” چه اتفاقی می افتد؟

FirstName به عنوان هر دو متغیر، متغیر اولیه FirstName و ویژگی e.FirstName به روزرسانی می شود.

آیا Model Binder با رابطه ترکیبی کار می کند؟

بله، اما در این مورد باید به نام گذاری کنترل ها دقت کرد.

مثال: فرض کنید کلاس های Customer و Address را به شکل زیر داریم:

public class Customer

{

         public string FName{get;set;}

    public Address address{get;set;}

}

public class Address

{

         public string CityName{get;set;}

         public string StateName{get;set;}

}


 

در این مثال، HTML آن باید به شکل زیر باشد:

 

 

...

...

...

<input type="text" name="FName">

<input type="text" name="address.CityName">

<input type="text" name="address.StateName">

...

...

...

 

تمرین یازدهم – دکمه های Reset و Cancel

گام ۱: شروع کار با دکمه های Reset و Cancel

دو دکمه Reset و Cancel را به شکل زیر اضافه می کنیم.

 

...



...



...



<input type="submit" name="BtnSubmit&rdquo; value="Save Employee" />



<input type="button" name="BtnReset" value="Reset" onclick="ResetForm();" />



<input type="submit" name="BtnSubmit" value="Cancel" />

 

 

توجه داشته باشید که دکمه Save و Cancel هر دو “Name” یکسان “BtnSubmit” دارند.

گام ۲: تعریف تابع ResetForm

در قسمت Head یک تگ script ایجاد می کنیم و درون آن یک تابع جاوااسکریپت به نام ResetForm به شکل زیر ایجاد می کنیم.

<script>

    function ResetForm() {

        document.getElementById('TxtFName').value = "";

        document.getElementById('TxtLName').value = "";

        document.getElementById('TxtSalary').value = "";

    }

</script>

 

گام ۳: پیاده سازی کلیک cancel در متد SaveEmployee موجود در EmployeeController

متد SaveEmployee را به صورت زیر تغییر می دهیم:

public ActionResult SaveEmployee(Employee e, string BtnSubmit)

{

    switch (BtnSubmit)

    {

        case "Save Employee":

            return Content(e.FirstName + "|" + e.LastName + "|" + e.Salary);

        case "Cancel":

            return RedirectToAction("Index");

    }

    return new EmptyResult();

}

گام ۴: تست و اجرای برنامه

کلید F5 را فشار داده و برنامه را اجرا می کنیم. با کلیک روی لینک “کارمند جدید” به صفحه AddNew هدایت می شویم.

گام ۵: تست کاربرد Reset

گام ۶: تست کاربرد Save و Cancel

پرسش و پاسخ در تمرین یازدهم:

چرا به دکمه های Save و Cancel نام یکسان داده شده است؟

می دانیم به محض اینکه روی دکمه submit کلیک شود، درخواستی به سرور ارسال می شود. به همراه آن مقادیر تمام کنترل های ورودی نیز ارسال می شود.

همچنین دکمه submit، یک دکمه ورودی است. از این رو مقدار دکمه submit نیز به سرور ارسال می شود.

زمانی که دکمه Save  کلیک شود، مقدار این دکمه که “Save Employee” است با درخواست به سرور ارسال می شود و زمانی که دکمه Cancel کلیک شود، مقدار دکمه cancel که “Cancel” است به همراه درخواست به سرور ارسال می شود.

در متد، Model Binder بقیه کارها را انجام می دهد. مقادیر پارامترها را با داده های ورودی به روزرسانی می کند (ارسال شده همراه درخواست)

روش های دیگر پیاده سازی چندین دکمه submit چیست؟

روش های بسیاری وجود دارد، که در اینجا سه تا از این روش ها بحث می شود.

  1. عناصر form پنهان

گام ۱: ایجاد یک عنصر form پنهان در View

 

<form action="/Employee/CancelSave" id="CancelForm" method="get" style="display:none">



</form>

 

گام ۲: تغییر دکمه submit به دکمه عادی و post کردن form بالا با استفده از جاوا اسکریپت

<input type="button" name="BtnSubmit" value="Cancel" onclick="document.getElementById('CancelForm').submit()" />

 

  1. تغییر آدرس action به صورت داینامیک با استفاده از جاوااسکریپت
<form action="" method="post" id="EmployeeForm" >

...

...

<input type="submit" name="BtnSubmit" value="Save Employee" onclick="document.getElementById('EmployeeForm').action = '/Employee/SaveEmployee'" />

...

<input type="submit" name="BtnSubmit" value="Cancel" onclick="document.getElementById('EmployeeForm').action = '/Employee/CancelSave'" />

</form>

Ajax

 

به جای استفاده از دکمه submit از دکمه ساده استفاده کنیم و در رویداد کلیک آن با استفاده از jquery یا هر کتابخانه دیگری  درخواست ajax ایجاد کنیم.

چرا ما از input type=reset برای پیاده سازی کاربرد Reset استفاده نکردیم؟

این نوع ورودی مقادیر کنترل ها را پاک نمی کند، فقط مقادیر را روی مقادیر پیش فرض کنترل تنظیم می کند. مثال:

<input type=”text” name=”FName” value=”mspsoft”>

در مثال بالا، مقدار پیش فرض کنترل “mspsoft” است.

اگر ما از input type=reset برای پیاده سازی کاربرد Reset استفاده نماییم، هر بار که دکمه “reset” کلیک شود، مقدار پیش فرض “mspsoft” در textbox تنظیم می شود.

اگر نام های تعریف شده با نام هیچ یک از property های کلاس ها مطابقت نداشته باشد، چه اتفاقی می افتد؟

این یکی از سوالات متداول در مصاحبه ها است.

فرض کنید HTML ای مانند زیر داشته باشیم:

First Name: <input type="text" id="TxtFName" name="FName" value="" /><br />

Last Name: <input type="text" id="TxtLName" name="LName" value="" /><br />

Salary: <input type="text" id="TxtSalary" name="Salary" value="" /><br />

 

حال کلاس Model ما شامل property های FirstName،LastName و Salary می باشد. بنابراین Model Binder پیش فرض در این جا کار نمی کند.

در این جا ما سه راه حل داریم:

  • در action method، مقادیر post شده را با استفاده از Form بازیابی کرده و به طور دستی شی Model را ایجاد می کنیم.
public ActionResult SaveEmployee()

{

        Employee e = new Employee();

        e.FirstName = Request.Form["FName"];

        e.LastName = Request.Form["LName"];

        e.Salary = int.Parse(Request.Form["Salary"])

...

...

}

 

از نام پارامترها استفاده کرده و شی Model را به طور دستی ایجاد کنیم.

public ActionResult SaveEmployee(string FName, string LName, int Salary)

{

    Employee e = new Employee();

    e.FirstName = FName;

    e.LastName = LName;

    e.Salary = Salary;

...

...

}

 

  • ایجاد یک Model Binder سفارشی و جایگزین کردن آن با Model Binder پیش فرض

گام ۱: ایجاد Model Binder سفارشی

public class MyEmployeeModelBinder : DefaultModelBinder

{

    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)

    {

        Employee e = new Employee();

        e.FirstName = controllerContext.RequestContext.HttpContext.Request.Form["FName"];

        e.LastName = controllerContext.RequestContext.HttpContext.Request.Form["LName"];

        e.Salary = int.Parse(controllerContext.RequestContext.HttpContext.Request.Form["Salary"]);

        return e;

    }

}

 

گام ۲: جایگزین کردن Model Binder پیش فرض با Model Binder جدید

public ActionResult SaveEmployee([ModelBinder(typeof(MyEmployeeModelBinder))]Employee e, string BtnSubmit)

{

         ......

 

RedirectToFunction چه کاری انجام می دهد؟

یک RedirectToRoueResult درست مانند ViewResult و ContentResult (که در روز اول بحث شد) تولید می کند، RedirectToRouteResult از ActionResult ارث بری می کند و یک redirect response ارائه می دهد. زمانی که مرورگر RedirectToRouteResult دریافت می کند، یک درخواست جدید به action method جدید ایجاد می کند.

توجه داشته باشید که در اینجا به محض آپدیت شدن URL، در مقابل این درخواست باید پاسخگو باشد.

EmptyResult چیست؟

یکی دیگر از فرزندان ActionResult می باشد. زمانی که مرورگر یک EmptyResult به عنوان درخواست دریافت می کند، یک صفحه سفید خالی نمایش می دهد. به معنی ساده تر، یعنی “No Result” را نشان می دهد.

در مثال ما این وضعیت اتفاق نمی افتد. فقط برای اینکه مطمئن باشیم همه کدهای نوشته شده مقداری برمی گردانند، عبارت EmptyResult نوشته شده است.

توجه داشته باشید که وقتی مقدار برگشتی action method، void باشد معادل EmptyResult می باشد..

تمرین دوازدهم – ذخیره رکوردها در دیتابیس و به روز رسانی Grid

گام ۱: ایجاد SaveEmployee در کلاس EmployeeBusinessLayer به صورت زیر:

public Employee SaveEmployee(Employee e)

{

    SalesERPDAL salesDal = new SalesERPDAL();

    salesDal.Employees.Add(e);

    salesDal.SaveChanges();

    return e;

}

 

گام ۲: تغییر متد SaveEmployee

متد SaveEmployee موجود در EmployeeController را به صورت زیر تغییر می دهیم:

public ActionResult SaveEmployee(Employee e, string BtnSubmit)

{

    switch (BtnSubmit)

    {

        case "Save Employee":

            EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();

            empBal.SaveEmployee(e);

            return RedirectToAction("Index");

        case "Cancel":

            return RedirectToAction("Index");

    }

    return new EmptyResult();

}

 

گام ۳: تست و اجرای برنامه

کلید F5 را فشار داده و برنامه را اجرا می کنیم. به صفحه ثبت داده رفته و چند داده معتبر وارد می کنیم.

تمرین سیزدهم – اضافه کردن اعتبارسنجی سمت سرور

در تمرین دهم کاربردهای ابتدایی Model Binder را مشاهده کردیم. حالا می خواهیم کمی بیشتر با آن آشنا شویم.

  • Model Binder اشیای Employee را با داده های post شده به روز رسانی می کند.
  • اما این تنها کاری نیست که توسط Model Binder اجرا می شود. Model Binder همچنین ModelState را به روزرسانی می کند. ModelState در واقع حالت Model را مخفی (encapsulation) می کند.
    • Property ای به نام IsValid دارد که مشخص می کند Model (که شی Employee است) به درستی به روزرسانی شده است یا خیر. اگر هر یک از اعتبارسنجی های سمت سرور به درستی انجام نشود، Model به روزرسانی نخواهد شد.
    • خطاهای اعتبارسنجی را نگه می دارد.

مثال: ModelState[“FirstName”] تمام خطاهای مربوط به FirstName را نگه خواهد داشت.

  • مقادیر داده های ورودی را نگه می دارد. (داده های postشده یا query string)

در ASP.Net MVC برای اعتبارسنجی سمت سرور از DataAnnotation استفاده می کنیم.

قبل از اینکه وارد بحث Annotation شویم، می خواهیم باز هم کمی بیشتر درباره Model Binder بدانیم.

Model Binder چگونه با انواع داده ای اولیه کار می کند؟

زمانی که action method شامل پارامتر هایی با نوع داده اولیه باشد، Model Binder  نام پارامتر را با کلید هر یک از داده های ورودی مقایسه می کند (داده های ورودی می توانند داده های post شده یا query string باشند).

  • زمانی که موردی یافت شد، داده ورودی مربوطه به پارامتر یافت شده تخصیص داده می شود.
  • زمانی که موردی یافت نشد، پارامتر با مقدار پیش فرض مقداردهی می شود. (مقدار پیش فرض برای نوع int ، ۰ است و برای رشته ها null می باشد و…)
  • در این مورد تخصیص ممکن نیست، چون استثنای نوع داده اشتباه، رخ می دهد.

Model Binder با کلاس ها چگونه کار می کند؟

زمانی که پارامتر، یک پارامتر از کلاس باشد، Model Binder روی همه property های کلاس تکرار می شود و نام هر property را با کلید هر یک از داده های ورودی مقایسه می کند.

  • اگر موردی یافت شد،
    • اگر مقدار داده ورودی مربوطه خالی باشد، سپس
      • مقدار Null به آن property تخصیص داده می شود. اگر تخصیص null به آن امکان پذیر نباشد، مقدار پیش فرض به آن داده می شود و IsValid مقدار false برمی گرداند.
      • اگر تخصیص مقدار Null امکان پذیر باشد اما به دلیل اعتبارسنجی های اعمال شده بر property مقدار معتبری نباشد، مقدار null به آن داده می شود و IsValid مقدار false را برمی گرداند.
    • اگر مقدار داده ورودی خالی نباشد،
      • در این مورد، تخصیص مقدار غیرممکن است چرا که خطای نوع داده اشتباه یا اعتبارسنجی سمت سرور رخ می دهد. مقدار Null به property داده می شود و IsValid مقدار false برمی گرداند.
        • اگر تخصیص Null غیر ممکن باشد، مقدار پیش فرض در آن قرار می گیرد.
      • اگر موردی یافت نشد، پارامتر با مقدار پیش فرض مقداردهی می شود. (مقدار پیش فرض برای int عدد ۰ و برای رشته null می باشد و…). در این مورد IsValid بدون تغییر باقی می ماند.

حالا با اضافه کردن امکان اعتبارسنجی به پروژه خود، این موضوع را بهتر متوجه می شویم.

گام ۱: اضافه کردن DataAnnotation به property ها

کلاس Employee را از فولدر Model باز کرده DataAnnotation را به property های FirstName و LastName اضافه می کنیم:

 

public class Employee

{

...

...

    [Required(ErrorMessage="Enter First Name")]

    public string FirstName { get; set; }

 

[StringLength(5,ErrorMessage=”نام خانوادگی نباید بیش از ۵ کاراکتر باشد.”)]

 

    public string LastName { get; set; }

...

...

}

 

گام ۲: تغییر متد SaveEmployee

EmployeeController را باز کرده و متد SaveEmployee را تغییر می دهیم.

public ActionResult SaveEmployee(Employee e, string BtnSubmit)

{

    switch (BtnSubmit)

    {

        case "Save Employee":

            if (ModelState.IsValid)

            {

                EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();

                empBal.SaveEmployee(e);

                return RedirectToAction("Index");

            }

            else

            {

                return View("CreateEmployee ");

            }

        case "Cancel":

            return RedirectToAction("Index");

    }

    return new EmptyResult();

}

 

همان طور که مشاهده می کنید، زمانی که ModelState.IsValid مقدار false را برمی گرداند، پاسخ رویداد کلیک دکمه SaveEmployee یک ViewResult است که به View با نام “CreateEmployee” متصل می شود.

گام ۳: نمایش خطاها در View

فایل HTML در “Views/Index/CreateEmployee.cshtml” را به صورت زیر تغییر می دهیم.

در این قسمت، طراحی خود را به کمک تگ table کمی فرمت می دهیم.

 

<table>

   <tr>

      <td>

         First Name:

      </td>

      <td>

         <input type="text" id="TxtFName" name="FirstName" value="" />

      </td>

   </tr>

   <tr>

      <td colspan="2" align="right">

        @Html.ValidationMessage("FirstName")

      </td>

   </tr>

   <tr>

      <td>

        Last Name:

      </td>

      <td>

         <input type="text" id="TxtLName" name="LastName" value="" />

      </td>

   </tr>

   <tr>

      <td colspan="2" align="right">

         @Html.ValidationMessage("LastName")

      </td>

   </tr>



   <tr>

      <td>

        Salary:

      </td>

      <td>

         <input type="text" id="TxtSalary" name="Salary" value="" />

      </td>

   </tr>

   <tr>

      <td colspan="2" align="right">

        @Html.ValidationMessage("Salary")

      </td>

   </tr>



   <tr>

      <td colspan="2">

         <input type="submit" name="BtnSubmit" value="Save Employee" />

         <input type="submit" name="BtnSubmit" value="Cancel" />

         <input type="button" name="BtnReset" value="Reset" onclick="ResetForm();" />

      </td>

   </tr>

</table>

 

گام ۴: تست و اجرای برنامه

با کلید F5 برنامه را اجرا می کنیم. به متد “Employee/AddNew” رفته و برنامه را تست می کنیم.

تست ۱:

تست ۲:

توجه داشته باشید که ممکن است با خطاهای زیر مواجه شوید:

“The Model backing the ‘SalesERPDAL’ context has changed since the database was created. Consider using Code First Migrations to update the database.”

برای از بین بردن این خطا، عبارت زیر را در قسمت Application_Start در فایل Global.asax قرار می دهیم.

Database.SetInitializer(new DropCreateDatabaseIfModelChanges<SalesERPDAL>());

 

کلاس های دیتابیس در فضای نام System.Data.Entity قرار دارند.

اگر همچنان با این خطا روبه رو می شوید، دیتابیس را باز کرده و جدول _MigrationHistory را حذف نمایید.

پرسش و پاسخ در تمرین سیزدهم

@Html.ValidationMessage چه کاری انجام می دهد؟

  • @ نشان می دهد که این یک کد Razor است.
  • Html نشان می دهد که شی ای از کلاس HtmlHelper در View موجود است.
  • ValidationMessage تابعی از کلاس HtmlHelper است که پیغام خطا را نمایش می دهد.

تابع ValidationMessage چگونه کار می کند؟

ValidationMessage یک تابع است که در زمان اجرا، اجرا می شود. همان طور که پیش از این بحث کردیم، Model Binder، ModelState را به روزرسانی می کند. ValidationMessage پیغام خطا را بر اساس کلید از ModelState را نمایش می دهد.

مثال: ValidationMessage(“FirstName”) پیام های خطای مربوط به FirstName را نشان می دهد.

آیا صفات دیگری مانند required و StringLength نیز داریم؟

بله ، برخی از آنها در زیر آمده است:

  • DataType – بررسی می کند که داده وارد شده از نوع خاصی از داده ها مثل email، شماره حساب، آدرس اینترنتی و… می باشد.
  • EnumDataTypeAttribute – بررسی می کند که مقدار در یک مجموعه شمارشی وجود دارد.
  • Range Attribute – بررسی می کند که مقدار موردنظر در بازه خاصی قرار داشته باشد.
  • Regular expression – مقدار را بر اساس یک عبارت منظم (regular expression) اعتبارسنجی می نماید.
  • Required – بررسی می کند که حتما مقداری فراهم شryده باشد.
  • StringLength – رشته ها را برای حداقل و حداکثر تعداد کاراکتر اعتبارسنجی می کند.

Salary چگونه اعتبارسنجی می شود؟

ما هیچ DataAnnotation ای به Salary اضافه نکردیم اما باز هم اعتبارسنجی می شود. دلیل آن این است که Model Binder نیز نوع داده property ها را زمانی که model را به روزرسانی می کند، بررسی می کند.

در تست ۱ – مقدار salary را خالی گذاشتیم. در این مورد (همانطور که در تمرین ۱۳ توضیح دادیم) ModelState.IsVaild مقدار false را برمی گرداند و پیام خطای مربوط به اعتبارسنجی salary را نگه می دارد که به دلیل Html.ValidationMessage(“salary”) در View نمایش می دهد.

در تست ۲ – نوع داده Salary اشتباه است و مطابقت ندارد، بنابراین اعتبارسنجی با خطا مواجه می شود.

به این ترتیب، آیا propertyهای int به طور پیش فرض اجباری هستند؟

بله، نه تنها نوع int بلکه همه انواع داده ای چرا که نمی توانند مقدار null را بپذیرند.

حال اگر یک فیلد int با خصوصیت non-required بخواهیم چطور؟

آن را قابل null پذیری کنیم؟

 

public int? Salary{get;set;}

 

چگونه می توان پیغام خطای مخصوص Salary را تغییر دهیم؟

اعتبارسنجی پیش فرض Salary (به دلیل نوع داده ای int) به ما اجازه تغییر در پیام اعتبارسنجی را نمی دهد. با استفاده از اعتبارسنجی های خود مانند regular expression، range یا custom validator نیز به همان نتیجه دست پیدا می کنیم.

چرا زمانی که اعتبارسنجی با خطا مواجه می شود، مقادیر پاک می شوند؟

به این دلیل که یک درخواست جدید ایجاد شده است. DataEntry View که در آغاز اجرا شده بود و بعدا هم اجرا می شود از نظر برنامه نویسی یکی است اما از نظر درخواست متفاوت می باشند. در سری آموزش روز چهارم، یاد خواهیم گرفت که چگونه مقادیر را حفظ کنیم.

آیا ما می توانیم به طور مستقیم Model Binder را برای اجرا فراخوانی کنیم؟

بله به راحتی پارامترها را از action method حذف می کند و از اجرا شدن Model Binder پیش فرض جلوگیری می کند.

در این مثال، می توانیم از تابع UpdateModel به صورت زیر استفاده کنیم:

Employee e = new Employee();

UpdateModel<employee>(e);

</employee>

 

توجه داشته باشید که UpdateModel با انواع داده اولیه کار نمی کند.

تفاوت بین UpdateModel با TryUpdateModel چیست؟

TryUpdateModel کاملا شبیه به UpdateModel است به جز یک مزیتی که به آن اضافه شده است.

TryUpdateModel در صورتی که سازگاری Model به هر دلیلی با خطا مواجه شود، خطای استثنایی ایجاد می کند.

TryUpdateModel اشیای Employee را دقیقا مانند پارامترهای تابع نگه می دارد. اگر به روزرسانی با خطا مواجه شود، ModelState.IsValid مقدار false می گیرد.

در مورد اعتبارسنجی سمت کلاینت چطور؟

این اعتبارسنجی باید به صورت دستی انجام شوند تا زمانی که از کلاس های HTML Helper استفاده می کنیم.

در سری آموزشی روز چهارم، درباره هر دو روش اعتبارسنجی سمت کلاینت به صورت دستی و خودکار با استفاده از کلاس های HTML Helper صحبت خواهیم کرد.

آیا می توانیم بیش از یک صفت DataAnnotation به یک property اضافه کنیم؟

بله می توانیم. در این مورد هر دو اعتبارسنجی انجام خواهد شد.

تمرین چهاردهم – اعتبارسنجی سفارشی سمت سرور

گام ۱: ایجاد Custom Validation

فایل Employee.Cs را باز کرده و کلاس جدیدی با نام FirstNameValidation در آن ایجاد می کنیم.

public class FirstNameValidation:ValidationAttribute

{

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)

    {

        if (value == null) // Checking for Empty Value

        {

            return new ValidationResult("Please Provide First Name");

        }

        else

        {

            if (value.ToString().Contains("@"))

            {

                return new ValidationResult("نام کارمند نباید شامل کاراکتر @ باشد.");

            }

        }

        return ValidationResult.Success;

    }

}

 

نکته: ایجاد چند کلاس درون یک فایل به هیچ عنوان راهکار خوبی محسوب نمی شود. بنابراین به شما پیشنهاد می کنیم در پروژه خود یک پوشه جدید به نام “Validations” در فولدر ریشه پروژه ایجاد کرده و یک کلاس جدید در آن ایجاد کنید.

گام ۲: اضافه کردن این Attribute به FirstName

کلاس Employee را باز کرده و صفت پیش فرض “Required” را از FirstName حذف کرده و FirsNameValidation را به شکل زیر به آن اضافه نمایید.

 

[FirstNameValidation]

public string FirstName { get; set; }

 

گام ۳: تست و اجرا

کلید F5 را زده و به متد “Employee/AddNew” بروید.

تست ۱:

تست۲:

جمع بندی:

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

  • پیاده سازی اعتبارسنجی سمت کلاینت
  • معرفی HTML Helper
  • پیاده سازی احراز هویت
  • اضافه کردن فوتر با استفاده از Partial View
  • ایجاد لایه سازگار با Master Page
  • فیلتر کردن سفارشی درخواست ها

تمریین یادتون نره 🙂 تمرینات نیز ضمیمه شدن.

موفق باشید 🙂

فاطمه زکایی

فاطمه زکایی هستم. فارغ التحصیل کارشناسی مهندسی نرم افزار، مدت سه سال هست که در زمینه توسعه اپلیکیشن های تحت وب و اندروید و همچنین تولید محتوای تخصصی برنامه نویسی تحت وب و اندروید در مجموعه mspsoft در خدمت شما هستم.

نوشته‌های مرتبط

دیدگاه‌ها

*
*

این سایت از اکیسمت برای کاهش هرزنامه استفاده می کند. بیاموزید که چگونه اطلاعات دیدگاه های شما پردازش می‌شوند.

    محمد وکیلی پاسخ

    واقعا خسته نباشید.

      باران بزرگمهر پاسخ

      خواهش میکنم موفق باشید :)

        koushamanage پاسخ

        PDF این روز کجاست؟

    programmer پاسخ

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

      باران بزرگمهر پاسخ

      اضافه کردن این بخش در حال پیاده سازی است به زودی در قسمتی مشخص قرار خواهد گرفت.و در اخر نیز کتاب جامع ای از این دوره در اختیار کاربران قرار خواهد گرفت.

        programmer پاسخ

        با سلام
        ممنون از زحمات حضرتعالی مممنون ولی اگر الان نیز این فایل را در قالب pdf در هر دوره ضمیمه می گردید بسیار عالی و مفید بود.چرا که مطالعه آفلاین آ« بسیار مورد نیاز است.
        با تشکر

          باران بزرگمهر پاسخ

          انجام شد :) فایل PDF کلیه روز ها قرار گرفت.
          www.mspsoft.com/tag/نینجای-mvc/

    ُSeyedAli پاسخ

    با سلام
    ببخشید می خواستم بدونم که اگه من بخوام که داده های یک table رو post کنم بایستی چه کار کنم ؟
    چون تگ خصوصیت name رو نمی پذیره.

    ُSeyedAli پاسخ

    با سلام
    ببخشید می خواستم بدونم که اگه من بخوام که داده های یک table رو post کنم بایستی چه کار کنم ؟
    چون تگ تیبل دیتا یا td خصوصیت name رو نمی پذیره.

      مسعود شریفی پاسخ

      خوب چرا جدول را میخواید ارسال کنید ؟ فیلد ها را تگ تگ post کنید.

    SeyedAli پاسخ

    با سلام
    ببخشید می خواستم بدونم که اگه من بخوام که داده های یک table رو post کنم بایستی چه کار کنم ؟
    چون تگ تیبل دیتا یا تی دی خصوصیت name رو نداره.

      مسعود شریفی پاسخ

      از ID هم میتونید استفاده کنید به جای name

        SeyedAli پاسخ

        سلام جناب شریفی
        چند روز بعد از این که سوال خودم رو مطرح کردم جواب رو پیدا کردم و از همین روش رفتم
        ممنون بابت پاسخ دادن.
        با تشکر SeyedAli

    ُسید پاسخ

    با سلام
    ببخشید می خواستم بدونم که اگه من بخوام که داده های یک table رو post کنم بایستی چه کار کنم ؟
    چون تگ تیبل دیتا یا تی دی خصوصیت name رو نداره.

    مهتاب پاسخ

    سلام
    واقعا دستتون درد نکنه آموزش ها عالی بود
    فقط چرا pdf روز سوم رو قرار ندادید!
    خیلی خوب میشه اونم بزارید