شروع کار با MVC 6 Web API و Entity Framework 7

پنجشنبه 22 مهر 1395

یکی از ویژگی های اصلی و جدید ASP.Net 5 متحد کردن programming model and combining MVC, Web API, and Web Pages و قرار دادن همه آنها در یک framework به نام MVC 6 است .در نسخه قبلی ASP.Net اشتراکی در بین ویژگیهای MVC و Web API وجود داشت ، اما پیاده سازی برای هر دو کتابخانه کاملا متفاوت بود ، با آمدن ASP.Net MVC 5 ادغام بین این تفاوتهای کتابخانه ، آن را برای توسعه برنامه های web سهولت بخشید و reusability کدهای آن را بالا برد .

شروع کار با MVC 6 Web API و Entity Framework 7

در این مقاله ،  هدف ما این است که نگاهی بر Asp.Net MVC 5 – MVC 6 Web API   داشته باشیم ، با استفاده از
MVC 6 Web API   و  Entity Framework   یک API  خیلی ساده خواهیم ساخت . همچنین در این مقاله خواهیم آموخت :

•  استفاده از  ASP.Net empty Template  برای ساختن web API

•  نگاهی اجمالی بر ساختار یک پروژه جدید در VS 2015    و چگونگی استفاده از  dependency management tool

• پیکربندی ASP.Net Pipline  برای اضافه کردن عناصری که برای Web API  ما نیاز است .

برای ادامه این مقاله شما به visual studio 2015 نیاز دارید .

مرحله اول : Visual Studio 2015 را باز کرده و همانطور که در عکس زیر نشان داده شده است یک
ASP.Net web Application
انتخاب کنید . 


حال ، در پنجره ای که باز شده است ، ASP.NET 5 Empty انتخاب میکنیم . 



مرحله دوم : اضافه کردن وابستگی های مورد نیاز
زمانی که پروژه ساخته می شود شما باید توجه داشته باشید که در اینجا فایلی به نام Project.json
 
  وجود دارد که حاوی تمام تنظیمات پروژه شما می باشد همراه با قسمتی برای مدیریت وابستگی ها در کتابخانه های دیگر .


ما با استفاده از
nuget package manager ، packageها و dependecyها را مدیریت میکنیم ، که این کار را میتوانید با افزایش ابزار ها انجام دهید . اما ما در این مورد تمام وابستگی ها را به فایل project.json .اضافه میکنیم . همانند عکس زیر :



بنابراین ما تمام وابستگی هایی که برای پیکربندی Web API لازم است را اضافه میکنیم ، پس ، فایل  project.json را باز میکنیم و کدهای قسمت dependencies را با کد های زیر جا بجا میکنیم :

ependencies": {
        "Microsoft.AspNet.Server.IIS": "1.0.0-beta1",
        "EntityFramework": "7.0.0-beta1",
        "EntityFramework.SqlServer": "7.0.0-beta1",
        "EntityFramework.Commands": "7.0.0-beta1",
        "Microsoft.AspNet.Mvc": "6.0.0-beta1",
        "Microsoft.AspNet.Diagnostics": "1.0.0-beta1",
        "Microsoft.Framework.ConfigurationModel.Json": "1.0.0-beta1"
    }

کاربرد وابستگی هایی که ما اضافه کرده ایم به شکل زیر است :

• Microsoft.AspNet.Server.IIS : ما قصد میزبانی از Web API  را توسط IIS  داریم ، بنابراین این package  نیاز است . اگر برنامه ریزی شما به این صورت که میزبانی به روش دیگری باشد دیگر نیازی به اضافه کردن این ، نیست .

•  EntityFramework & EntityFramework.SqlServer : نگهدارنده داده ها در web API  ما sql server   خواهد بود .
 entity framwork 7  می تواند برای کار با نگهدارنده داده دیگری پیکربندی شود و حتما هم نباید پایگاه داده باشد ، نگهدارنده های داده ای که  توسط  E7 پشتیبانی میشوند عبارت اند از : SqlServer, SQLite, AzureTableStorage, and InMemory

EntityFramework.Commands : این package  به دلیل در دسترس قرار دادن دستور مهاجر پایگاه داده برای پروژهweb API  بوسیله KVM   مورد استفاده قرار میگیرد .

Microsoft.AspNet.Mvc : این اصلی ترین packageای است
که تمام عناصر مورد نیاز برای اجرای MVC و Web API 
را اضافه میکند .

