در این جلسه به بررسی نحوه اتصال موجودیت در EF Core و وب اپلیکشن ASP.NET Core خواهیم پرداخت. همچنین، روش جستجو در EF Core و ایجاد Query مناسب با استفاده از Lambda Expression (LINQ) را شرح خواهیم داد. در جلسات گذشته موجودیتهای مختلفی برای وب اپلیکشن خود ایجاد نمودیم که برخی از آنها با هم در ارتباط بودند. در این جلسه به اتصال این موجودیتها جهت نمایش در نما و گزارشات خواهیم پرداخت. سپس روش جستجو در لیست را با استفاده از EF Core و LINQ را با جزئیات کامل بررسی میکنیم.
اتصال موجودیت در EF Core
جهت اتصال موجودیت در EF Core میبایست از متد Join استفاده نماییم. با استفاده ازاین متد، میتوانیم یک موجودیت (جدول) را به یک یا چندین موجودیت (جدول) دیگر متصل نماییم. همانگونه که در تصویر زیر مشاهده میکنید، در لیست نمایش داده شده رکوردهای Cost، به جای نمایش نام Category مقدار ID آن نمایش داده شده است.

برای پیادهسازی اتصال بین این دو موجودیت و نمایش آن در نما متناظر، ابتدا نیاز به یک ViewModel جدید خواهیم داشت که دارای فیلدهای متناسب با نمای مورد نظر باشد.
public class CostList
{
public int ID { get; set; }
public decimal Amount { get; set; }
public string Comment { get; set; }
public string CategoryName { get; set; }
public PaymentMethods? PaymentMethod { set; get; }
}
سپس درون ICostRepository یک متد جدید ایجاد نموده که خروجی آن از نوع لیستی از ViewModel فوق میباشد.
public interface ICostRepository
{
Cost GetCostByID(int id);
IEnumerable<Cost> GetAllCost();
Cost Create(Cost NewCost);
Cost Update(Cost UpdateCost);
Cost Delete(int id);
List<CostList> GetCostList(string searchby, string searchfor);
}
سپس اقدام به پیادهسازی متد در ریپازیتوری یا ریپازیتوریها میکنیم.
public List<CostList> GetCostList()
{
var CL = context.Costs.Join(context.Categories, costen => costen.CategoryID, caten => caten.ID, (costen, caten) => new { costen, caten }).
Select(sel => new
{
sel.costen.ID,
sel.costen.Amount,
sel.costen.Comment,
sel.costen.PaymentMethod,
sel.caten.CategoryName
}).ToList();
List<CostList> costList = new();
foreach (var cost in CL)
{
costList.Add(new CostList
{
ID = cost.ID,
Amount = cost.Amount,
Comment = cost.Comment,
PaymentMethod = cost.PaymentMethod,
CategoryName = cost.CategoryName
});
}
return costList;
}
همانگونه که در قطعه کد فوق مشاهده میکنید، عملیات اتصال در قسمت ریپازیتوری انجام گردید و با استفاده از متد Join اتصال بین دو موجودیت برقرار گردید. سپس میتوانیم از این متد جدید در قسمتهای مورد نیاز استفاده نماییم.
[HttpGet]
public IActionResult Index()
{
var costs = costRepository.GetCostList();
return View(costs);
}
همچنین تغییرات زیر را در نمای Cost/Index اعمال میکنیم.
@model IEnumerable<CostList>
@{
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.CategoryName</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>
در نتیجه لیست فوق به شکل زیر تغییر کرده و نام دستهبندی به جای کد آن نمایش داده میشود.

جستجو در EF Core
جهت جستجو در پایگاه داده و نمایش لیستی که مطابق با شرایط جستجو باشد، میتوانیم از متد Where استفاده نماییم. در این متد و با استفاده از Lambda Expression میتوانیم از عملگرهای مقایسهایی مانند بزرگتر، کوچکتر، برابر یا نا برابر و یا همچنین متدهای Contains، Equal، StartsWith و EndsWith نیز استفاده نمود. ما در این جلسه از عملگر برابر با و Contains استفاده خواهیم نمود. شما به عنوان تمرین میتوانید از دیگر عملگرها استفاده نمایید. ضمنا متد Contains برای زمانی استفاده میگردد که ما به دنبال رشتههایی میگردیم که حاوی عبارت مورد نظر ما باشد.
برای اعمال این تغییرات، ابتدا در نمای خود فرم مربوط به جستجو را اضافه میکنیم.
<form asp-controller="Cost" asp-action="Index" method="get" class="my-2 mx-2">
<div class="input-group">
<select class="form-select" id="inputSearch" name="searchby">
<option selected value="">Search by...</option>
<option value="comment">Comment</option>
<option value="category">Category</option>
</select>
<input name="searchfor" class="form-control" id="inputSearch" aria-describedby="inputSearchComment" aria-label="Search">
<button class="btn btn-outline-secondary" type="submit" id="inputSearch">Search</button>
<a class="btn btn-outline-primary" type="button" asp-controller="cost" asp-action="index">Clear Search</a>
</div>
</form>
همانگونه که در قطعه کد فوق مشاهده میکنید، ما یک فرم به نمای Cost/Index اضافه نمودیم. متد این فرم Get میباشد و عبارات مربوط به جستجو را بوسیله Query String به کنترلر ارسال میکند. پس ما باید در کنترلر و اکشن متد مربوطه، تغییراتی اعمال نماییم تا عبارات جستجو را بتوانیم به کنترلر و سپس ریپازیتوری ارسال نماییم.
[HttpGet]
public IActionResult Index(string searchby, string searchfor)
{
//var costs = costRepository.GetAllCost();
var costs = costRepository.GetCostList(searchby, searchfor);
return View(costs);
}
حال میبایست تغییرات مورد نظر را برای اینترفیس و ریپازیتوری اعمال نماییم.
public interface ICostRepository
{
Cost GetCostByID(int id);
IEnumerable<Cost> GetAllCost();
Cost Create(Cost NewCost);
Cost Update(Cost UpdateCost);
Cost Delete(int id);
List<CostList> GetCostList(string searchby, string searchfor);
}
سپس با توجه به تغییرات اینترفیس، ریپازیتوری متناظر خود را بروزرسانی میکنیم.
public List<CostList> GetCostList(string searchby, string searchfor)
{
var CL = context.Costs.Join(context.Categories, costen => costen.CategoryID, caten => caten.ID, (costen, caten) => new { costen, caten }).
Select(sel => new
{
sel.costen.ID,
sel.costen.Amount,
sel.costen.Comment,
sel.costen.PaymentMethod,
sel.caten.CategoryName
}).ToList();
if(searchby == "comment" && searchfor != null)
{
CL = CL.Where(ser => ser.Comment.ToLower().Contains(searchfor.ToLower())).ToList();
}
if (searchby == "category" && searchfor != null)
{
CL = CL.Where(ser => ser.CategoryName.ToLower() == searchfor.ToLower()).ToList();
}
List<CostList> costList = new();
foreach (var cost in CL)
{
costList.Add(new CostList
{
ID = cost.ID,
Amount = cost.Amount,
Comment = cost.Comment,
PaymentMethod = cost.PaymentMethod,
CategoryName = cost.CategoryName
});
}
return costList;
}
در صورت نیاز به جزئیات بیشتر، میتوانید ویدئو آموزشی این جلسه را تماشا نمایید. همچنین برای آگاهی از جلسات بعدی این دوره آموزشی، ما را در اینستاگرام، تلگرام، یوتیوب و آپارات دنبال کنید. ضمنا لیست کامل جلسات در این قسمت در دسترس شما میباشد و سورس کد این جلسه را میتوانید از GitHub ما دانلود نمایید.
دانلود اسلایدهای آموزشی این جلسه از اینجا