Asp.Net Core ve MVC Pattern Kullanarak BookApp Projesi (Part 3)

Serap Baysal
5 min readOct 7, 2021

Merhaba, daha önce başladığım BookApp projesinin 3. bölümü ile karşınızdayım. İlk iki bölüme aşağıdaki linklerden erişebilirsiniz:

İlk olarak, daha önce oluşturduğumuz sitede, kitapların açıklamalarını kendi sayfalarında göstermek istedim. Bunun için her kitaba bir link oluşturmam gerekiyor.

Views/Home/_books.cshtml sayfasında @book.Name yazılı olan h5 etiketi içine bir a etiketi açtım ve @book.Name’i içine aldım. href ile link vermek yerine asp-action=”Details” yazarak linki oluşturdum.

a etiketinin son hali aşağıdaki gibi:

<a asp-action="Details" asp-controller="Home" asp-route-id="@book.Id">@book.Name</a>

Controller/HomeController.cs dosyasında Details isimli yeni bir action oluşturdum. Şimdilik içini doldurmadan siteyi çalıştırdığımda link görünümünün geldiğini görebiliyorum:

Tıkladığımızda tabi ki view’ı oluşturmadığım için hata veriyor. View’ı oluşturmak için Views/Home içine Details.cshtml sayfası oluşturdum. İçini Index.cshtml sayfasından kopyaladım. @model kısmını Book olarak değiştirdim, çünkü Details sayfasına sadece tek bir kitap dönecek. Bunu da controller içinde Details action’ında return değerini

Repository.GetById(id)

olarak değiştirerek sağlayacağım. Böylece Repository içindeki GetById methodunu kullanmış oldum.

col-md-9 div’inin içini temizledim. Ardından Home/_books.cshtml içinden foreach döngüsündeki şemayı alarak kopyaladım. Bazı değişiklikler yaparak düzenledim.

İlk olarak card-body isimli div’i col-md-9'un hemen altına aldım. İçine row no-gutter div’ini taşıdım ve geri kalan içeriği de bunun içine yapıştırdım. Bu değişikliklerin sebebi, resim ve bilgilerin hepsine padding uygulanmasını istemem. image’in olduğu div’i 4'e, Details’in olduğu div’i de 8'e bölerek değiştirdim.

Ardından, örnek olarak img etiketine bakarsak, src içinde ~/img/@book.ImageUrl olduğunu görüyoruz. Bunun yerine, @book.ImageUrl’i @Model.ImageUrl olarak değiştirmemiz gerekiyor. Aynı şekilde description ve diğerlerini de değiştirelim.

Değişiklikleri kaydedip çalıştırdığımda ve sitede belirli bir linke tıkladığımda örnek olarak aşağıdaki gibi bir görüntü aldım:

Sıradaki değişiklikle ana sayfayı biraz daha düzenleyeceğim. İstediğim şey, ana sayfada kitabın kısa bir açıklamasının olması, tamamının değil. Bunun için Models/Book.cs sayfasındaki modele ShortDescription isimli bir alan ekledim. Repository.cs içinde de her kitaba kısa bir açıklama yazdım. _books.cshtml içinde @book.Description yazan yeri, book.ShortDescription olarak değiştirdim.

Models üzerine sağ tıklayıp Category isimli bir class oluşturdum. İçine Id ve Name isimli proplar yazdım. Repository içindeki class’ın adını ProductRepository olarak değiştirdim ve kullandığım yerlerdeki hataları da düzelttim.

Models içine CategoryRepository class’ı oluşturdum. Aşağıdaki şekilde doldurdum:

using System.Collections.Generic;using System.Linq;namespace ChooseBookApp.Models{public static class CategoryRepository{private static List<Category> _categories = null;static CategoryRepository() {_categories = new List<Category>() {};}public static List<Category> Categories {get {return _categories;}}public static void AddCategory(Category entity){_categories.Add(entity);}public static Category GetById(int id){return _categories.FirstOrDefault(i => i.Id == id);}}}

Ardından ana dizinde Data klasörü oluşturup repository’leri içine taşıdım. Her iki repository’de de

using ChooseBookApp.Models

yazdım, namespace ismideki Model kısmını Data olarak değiştirdim. HomeController’da da Data class’ını tanımladım ve CategoryRepository içinde aşağıdaki gibi yeni türler ekledim.

new Category() {Id=1,Name="Horror"},

Sırada menüyü dinamik hale getirmek var. Bunun için Views/Shared/_menu.cshtml sayfasına gidip Category modelini ekledim. Ardından @foreach içine a etiketlerinin birini yazdım ve etiket içine @i.Name ile kategorinin ismini aldım. Bu sayfa yeni bir model bekliyor. Bu modeli vermek için Home/Index.cshtml içinde menüyü çağırdığımız html’in içine Model eklememiz gerekli. Fakat Index sayfasında Book modeli ekli. Bunu değiştirmek için HomeController’da iki modeli göndermemiz gerekiyor.

İlk olarak yeni bir class oluşturup diğer iki class’ı birleştirdim. Bunun için öncelikle Models içinde ProductCategoryModel.cs class’ını oluşturdum ve içini aşağıdaki gibi doldurdum:

using System.Collections.Generic;namespace ChooseBookApp.Models{public class ProductCategoryModel{public IEnumerable<Book> Books { get; set; }public IEnumerable<Category> Categories { get; set; }}}

