ASP.NET Core 6/View Component'lar

View component'lar layout'lar ve partial view'larla aynı amaca hizmet ederler. View'lardaki kod tekrarını önlerler. Özellikle partial view'lara benzerler, ancak partial view'larla aralarında önemli farklar da vardır. Daha önce karmaşık bir web sayfasının içeriğini üretmek için ana view'ın devasa bir veri kümesi aldığını, bu verileri partial view'lara dağıttığını söylemiştik. Bu aslında çok da tavsiye edilen bir durum değildir. Karmaşık web sayfalarında sunulan içerikler sıklıkla değişebilir. Örneğin bir haber sitesinde ana sayfada belirli bir bölgede son dakika haberleri yerine kullanıcı yorumlarının gösterilmesi istenebilir. Bu durumda ana view'a gönderilen verilerin değişmesi gerekmektedir. Bu değişiklik ilgili view'ı renderlayan bütün action'ları da etkileyecektir. Yazılım geliştirmede ana mottolardan biri yapılan bir değişikliğin yazılımda en az yeri etkilemesini sağlamaktır. Ana view'daki partial view'lar gönderilen veriye bağımlı olmazsa karşılaşacağımız sorun büyük ölçüde ortadan kalkacaktır. Elbette ki partial view'ın ana view'a gönderilenlerden başka bir datayı render'laması teknik olarak mümkündür. Partial view C# kod bloklarında kendi iş mantığını içerebilir. Ancak bu, ana view'ın model tipini değiştirmekten daha da yanlış bir tercih olacaktır. View'lar kesinlikle iş mantığı içermemelidir. Yalnızca kendine gönderilen verileri gösterme amacıyla minimal seviyede C# kodu içermelidir.

Ayrıca bazen view component'lerin kullanılması kaçınılmazdır. Örneğin bir alışveriş sitesi geliştirdiğimizi düşünelim. Her sayfada sağ üstte sepetteki ürün sayısı ve ürünlerin toplam tutarı yazması gereksin. Bunu nasıl yapabiliriz? Kuşkusuz bu sepet bilgisini gösterecek header bir layout'ta tutulacaktır. Layout'ların kesinlikle iş mantığı içermemesi gerekir. View component'ları seçenekler arasından çıkardığımızda tek seçenek ilgili layout'u kullanan her view'ın layout'ta section olarak tanımlanan kısma ilgili içeriği göndermesidir. Bu, kod tekrarı açısından korkunç bir şeydir. İşte burada view component'lar imdada yetişiyor. View component'lar partial view'ların iş mantığı içerebilen versiyonlarıdır. Partial view'lar esasında sadece bir view'dır ve kendisine verilen veriyi göstermekle mükelleftir. View component'lar Razor sayfalarında olduğu gibi hem iş mantığı hem de view içerirler. Diğer bir deyişle kendi verilerini kendileri oluştururlar ve kendi view'larını render'larlar. Kullanıldıkları ana view'dan veri almazlar.

Şimdi projenizdeki Models klasörüne ismi City.cs olan ve içeriği aşağıdaki gibi olan model sınıfını ekleyin:

namespace WebApp.Models {
    public class City {
        public string Name { get; set; }
        public string Country { get; set; }
        public int Population { get; set; }
    }
}

Bu, basit bir model sınıfıdır ve şehir bilgilerini içermektedir. Şimdi projenizin Models klasöründe CitiesData.cs isimli dosyayı oluşturun ve içeriği şöyle olsun:

namespace WebApp.Models {
    public class CitiesData {
        private List<City> cities = new List<City> {
            new City { Name = "London", Country = "UK", Population = 8539000},
            new City { Name = "New York", Country = "USA", Population = 8406000 },
            new City { Name = "San Jose", Country = "USA", Population = 998537 },
            new City { Name = "Paris", Country = "France", Population = 2244000 }
        };
        public IEnumerable<City> Cities => cities;
        public void AddCity(City newCity) {
            cities.Add(newCity);
        }
    }
}