Microsoft.AspNet.Diagnostics : اساسا از این برای نمایش یک صفحه خوش آمد گویی زیبا وقتی که درخواست در مرورگر فرستاده میشود ، استفاده میشود . شما اگر میخواهید میتوانید جلوی این را بگیرید ، اما این یک صفحه خوش آمدگویی زیبا را به جای نمایش صفحه 403 که در 2  مورد استفاده قرار میگرفت ، نشان می دهد . 




Microsoft.Framework.ConfigurationModel: این package مسئولیت بارگذاری و خواندن فایل پیکربندی به نام
config.json را بر عهده دارد . که این فایل را در مرحله های بعدی اضافه خواهیم کرد . این فایل مسئول تنظیم
“IConfiguration” object  است .

آخرین چیزی که نیاز است که به فایل project.json اضافه شود قسمتی است به نام  commands که در قطعه کد زیر آمده است . 

"commands": {
        "ef": "EntityFramework.Commands"
}

ما پسوند "ef"  را برای  EntityFramework.Commands اضافه میکنیم که به اجازه نوشتن دستورات EF را میدهد .

مرحله سوم : اضافه کردن فایل پیکربندی Config.json
حال روی پروژه کلیک راست کرده و یک item جدید از نوع asp.net configuration file اضافه کنید و نام آن را config.json بگذارید ، شما میتوانید این فایل را میراث فایل web.config  نظر بگیرید ، فعلا این فایل فقط شامل پایگاه داده است . 

    "Data": {
        "DefaultConnection": {
            "Connectionstring": "Data Source=.\\sqlexpress;Initial Catalog=RegistrationDB;Integrated Security=True;"
        }
    }
}


توجه داشته باشید که  این یک فایل Json است ، به همین دلیل است که ما در   connection string
 از  escape character استفاده می کنیم .

مرحله چهارم : پیکربندی ASP.Net 5 pipeline برای web API

این کلاس برای اضافه کردن عناضر مورد نیاز Pipline ما ، مسئول است ، در حال حاضر با
  ASP.net 5 empty کلاس ما خالی است و پروژه ما واقعا کاری را انجام نمی دهد . ابتدا ما کدهایی را به کلاس
Startup  اضافه خواهیم کرد سپس خط ب خط آن را توضیح می دهیم . کلاس Startup.cs را باز کرده و کدهای زیر را در آن قرار دهید . 

using System;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Hosting;
using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection;
using Registration_MVC6WebApi.Models;
 
namespace Registration_MVC6WebApi
{
    public class Startup
    {
        public static IConfiguration Configuration { get; set; }
 
        public Startup(IHostingEnvironment env)
        {
            // Setup configuration sources.
            Configuration = new Configuration().AddJsonFile("config.json").AddEnvironmentVariables();
        }
        public void ConfigureServices(IServiceCollection services)
        {
            // Add EF services to the services container.
            services.AddEntityFramework().AddSqlServer().AddDbContext<RegistrationDbContext>();
 
            services.AddMvc();
 
            //Resolve dependency injection
            services.AddScoped<IRegistrationRepo, RegistrationRepo>();
            services.AddScoped<RegistrationDbContext, RegistrationDbContext>();
        }
        public void Configure(IApplicationBuilder app)
        {
            // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
            app.UseMvc();
 
            app.UseWelcomePage();
 
        }
    }
}


چیزی که ما در بالا پیاده سازی کردیم به شرح زیر است :

constructor در این کلاس مسئول خواندن تنظیمات از فایل پیکربندی config.json می باشد که پیش تر آن را تعریف کردیم  در حال حاضر ما فقط connection string را داریم . پس object ایستا configuration شامل این تنظیمات میشود که در مراحل پیش رو از آن استفاده خواهیم کرد .

متد ConfigurationServises پارامتری از نوع IServicesCollection  می پذیرد ، زمانی که پروژه شروع به کار می کند این متد بصورت خودکار فراخوانی میشود ، این متد اساسی مسئول ثبت عناصر در Pipline ما است ، خب ، عناصری که ما ثبت خواهیم کرد :

- ما Entity Framework را با استفاده از SQL Server به عنوان data provider برای محتوای پایگاه داده با نام RegistrationDBContext به پروژه اضافه میکنیم .

- مولفه های  MVC را به Pipline خود اضافه می کنیم ، سپس ، ما میتوانیم از  MVC و Web API  استفاده کنیم .