Bu aşamada bir hata fark ettim. ProductRepository olan modeli BookRepository olarak değiştirdim ve gereken yerlerdeki değişimleri yaptım. Ardından HomeController içindeki Index action’ının içinde Category ve Book Repsitory’lerinin modellerini birleştirdim. Kod şu şekilde:

ProductCategoryModel model = new ProductCategoryModel();model.Categories = CategoryRepository.Categories;model.Books = BookRepository.Books;return View(model);

Sitedeki görüntü de böyle:

Burada da details sayfasına gitmek istediğimizde hata alıyoruz. Bu hatayı düzeltmek için BookCategoryModel.cs içine

public Book Book { get; set; }

ekledim. Ardından HomeController sayfasına gittim ve Details action’ında değişiklik yaptım. Details içi aşağıdaki şekildedir:

BookCategoryModel model = new BookCategoryModel();model.Categories = CategoryRepository.Categories;model.Book = BookRepository.GetById(id);

Views/Home/Details.cshtml içinde de değişiklikler yapılması gerekli. Html.Partial içinde _menu yanında virgül ve Model.Categories yazdım. Diğer @ ile belirtilen her yerde de Model.Book şeklinde bir ekleme yaptım, Model içinde direkt ImageUrl bulunamadığı için.

Menüyü hazırladık, fakat menü içinde bulunduğu view’in şekline bağımlı. Yani her defasında view’i alıp kullanması gerekiyor. Bunu değiştirmek amacıyla, menüyü ayrı bir component olarak oluşturmak istedim.

Başlangıç olarak ana dizinde ViewComponents isimli bir klasör oluşturdum. Bu klasöre CategoryMenuViewComponent isimli bir class ekledim, class’ı da ViewComponent class’ından türettiğimi belirttim.

public class CategoryMenuViewComponent:ViewComponent

Class içine public bir method ekledim ve return değeri olarak CategoryRepository içinden Categories’i döndürdüm. Method aşağıdaki gibi:

public IViewComponentResult Invoke(){return View(CategoryRepository.Categories);}

Bu işlemin ardından Views/Shared içine Components, bunun içine CategoryMenu isimli klasörler oluşturdum. CategoryMenu içine de Default.cshtml dosyası ekledim. Shared klasöründeki _menu.cshtml içeriğini kopyaladım. Default.cshtml’i, menünün kullanıldığı sayfalardan çağırmam gerekiyor, örneğin Home klasöründe Index.cshtml’de çağıralım.

Html.Partial şeklinde menüyü çağırdığımız satırı yorum satırı haline getirip (<! — — >), InvokeAsync ile oluşturuğumuz component’i çağırdım:

@await Component.InvokeAsync("CategoryMenu")

Bu aşamada, yaptığımız düzenleme sayesinde HomeController içinde hem menü için bir view hem de kitaplar için bir view göndermemiz gerekmiyor. Dolayısıyla bu dosyada sadece kitaplar view’ını göndermemiz yeterli. Kodun github linki yazı sonunda bulunmakta, buradan HomeController’ın son halini görebilirsiniz.

HomeController’da yaptığımız değişiklikler View klasörü içinde de değişikliğe sebep olmaktadır. Index sayfasında gönderdiğimiz modeli

IEnumerable<Book>

olarak, Html.Partial içindeki Model.Book kısmını Model olarak değiştiriyoruz. Aynı şekilde Details sayfasını da değiştirdim.

Daha önce yorum satırı yaptığım kısımlar hata verdi, onlara ihtiyacımız olmadığı için sildim.

Bu aşamaya kadar, menüyü kullanılır hale getirmemiştik. Artık tıkladığımızda gerekli kategorideki kitaplara nasıl ulaşabildiğimizi anlatacağım.

İlk olarak Models içindeki Book.cs sayfasında, oluşturduğumuz modele CategoryId isimli bir satır ekledim. Data/CategoryRepository’deki id’lere eş olacak şekilde BookRepository’deki tüm verilere ek olarak CategoryId ekledim.

Menüye tıklandığında url’in değişmesi gerekli, bunu Default.cshtml içindeki a etiketi ile sağladım:

<a asp-controller="Home"asp-action="Index"asp-route-id = "@i.Id"class="list-group-item list-group-item-action">@i.Name</a>

HomeController içinde filtreleme yapmamız gerekiyor. İlk olarak Index action’ının dışarıdan bir id alması lazım.

public IActionResult Index(int? id){var books = BookRepository.Books;if(id != null) {books = books.Where( i => i.CategoryId == id).ToList();}return View(books);}

Index fonksiyonunun içini yukarıdaki gibi değiştirdikten sonra uygulamayı çalıştırdığımızda gerekli değişikliğin menü içerisinde oluştuğunu görüyoruz.

Menü içinde seçim yaptığımızda, seçtiğimiz kategorinin daha belirgin olması için class’ının active yapılması gerekmektedir. Bunun için almamız gereken değer id değeridir. Hangi id seçili ise onun karşılık geldiği kategory belirgin yapılmalıdır.

CategoryMenuViewController.cs içinde ViewBag kullanarak seçili id değerini aşağıdaki kodla aldık:

ViewBag.SelectedCategory = RouteData?.Values["id"];

Default.cshtml içindeki a etiketinin class kısmının sonuna aşağıdaki kodu eklersek istediğimiz görüntüyü elde etmiş olacağız:

@(ViewBag.SelectedCategory == i.Id.ToString()?"active":" ")"

BookApp projesinin sonuna geldik. Okuduğunuz için teşekkürler, başka yazılarımda görüşmek üzere…

Projenin github linki:

https://github.com/serapbaysal/ChooseBookApp

--

--