Bu sınıf ise model verilerine erişim ve değiştirme amacıyla kullanılacak sınıftır. Bu sınıfı programımızın her yerinden kolayca erişilebilmesi için servis olarak tanımlamalıyız. Şimdi aşağıdaki servis çağrısını Program.cs dosyasındaki servis aboneliklerinin yapıldığı kısma ekleyiniz:

builder.Services.AddSingleton<CitiesData>();

Burada servisin singleton bir servis olarak tanımlanması önemlidir. Bu sayede hep aynı City kolleksiyonu üzerinde çalıştığımız için bir request'te bu koleksiyona yaptığımız eklemeleri daha sonraki request'lerde de görebileceğiz. Şimdi Pages klasörüne içeriği aşağıdaki gibi olan ve ismi Cities.cshtml olan bir view ekleyelim.

@page
@inject CitiesData Data
<div>
    <table>
        <tbody>
            @foreach (City c in Data.Cities) {
                <tr>
                    <td>@c.Name</td>
                    <td>@c.Country</td>
                    <td>@c.Population</td>
                </tr>
            }
        </tbody>
    </table>
</div>

Burada ilgili view'ın sadece bir kısmı gözükmektedir. View, geri kalan kısmını _ViewStart.cshtml dosyasında belirtilen layout'tan almaktadır. View basitçe servisten aldığı şehir bilgilerini sayfaya yazmaktadır.

View component'ın oluşturulması

değiştir

İsmi ViewComponent ile bitip Invoke() veya InvokeAsync() metotlarını içeren veya ViewComponent ana sınıfından türeyen veya ViewComponent attribute'u ile işaretlenen sınıflar view component sayılır. View component'lar teknik olarak proje içinde herhangi bir yerde tanımlanabilmesine rağmen geleneksel olarak Components klasöründe tanımlanır. Şimdi Components klasöründe CitySummary.cs isimli bir dosya oluşturalım ve içeriği şöyle olsun:

using Microsoft.AspNetCore.Mvc;
using WebApp.Models;
namespace WebApp.Components
{
    public class CitySummary : ViewComponent
    {
        private CitiesData data;
        public CitySummary(CitiesData cdata)
        {
            data = cdata;
        }
        public string Invoke()
        {
            return $"{data.Cities.Count()} cities, " + $"{data.Cities.Sum(c => c.Population)} people";
        }
    }
}

Bu sınıf bir view component'tır. Çünkü ViewComponent sınıfından türemiştir. View component'larda da controller'larda olduğu gibi yapıcı metot yoluyla servis enjeksiyonu yapılabilmektedir. Bu view component CitiesData servisindeki verilerdeki şehir sayısını ve şehirlerin toplam nüfusunu düz metin olarak üretmektedir. View component'ın ürettiği bu metin herhangi bir view'da rahatça kullanılabilecektir.

View component'ın kullanılması

değiştir

View component'ı herhangi bir view'da aşağıdaki şekilde kullanabiliriz:

@await Component.InvokeAsync("CitySummary")

View'da bu kodu kullandığımız yere "4 cities, 20187537 people" çıktısı verilecektir. Burada aslında CitySummary isimli view component'ın Invoke() metodu çalıştırılmaktadır. ASP.NET Core view component'ta InvokeAsync() metodu olmaması durumunda InvokeAsync() metot çağrılarını Invoke() metoduna yönlendirir. Ancak böyle bir durum da metodun @await direktifiyle beraber çalıştırılması gerekir. Direkt Invoke() metodunu çağırarak view component'lar kullanılamaz. Dolayısıyla bir view component'ta ya Invoke() ya da InvokeAsync() metodu olmalıdır. İkisi beraber olamaz. Ayrıca söz konusu view component'a kullandığımız view'da direkt erişebilmeniz için aşağıdaki satırı ilgilin view'ın kullandığı _ViewImports.cshtml dosyasına veya view'ın kendisine ekleyin:

@using WebApp.Components

View component'a tag helper kullanarak erişme

değiştir

Tag helper vasıtasıyla view component'a erişebilmek için öcelikle aşağıdaki satırı view'ın kullandığı _ViewImports.cshtml dosyasına veya view'ın kendisine ekleyin

@addTagHelper *, WebApp

Bu kodla WebApp isim alanının ve altındaki isim alanlarında tanımlanan bütün view component'ları tag helper vasıtasıyla kullanabileceğiz. Şimdi ilgili view'da view component'ı kullanmak istediğiniz yere aşağıdaki kodu yazın:

<vc:city-summary />

Bu kod InvokeAsync() metot çağrısıyla aynı işleve sahiptir. View component sınıf ismi olan "CitySummary"nin "city-summary"ye nasıl değiştiğine dikkat edin.

View component'ların geri dönüş tipleri

değiştir

Şimdiye kadar Invoke() metodu string döndürdü, biz döndürülen string'i ilgili view'a yerleştirdik. Ancak view component'lar bundan çok daha fazlasıdır. View component'lardaki Invoke() veya InvokeAsync() metotları IViewComponentResult arayüzünü uygulamış bir sınıfla geri dönebilirler. Bu arayüzü uygulamış dahili üç sınıf vardır. Bu sınıflar ve bu sınıf türünden nesne üretmek için çağrılması gereken metotlar aşağıdaki listede verilmiştir:

ViewViewComponentResult: Bir view render'lamak için kullanılır. Bu sınıf türünden nesne üretmek için opsiyonel bir model nesnesi parametresiyle beraber View() metodu kullanılır.
ContentViewComponentResult: Düz string döndürmek için kullanılır. Düz string'deki HTML kodları HTML olarak algılanmayacak şekilde kodlanır. Bu sınıf türünden nesne oluşturmak için Content() metot çağrısı kullanılır. Bir Invoke() veya InvokeAsync() metodunun string döndürmesi durumunda arkaplanda bu sınıf ve metot kullanılır.
HtmlContentViewComponentResult: HTML kodlarının HTML olarak algılandığı, dolayısıyla ekstra bir kodlamanın yapılmadığı düz string oluşturmak için kullanılır. Bu sınıf türünden nesne oluşturabilecek ViewComponent sınıfından devralınmış herhangi bir metot yoktur. new anahtar sözcüğüyle yeni sınıf nesnesi oluşturulmalıdır.

Bir partial view döndürme

değiştir

Kuşkusuz yukarıdaki listedeki sınıf ve metotlardan en faydalı olanı ViewViewComponentResult sınıfı ve View() metodudur. View() metodunun aşırı yüklenmiş aşağıdaki versiyonları bulunmaktadır:

View(): View component'ın varsayılan view'ı render'lanır. Bir model nesnesi sağlanmaz.
View(model): View component'ın varsayılan view'ı parametrede verilen model nesnesiyle render'lanır.
View(viewName): Belirtilen view render'lanır. Model nesnesi sağlanmaz.
View(viewName, model): Belirtilen view belirtilen model nesnesiyle beraber render'lanır.

Bir view component istediği view'ı seçebilir ve model olarak da view'ın kabul ettiği model nesnesini gönderebilir. Bu bakımdan bir controller action'ının view seçmesi ve model göndermesiyle arasında büyük benzerlik vardır. Ancak view component'ların view arama süreci controller'lardan biraz farklıdır. Aşağıda bu süreç verilmiştir.

View component'ların view arama süreci

değiştir

View component'ların view arama süreci bir MVC view'ı veya Razor sayfasında kullanılıp kullanılmadığına göre değişmektedir. Eğer bir view component View() metodunda parametre vermeden varsayılan view'ı çağırdıysa ve eğer view component bir controller tarafından çağrılmış bir view'da kullanıldıysa sırasıyla şu konumlar aranır:

  1. /Views/<controller>/Components/<viewcomponent>/Default.cshtml
  2. /Views/Shared/Components/<viewcomponent>/Default.cshtml
  3. /Pages/Shared/Components/<viewcomponent>/Default.cshtml

Eğer bir view component View() metodunda parametre vermeden varsayılan view'ı çağırdıysa ve eğer view component bir Razor sayfasında kullanıldıysa sırasıyla şu konumlar aranır:

  1. /Pages/Components/<viewcomponent>/Default.cshtml
  2. /Pages/Shared/Components/<viewcomponent>/Default.cshtml
  3. /Views/Shared/Components/<viewcomponent>/Default.cshtml

Razor sayfalarında ilgili Razor sayfasının direkt Pages klasöründe değil de bir alt klasörde tanımlanması da söz konusudur. Eğer view component bir Razor sayfasında kullanıldıysa ve bu Razor sayfası örneğin /Pages/Deneme/Deneme2 klasöründe tanımlandıysa sırasıyla şu konumlar aranır:

  1. /Pages/Deneme/Deneme2/Components/<viewcomponent>/Default.cshtml
  2. /Pages/Deneme/Components/<viewcomponent>/Default.cshtml
  3. /Pages/Components/<viewcomponent>/Default.cshtml
  4. /Pages/Shared/Components/<viewcomponent>/Default.cshtml
  5. /Views/Shared/Components/<viewcomponent>/Default.cshtml

View() metodunu bir view ismi belirtecek şekilde çağırırsak yine aynı konumlarda arama yapılır. Ancak bu sefer aranılan view'ın ismi Default değil, belirtilen isim olur.

View component'ların HTML döndürmesi

değiştir

Daha önce de söylediğimiz gibi view component'lar bir view yanında direkt HTML de döndürebilirler. Örnek (CitySummary.cs):

using Microsoft.AspNetCore.Mvc;
using WebApp.Models;
using Microsoft.AspNetCore.Mvc.ViewComponents;
using Microsoft.AspNetCore.Html;
namespace WebApp.Components
{
    public class CitySummary : ViewComponent
    {
        private CitiesData data;
        public CitySummary(CitiesData cdata)
        {
            data = cdata;
        }
        public IViewComponentResult Invoke()
        {
            return new HtmlContentViewComponentResult(new HtmlString("This is a <h3><i>string</i></h3>"));
        }
    }
}

Gördüğünüz gibi HtmlContentViewComponentResult sınıfının yapıcı metoduna bir HtmlString nesnesi vermeliyiz. Bu örneğimizdeki HtmlString yapıcı metot çağrısındaki string gerçekten HTML olarak ele alınır. Encoding yapılmaz.

View component'ta bağlam bilgisine erişme

değiştir

View component'lerin türediği ViewComponent sınıfı bizim için faydalı olabilecek özellikler içermektedir. Bu özellikler Invoke() veya InvokeAsync() metodunun içinde kullanılabilir. Bu özelliklerden önemli olanları aşağıdaki listede verilmiştir:

HttpContext: Geçerli request'in HttpContext nesnesini döndürür.
Request: Geçerli request'in HttpRequest nesnesini döndürür.
User: Request'i yapan kullanıcı bilgilerini döndürür.
RouteData: Rota değişkenlerine erişim imkanı sağlar.
ViewBag: ViewBag'e erişim sağlar. ViewBag kullanılarak view component'ın lojik kısmından görünüm kısmına alternatif bir şekilde veri gönderilebilir.
ModelState: Forma girilen bilgilerin geçerli olup olmadığını gösteren ModelStateDictionary nesnesini döndürür.
ViewData: ViewData'ya erişim sağlar. ViewData ile çeşitli bileşenler arasında veri taşınabilir.

View component'ların ana view'dan parametre alması

değiştir