- آخرین و یکی از ویژگی های زیبا و  خارج از چارچوبی که به ASP.Net 5 اضافه شده است
Dependency Injection است بدون استفاده از هرگونه IoC Container  بیرونی ، توجه داشته باشید که ما چگونه نمونه مورد نظر از IRegistrationRepo را با فراخوانی  
services.AddScoped<IRegistrationRepo, RegistrationRepo>(); ایجاد کردیم .
این نمونه تا زمان وجود داشتن درخواست در دسترس می باشد . ما کلاس های IRegistrationRepo و
RegistrationRepo را در مراحل بعدی پیاده سازی خواهیم کرد  .

در پایان ، متد Configure پارامتری از نوع  IApplicationBuilder را می پذیرد ، این متد ، را برای استفاده از MVC و نمایش صفحه خوش‌آمد گویی  پیکربندی میکند .

مرحله پنجم : اضافه کردن Models , DataBase Context  و Respository
حال فایلی با نام model را که شامل دو کلاس است را اضافه میکنیم  : course و CourseStatusModel ، این کلاسها دامنه داده های ما را نمایش خواهند داد ، پس ، برای سازماندهی بهتر کد ، فولدری را به نام Models را اضافه می کنیم  سپس فایلی را که شامل کدهای زیر است را به آن اضافه میکنیم . 

using System;
using System.ComponentModel.DataAnnotations;
 
namespace Registration_MVC6WebApi.Models
{
    public class Course
    {
        public int Id { get; set; }
        [Required]
        [StringLength(100, MinimumLength = 5)]
        public string Name { get; set; }
        public int Credits { get; set; }
    }
 
    public class CourseStatusModel
    {
        public int Id { get; set; }
        public string Description { get; set; }
    }
}

حال نیاز است که ما کلاس  DataBase Context را که مسئولیت ارتباط با پایگاه داده ما را دارد را اضافه کنیم ، خب ، کلاسی را اضافه میکنیم و نام آن را RegistrationDbContext میگذاریم سپس کدهای زیر را به آن اضافه میکنیم :

using Microsoft.Data.Entity;
using System;
using Microsoft.Data.Entity.Metadata;
 
namespace Registration_MVC6WebApi.Models
{
    public class RegistrationDbContext :DbContext
    {
        public DbSet<Course> Courses { get; set; }
 
        protected override void OnConfiguring(DbContextOptions options)
        {
            options.UseSqlServer(Startup.Configuration.Get("Data:DefaultConnection:ConnectionString"));
        }
    }
}


اساسا چیزی که در اینجا پیاده سازی کردیم  ، را به عنوان Dbset اضافه میکند . بنابراین زمانی که یکبار 
courses data model  
را اجرا میکنیم ، جدول پایگاه داده را نمایش می دهد ، توجه داشته باشید که در اینجا یک متد جدید با نام وجود دارد که ما میتوانیم آن را بازنویسی کنیم ، بنابراین ما میتوانیم data provider هایی که برای کار کردن با DB Context  نیاز داریم را نمایش دهیم .

در این مورد ، ما از SQL Server
استفاده میکنیم . Constructor برای extension  متدِ UserSqlServer پارامتری از نوع Connection String  میپذیرد ، خب ، ما آن را از فایل Config.json ، با نشان دادن کلید
Data : DefaultConnection:ConnectionString برای شی Configuration  ای که ما پیش تر آن را در کلاس Startup ایجاد کردیم
 
توجه داشته باشید که این راهی بهینه برای تنظیم
Connection String  نمی باشد .

درآخر ما نیاز داریم که
IRegistrationRepo  را اضافه کنیم و در این  Interface بدنه RegistrationRepo   را پیاده سازی میکنیم ، پس در فولدر Models  دوفایل با نام های  RegistrationRepo   و IRegistrationRepo   را اضافه میکنیم و دو تکه کد زیر را در آنها قرار میدهیم :


IRegistrationRepo Code

using System;
using System.Collections;
using System.Collections.Generic;
 
namespace Registration_MVC6WebApi.Models
{
    public interface IRegistrationRepo
    {
        IEnumerable<Course> GetCourses();
        Course GetCourse(int courseId);
        Course AddCourse(Course course);
        bool DeleteCourse(int courseId);
    }
}

RegistrationRepo implementation

using System;
using System.Collections.Generic;
using System.Linq;
 
 
namespace Registration_MVC6WebApi.Models
{
    public class RegistrationRepo : IRegistrationRepo
    {
        private readonly RegistrationDbContext _db;
 
