تکنیک Code First باEntity Framework 6 درWeb API (بخش دوم)

یکشنبه 20 دی 1394

در این مقاله در ادامه مقاله قبل قصد داریم نحوه رسیدگی به موجودیت های مرتبط را بررسی و برنامه ای را با استفاده از کلاس های DTO در ASP.net Web API همراه با Entity Framework ایجاد کنیم.

تکنیک Code First باEntity Framework 6 درWeb API (بخش دوم)

در این مقاله در ادامه مقاله قبل قصد داریم نحوه رسیدگی به موجودیت های مرتبط را بررسی و برنامه ای را  با استفاده از کلاس های DTO در  ASP.net Web API همراه با Entity Framework   ایجاد کنیم.

عملی در کنترلر  Employee که جزئیات کارمندان را به ما می دهد به صورت زیر می باشد :

GET :/api/employees

و نتیجه آن به صورت زیر نمایش داده می شود :

 

 

از تصویر بالا متوجه می شویم که برای  Department مقدار  null  را دریافت کردیم. به این دلیل است که  Entity Framework موجودیت های Department  مرتبط را لود نکرده است.

گزارش Trace   کوئری SQL زیر،  این را تایید میکند :

{SELECT     [Extent1].[EmployeeID] AS [EmployeeID],     [Extent1].[FirstName] AS [FirstName],     [Extent1].[LastName] AS [LastName],     [Extent1].[DepartmentID] AS [DepartmentID]    FROM [dbo].[Employees] AS [Extent1]} 

 

دستور  Select  اطلاعات را از جدول Employee  میگیرد و جدول  Department  را منبع قرار نمی دهد. اکنون  Eager Loading  و Lazy Loading  به کار می آیند.

Eager Loading

 Entity Framework  موجودیت های مرتبط را  به عنوان بخشی از کوئری دیتابیس اولیه بارگذاری میکند. بجای عمل  GET در کنترلر  Employees  از کد زیر استفاده می شود :

public IQueryable<Employees> GetEmployees()    
{  
   return db.Employees.Include(dept=>dept.Department);  
}   

گزارش trace  کوئری  SQL به صورت زیر می باشد :

{SELECT     [Extent1].[EmployeeID] AS [EmployeeID],     [Extent1].[FirstName] AS [FirstName],     [Extent1].[LastName] AS [LastName],     [Extent1].[DepartmentID] AS [DepartmentID],     [Extent2].[DepartmentID] AS [DepartmentID1],     [Extent2].[DepartmentName] AS [DepartmentName]    FROM  [dbo].[Employees] AS [Extent1]    INNER JOIN [dbo].[Departments] AS [Extent2] ON [Extent1].[DepartmentID] = [Extent2].[DepartmentID]}

از گزارش بالا نتیجه میگیریم که Entity Framework   یک join  را در جداول  Employees و  department  اجرا میکند.

اکنون پاسخ سرویس  /api/employees به صورت زیر می باشد :

 

همین نتیجه را می توانیم با استفاده از Lazy Loading  بدست آوریم. برای فعال کردن  Lazy Loading  ، ویژگی navigation مجازی را مانند کد زیر ایجاد میکنیم.

public class Employees
    {
        [Required]
        [Key]
        public int EmployeeID {get; set;}

        [Required]
        public string FirstName { get; set; }

        public string LastName { get; set; }

        public int DepartmentID { get; set;} //Foreign Key    
        public virtual Department Department { get; set; } //Navigation Property    

    }  

 

 

 و در کنترلر  Employee متد  Get را به صورت زیر تغییر دهید.

var Employee = db.Employees.ToList();
            var dept = Employee[0].Department;  

ویژگی  Departmet بر روی  Employee[0] سبب می شود   Entity Framework به دیتابیس برای Department  کوئری بزند. گزارش trace  کوئری  SQL به صورت زیر خواهد بود.

{      SELECT[Extent1].[EmployeeID] AS[EmployeeID], [Extent1].[FirstName] AS[FirstName], [Extent1].[LastName] AS[LastName], [Extent1].[DepartmentID] AS[DepartmentID] FROM[dbo].[Employees] AS[Extent1]  }   
{      SELECT[Extent1].[DepartmentID] AS[DepartmentID], [Extent1].[DepartmentName] AS[DepartmentName] FROM[dbo].[Department] AS[Extent1] where[Extent1].[DepartmentID] = @EntityKeyValue1  
}    
{      SELECT[Extent1].[DepartmentID] AS[DepartmentID], [Extent1].[DepartmentName] AS[DepartmentName] FROM[dbo].[Department] AS[Extent1] where[Extent1].[DepartmentID] = @EntityKeyValue1  
}   
{      SELECT[Extent1].[DepartmentID] AS[DepartmentID], [Extent1].[DepartmentName] AS[DepartmentName] FROM[dbo].[Department] AS[Extent1] where[Extent1].[DepartmentID] = @EntityKeyValue1  
} 

 

Lazy Loading در  Entity Framework  هر بار یک کوئری برای بازیابی موجودیت های مرتبط ارسال میکند.

منابع مدور

یک ویژگی  Navigation  را در کلاس  Employee برای ارتباط  Employee-Department تعریف کردیم. فرض کنید که میخواهیم ویژگی  Navigation  را برای کلاس Department  تعریف کنیم ، هنگامی که مدل ها را Serialize کنیم مسئله ای ایجاد می شود.  اگر داده های مرتبط را بارگذاری کنیم یک گراف مدور ایجاد می شود.

public class Department
    {
        [Required]
        public int DepartmentID { get; set; }
        [Required]
        public string DepartmentName { get; set; }
        public ICollection<Employees> Employees { get; set; }

    }  

اکنون پاسخ سرویس /api/Employee به صورت زیر خواهد بود.

این مسئله را می توانیم با استفاده از  Data Transfer Object    یا DTO  حل کنیم.

یک  DTO  عنصری است که نحوه ارسال داده بر روی شبکه را تعریف میکند. یک پوشه با نام  DTO در پروژه اضافه کنید و با راست کلیک بر روی آن دو کلاس با نام های EmployeeDetailDTO و   EmployeeDTO  ایجاد کنید.

کدها در  EmployeeDetailDTOبه صورت زیر می باشد:

public class EmployeeDetailDTO
    {
        public int EmployeeID { get; set; }
        public string FristName { get; set; }

        public string LastName { get; set; }

        public string DepartmentName { get; set; }
        public int DepartmentID { get; set; }

    }  

و در EmployeeDTO به صورت زیر است:

public class EmployeeDTO
    {
        public int EmployeeID { get; set; }
        public string FristName { get; set; }
        public string DepartmentName { get; set; }
    }  

اکنون عمل API را در کنترلر Employee تغییر می دهیم.

public IQueryable<EmployeeDTO> GetEmployees()
        {
            var employeedetail = from s in db.Employees
                                 select new EmployeeDTO()
                                 {
                                     EmployeeID = s.EmployeeID,
                                     FristName = s.FirstName,
                                     DepartmentName = s.Department.DepartmentName

                                 };
            return employeedetail;

        } 

 پاسخ به صورت زیر خواهد بود.

عمل  Get به همراه  ID به صورت زیر انجام می شود :

[ResponseType(typeof(EmployeeDetailDTO))]
            public async Task<IHttpActionResult> GetEmployees(int id)
            {
                EmployeeDetailDTO employees = await db.Employees.Include(s => s.Department).Select(
                    b => new EmployeeDetailDTO()
                    {
                        EmployeeID = b.EmployeeID,
                        FristName = b.FirstName,
                        LastName = b.LastName,
                        DepartmentName = b.Department.DepartmentName,
                        DepartmentID = b.DepartmentID
                    }).SingleOrDefaultAsync(c => c.EmployeeID == id);


                if (employees == null)
                {
                    return NotFound();
                }

                return Ok(employees);
            }  

که پاسخ برای  Id=2به صورت زیر خواهد بود.

 

 

عمل Post :

 این سرویس برای اضافه کردن رکوردهای  Employee   استفاده می شود :

[ResponseType(typeof(Employees))]
        public async Task<IHttpActionResult> PostEmployees(Employees employees)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            db.Employees.Add(employees);
            await db.SaveChangesAsync();
            db.Entry(employees).Reference(s => s.Department).Load();

            var StoreEmp = new EmployeeDTO()
            {
                EmployeeID = employees.EmployeeID,
                FristName = employees.FirstName,
                DepartmentName = employees.Department.DepartmentName

            };


            return CreatedAtRoute("DefaultApi", new
            {
                id = employees.EmployeeID
            }, StoreEmp);
        }

عمل  Put :

این سرویس برای آپدیت رکوردهای   Employee استفاده می شود :

[ResponseType(typeof(void))]
            public async Task<IHttpActionResult> PutEmployees(int id, Employees employees)
            {
                if (!ModelState.IsValid)
                {
                    return BadRequest(ModelState);
                }

                if (id != employees.EmployeeID)
                {
                    return BadRequest();
                }

                db.Entry(employees).State = EntityState.Modified;

                try
                {
                    await db.SaveChangesAsync();


                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!EmployeesExists(id))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }

                return StatusCode(HttpStatusCode.NoContent);
            }  

 

با نرم افزار    Postman می توانید نتیجه این کدها را در مرورگر خود ببینید.

 در مقاله بعد درباره مسیر یابی در  ASP.net Web API صحبت خواهیم کرد.

 

 

 

فایل های ضمیمه

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

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

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

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