View component'lar içinde kullanıldıkları view'dan parametre alabilirler. Bu paremetre view component'ın çalışmasını özelleştirebilir. Parametre, view component'ın Invoke() veya InvokeAsync() metodunda standart parametre olarak belirtilir. Örneğin bir view component'ın Invoke() metodu şöyle değiştirilebilir:

public IViewComponentResult Invoke(string viewName) {
    if(viewName == "1")
        return View("Birinci");
    else return View("Ikinci");
}

Burada ana view'ın gönderdiği parametreye göre iki olası view'dan biri render'lanmaktadır. Bir view component'ın Invoke() veya InvokeAsync() metodu opsiyonel olmayan bir parametre tanımladığı zaman bu parametreye değer mutlaka ana view'da verilmelidir. Bir view component'a parametre gerekmesine rağmen parametre verilmezse çalışma zamanı hatası oluşmaz, ama view component çalışmaz ve view component'ın çıktı üretmesi gereken yer boş gelir. Ana view'da ilgili parametreye şöyle değer verilir:

<vc:city-summary view-name="1" />

Yine "view-name"in nasıl "viewName"e dönüştüğüne dikkat edin. Eğer view component çağırmak için InvokeAsync() metodunu kullandıysak parametreyi şöyle belirtiriz:

@await Component.InvokeAsync("CitySummary", new { viewName = "1" })

Invoke() veya InvokeAsync() metodunun opsiyonel parametre alması

değiştir

Invoke() ve InvokeAsync() metotları sağlanmayan parametreler için bir varsayılan değer belirterek ilgili parametrenin opsiyonel olmasını sağlayabilir. Örnek:

public IViewComponentResult Invoke(string viewName="1") {
    if(viewName == "1")
        return View("Birinci");
    else return View("Ikinci");
}

Bu durumda view component'ı kullanan ana view, view component'a parametre vermek zorunda değildir.

Hibrit view component'lar

değiştir

Daha önce ViewComponent attribute'u içeren sınıfların view component varsayılacağını söylemiştik. Bu attribute controller sınıflarına veya Razor sayfalarının model sınıflarına da eklenebileceği için bir controller veya Razor model sınıfı aynı zamanda bir view component olabilir. Birisi controller, birisi Razor model sınıfı olmak üzere burada iki durum için de örnek kullanım verilecektir. Ancak öncelikle Models klasörüne ismi CityViewModel olan ve içeriği aşağıdaki gibi olan bir view model sınıfı ekleyin:

namespace WebApp.Models {
    public class CityViewModel {
        public int Cities { get; set; }
        public int Population { get; set; }
    }
}

View model sınıfları controller'ların view'a göndereceği birden fazla veri olduğunda bütün bu verileri tek bir sınıfta paketlemek için kullanılan sınıflardır. View, model olarak ilgili view model sınıfını alacak ve ilgili view modelde paketlenmiş bütün verilere erişecektir. Bu örneğimizde toplam şehir sayısını ve şehirlerin toplam nüfuslarını tek bir nesnede paketlemek için bir view model sınıfı oluşturulmuştur. View model sınıfları geleneksel olarak Models klasörünün ViewModels alt klasöründe oluşturulmasına rağmen bu örneğimizde pratiklik olması açısından direkt Models klasöründe oluşturulmuştur.

Razor model sınıflarının view component olarak kullanılması

değiştir

Dersimizin en başında Cities.cshtml isimli bir Razor view'ı oluşturmuştuk. Bu view'ın bir model sınıfı yoktu. View CitiesData servisini kendine enjekte ediyordu. Şimdi bu view'a bir model sınıfı ekleyeceğiz ve view'ı biraz değiştireceğiz. View'a bir model sınıfı eklemek için şimdi Pages klasöründe Cities.cshtml.cs isimli bir dosya ekleyin ve içeriği aşağıdaki gibi olsun. Eğer Cities.cshtml dosyasının altında böyle bir dosya varsa direkt içine girin.

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.ViewComponents;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using WebApp.Models;
namespace WebApp.Pages
{
    [ViewComponent(Name = "CitiesPageHybrid")]
    public class CitiesModel : PageModel
    {
        public CitiesModel(CitiesData cdata)
        {
            Data = cdata;
        }
        public CitiesData Data { get; set; }
        [ViewComponentContext]
        public ViewComponentContext Context { get; set; } = new();
        public IViewComponentResult Invoke()
        {
            return new ViewViewComponentResult()
            {
                ViewData = new ViewDataDictionary<CityViewModel>(
                    Context.ViewData,
                    new CityViewModel { Cities = Data.Cities.Count(), Population = Data.Cities.Sum(c => c.Population) }
                )
            };
        }
    }
}

Öncelikle bu Razor model sınıfına ViewComponent attribute'unu ekledik ve attribute'a parametre olarak bu view component'a hangi isimle erişmek istediğimiz belirttik. Bu view component ViewComponent sınıfından türemediği için normalde ana sınıftan devraldığımız bağlam özelliklerine erişmek için ViewComponentContext tipinde ve Context isminde bir özellik tanımladık ve bu özelliği ViewComponentContext attribute'u ile işaretledik. Bu bağlam özelliklerinin en önemlileri daha önce de gördüğümüz gibi HttpContext, Request, User, RouteData, ViewBag, ModelState ve ViewData'dır. Sınıfımız ana sınıf olarak ViewComponent'ı kullanmadığı için çağırabileceğimiz bir View() metodu yoktur. Bunun yerine bir view render'lamak istediğimizi söylemek için ViewViewComponentResult sınıfının yapıcı metodunu çağırmaktayız. Yeni ViewViewComponentResult nesnesi oluşturmak için verdiğimiz parametreler karmaşık gelebilir. Ancak basitçe

return new ViewViewComponentResult()
{
    ViewData = new ViewDataDictionary<CityViewModel>(
        Context.ViewData,
        new CityViewModel { Cities = Data.Cities.Count(), Population = Data.Cities.Sum(c => c.Population) }
    )
};

kodları

return View(new CityViewModel { Cities = Data.Cities.Count(), Population = Data.Cities.Sum(c => c.Population) });

koduna karşılık gelmektedir. Şimdi bu model sınıfının view'ını ise şöyle değiştirelim (Cities.cshtml):

@page
@model WebApp.Pages.CitiesModel
<div>
	<table>
		<tbody>
			@foreach (City c in Model.Data.Cities)
			{
				<tr>
					<td>@c.Name</td>
					<td>@c.Country</td>
					<td>@c.Population</td>
				</tr>
			}
		</tbody>
	</table>
</div>

Razor model sınıfının view kısmı modelindeki Data özelliğinden şehir bilgilerine erişmekte ve bu şehirlerin bilgilerini ekrana yazdırmaktadır. View, doğal olarak model sınıfının view component olma özelliğini hiç kullanmamıştır. Şimdi yukarıdaki hibrit view component'ın view'ını oluşturmaya sıra geldi. View component Pages klasöründe oluşturulduğuna göre view component'ın kullanacağı view'ı Pages/Shared/Components/CitiesPageHybrid klasöründe oluşturalım, ismi Default.cshtml ve içeriği aşağıdaki gibi olsun:

@model CityViewModel
<table>
<thead><tr><th colspan="2">Hybrid Page Summary</th></tr></thead>
<tbody>
	<tr>
		<td>Cities:</td><td>@Model.Cities</td>
	</tr>
		<tr>
			<td>Population:</td>
			<td>
				@Model.Population.ToString("#,###")
			</td>
		</tr>
	</tbody>
</table>

Bu view, view component'in kendisine gönderdiği şehir sayısı ve toplam nüfus bilgisini basitçe tablo halinde ekrana yazmaktadır. Şimdi Pages klasöründe Data.cshtml isminde bir Razor sayfası view'ı oluşturun ve içeriği şöyle olsun:

@page
@inject DataContext context;
<h5>Categories</h5>
<ul>
	@foreach (Category c in context.Categories) {
		<li>@c.Name</li>
	}
</ul>
<div>
<vc:cities-page-hybrid />
</div>

Bu Razor sayfasının kod kısmı yoktur. DataContext servisini kendine enjekte etmektedir ve bu servis üzerinden kategorileri ekrana yazdırmaktadır. Aynı zamanda alt kısımda başka bir veri kaynağından beslenen yukarıda oluşturduğumuz view component'ı render'lamaktadır.

Controller'ların view component olarak kullanılması

değiştir

Benzer şekilde controller'lar da view component olarak kullanılabilir. Şimdi projemizdeki Controllers klasörüne ismi CitiesController.cs olan ve içeriği aşağıdaki gibi olan dosyayı ekleyelim:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewComponents;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using WebApp.Models;
namespace WebApp.Controllers
{
    [ViewComponent(Name = "CitiesControllerHybrid")]
    public class CitiesController : Controller
    {
        private CitiesData data;
        public CitiesController(CitiesData cdata)
        {
            data = cdata;
        }
        public IActionResult Index()
        {
            return View(data.Cities);
        }
        public IViewComponentResult Invoke()
        {
            return new ViewViewComponentResult()
            {
                ViewData = new ViewDataDictionary<CityViewModel>(
                    ViewData,
                    new CityViewModel
                    {
                        Cities = data.Cities.Count(),
                        Population = data.Cities.Sum(c => c.Population)
                    }
                )
            };
        }
    }
}

Razor sayfalarından farklı olarak controller sınıfında ViewComponentContext attribute'u ile işaretlenmiş bir özellik kullanmadık. ViewDataDctionary sınıfının yapıcı metoduna parametre verirken direkt Controller sınıfından devralınan ViewData özelliğini kullandık. Şimdi bu controller tarafından kullanılacak view'ı oluşturmak için Views/Cities klasöründe Index.cshtml isminde bir view oluşturalım ve içeriği şöyle olsun:

@model IEnumerable<City>
@{
	Layout = "_ImportantLayout";
}
<div>
	<table>
		<tbody>
			@foreach (City c in Model)
			{
				<tr>
					<td>@c.Name</td>
					<td>@c.Country</td>
					<td>@c.Population</td>
				</tr>
			}
		</tbody>
	</table>
</div>

Bu view, City ardılı şeklinde aldığı City nesnelerinin özelliklerini sayfaya yazdırmaktadır. Şimdi view component için view oluşturmaya sıra geldi. Şimdi Views/Shared/Components/CitiesControllerHybrid klasöründe, Default.cshtml isminde ve içeriği aşağıdaki gibi olan bir view oluşturalım:

@model CityViewModel
<table>
	<thead><tr><th colspan="2">Hybrid Controller Summary</th></tr></thead>
	<tbody>
		<tr>
			<td>Cities:</td>
			<td>@Model.Cities</td>
		</tr>
		<tr>
			<td>Population:</td>
			<td>
				@Model.Population.ToString("#,###")
			</td>
		</tr>
	</tbody>
</table>

Bu view aldığı CityViewModels nesnesindeki şehir sayısı ve toplam nüfusu gösteren özelliklerin değerlerini ekrana yazdırmaktadır. Şimdi bu view component'i Pages klasöründeki Data.cshtml dosyasında kullanalım:

@page
@inject DataContext context;
<h5>Categories</h5>
<ul>
	@foreach (Category c in context.Categories)
	{
		<li>@c.Name</li>
	}
</ul>
<div>
	<vc:cities-controller-hybrid />
</div>

Burada da view component'ı Data.cshtml Razor view'ının kendine enjekte ettiği DataContext servisinden bağımsız bir veri kaynağından alınan verilerin toplam bilgisini yazdırmak için kullandık.