        public RegistrationRepo(RegistrationDbContext db)
        {
            _db = db;
        }
        public Course AddCourse(Course course)
        {
            _db.Courses.Add(course);
 
            if (_db.SaveChanges() > 0)
            {
                return course;
            }
            return null;
 
        }
 
        public bool DeleteCourse(int courseId)
        {
            var course = _db.Courses.FirstOrDefault(c => c.Id == courseId);
            if (course != null)
            {
                _db.Courses.Remove(course);
                return _db.SaveChanges() > 0;
            }
            return false;
        }
 
        public Course GetCourse(int courseId)
        {
            return _db.Courses.FirstOrDefault(c => c.Id == courseId);
        }
 
        public IEnumerable<Course> GetCourses()
        {
            return _db.Courses.AsEnumerable();
        }
    }
}

پیاده سازی در اینجا واقعا آسان است ، چیزی که در اینجا هیچ ارزشی ندارد چگونگی ارسال RegistrationDbContext به عنوان پارامتر به سازنده  RegistrationRepo است ، پس ما Constructor Injection را پیاده سازی میکنیم . اگر ما آن را پیش تر  در کلاس Startup پیکربندی نکرده باشیم ، کار نمیکند . 

مرحله ششم : نصب (KNM (K Version Manager :
بعد از اینکه ما DataBase Context و domain data models را اضافه کردیم ،ما از migrations برای ایجاد  database میتوانیم استفاده کنیم ، در نسخه های قبلی Asp.Net ما از NuGet Package Manager برای این نوع کارها استفاده میکردیم ، اما در ASP.Net MVC 5 ما میتواینم با استفاده از دستورات *K از command prompt استفاده کنیم . 

KVM چیست ؟ - یک اسکریپت PowerShell است برای گرفتن runtime و مدیریت چند نسخه از آن در ماشین وجود دارد در یک زمان یکسان ، استفاده میشود . 

حال به سراغ نصب KVM میرویم ، مراحل زیر را دنبال کنید :

1. command prompt را باز کنید با Run as Administrator .
2. دستور زیر را اجرا کنید 

@powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/aspnet/Home/master/kvminstall.ps1'))"

3. این اسکریپت KVM را برای user جاری سیستم ، نصب میکند. 
4. از command prompt خارج شوید و دوباره با Run as Administrator وارد آن شوید . 
5. KVM را با دستور زیر بروزرسانی کنید .

KVM upgrade

حال ما آماده اجرای EF migrations طبق مراحل زیر هستیم :

مرحله هفتم : مقدار دهی اولیه و استفاده از migrationها در database

حال cmd دستورات K و Entity Framework را میشناسد ، اولین کاری که باید انجام دهیم آدرس دایرکتوری را به آدرس دایرکتوری پروژه تغییر دهیم . همانطور که در عکس زیر مشاهده میکنید دایرکتوری پروژه شامل فایل project.json میباشد :




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

k ef migration add initial
k ef migration apply


اساسا دستور اول ، فایل migration را با فرمت نام (<date>_<migration name>) اضافه میکند بنابراین ما فایلی با نام initial.cs _201411172303154 در زیر شاخه MIGRATIONS در پروژه خواهیم داشت . 
ساختار پروژه ما به شکل زیر خواهد بود :




دستور دوم ، migration ها را تایید خواهد کرد و پایگاه داده را بر اساس Connection Stringای که پیش تر در config.json نشان دادیم ،  برای ما ایجاد میکند .

توجه داشته باشید که دستور "ef" از تنظیماتی که پیش تر در فایل project.json نشان دادیم ، می آید . 

مرحله هشتم : اضافه کردن متد GET برای  Courses Controller :

Controller کلاسی است که مسئول مدیریت و اداره درخواست های HTTP است ، در Web API Controller ، ASP.Net 5 ما از کلاس Controller ارث بری خواهد کرد ، پس ، فولدری به نام Controller اضافه می کنیم ، سپس Controllerی با نام 
CoursesController اضافه میکنیم و کد های زیر را به آن اضافه میکنیم :

using Microsoft.AspNet.Mvc;
using Registration_MVC6WebApi.Models;
using System;
using System.Collections.Generic;
 
namespace Registration_MVC6WebApi.Controllers
{
    [Route("api/[controller]")]
    public class CoursesController : Controller
    {
        private IRegistrationRepo _registrationRepo;
 
        public CoursesController(IRegistrationRepo registrationRepo)
        {
            _registrationRepo = registrationRepo;
        }
 
        [HttpGet]
        public IEnumerable<Course> GetAllCourses()
        {
            return _registrationRepo.GetCourses();
        }
 
        [HttpGet("{courseId:int}", Name = "GetCourseById")]
        public IActionResult GetCourseById(int courseId)
        {
            var course = _registrationRepo.GetCourse(courseId);
            if (course == null)
            {
                return HttpNotFound();
            }
 
            return new ObjectResult(course);
        }
 
    }
}

چیزی که در کلاس Controller پیاده سازی کرده ایم به شرح زیر است :

Controller صفتی است با صفت Route به شکل [Route("api/[controller]")بنابراین هر درخواست HTTP که تطابق داشته باشد به Controller هدایت میشود . 
[Controller] که در URL قرار دارد ، نام Controller در اینجا قرار میگیرد . در مثال ما در route که Controllerی با نام Courses داریم route ما api/Courses میشود .
ما دو متد HTTP GET تعریف کردیم  ، اولین آن GetAllCourses که دارای صفت [HttpGet] میباشد که یک شی Net. که با استفاده از فرمت json ، رمزگذاری شده است ، را بازمیگرداند .
دومین آنها ،  GetCoursesById با صفت [HttpGet] میباشد . که یک پارامتر CourseId از نوع integer دریافت میکند . آخرین چیز اینکه ، این متد یک object از نوع IActionResult باز میگرداند که این قابلیت را به ما میدهد که بر اساس منطق خودمان action های متفاوتی را بازگردانیم .
در این مورد اگر Course وجود نداشته باشد ما HttpNotFound را باز خواهیم گرداند یا اگر Course وجود داشته ما شی کدگذاری شده را باز میگردانیم . 
در آخر توجه داشته باشید که  چگونه IRegistrationRepo را برای سازنده CoursesController ارسال کردیم ، این Constructor Injection است . 


مرحله نهم : اضافه کردن متدهای Post و DELETE برای Courses Controller :

حال قصد داریم دو متد HTTP دیگر را پیاده سازی کنیم که به ما اجازه اضافه کردن یک course جدید یا حذف کردن یکcourse را میدهد ، پس ، فایل CoursesController را باز کرده و کدهای زیر را در آن قرار دهید :


HttpPost]
public IActionResult AddCourse([FromBody] Course course)
{
	if (!ModelState.IsValid)
	{
		Context.Response.StatusCode = 400;
		return new ObjectResult(new CourseStatusModel { Id = 1 , Description= "Course model is invalid" });
	}
	else
	{
	   var addedCourse =  _registrationRepo.AddCourse(course);
 
		if (addedCourse != null)
		{
			string url = Url.RouteUrl("GetCourseById", new { courseId = course.Id }, Request.Scheme, Request.Host.ToUriComponent());
 
			Context.Response.StatusCode = 201;
			Context.Response.Headers["Location"] = url;
			return new ObjectResult(addedCourse);
		}
		else
		{
			Context.Response.StatusCode = 400;
			return new ObjectResult(new CourseStatusModel { Id = 2, Description = "Failed to save course" });
		}
	   
	}
}

چیزی که ما پیاده سازی کرده ایم به شرح زیر است :

برای متد AddCourse :
 متد HTTP POST که مسئول ایجاد یک  course جدید است را اضافه میکنیم  ، این متد course objectی را که از بدنه درخواست آمده است را میپذیرد سپس web API framework آن را برای CLR  رمزگشایی میکند . 
اگر course object معتبر نبود (مثلا اصن وجود نداشت ) ، ما وضعیت HTTP 400 را باز خواهیم گردانند ، و در بدنه ی پاسخ ما یک نمونه از کلاس (POCO (courseStatusModel باز خواهیم گرداند . 
اگر course با موفقیت ایجاد شده بود ، ما یک id برای دسترسی به course تنظیم میکنیم مثلا :  api/courses/4 .
در آخر هم ما course را در صفحه پاسخ باز میگردانیم . 

برای متد DeleteCourse :
متد HTTP Delete که مسئول حذف یک Course موجود است را ، اضافه میکنیم .این متد یک پارامتر از نوع integer دریافت میکند . اگر course با موفقیت حذف شد ما وضعیت HTTP 204 را باز میگردانیم . (Not Content)
اگر پارامتر ارسالی برای متد نامعتبر باشد ما وضعیت HTTP 404 را بازمیگردانیم . 

آموزش asp.net mvc

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

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

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید