ekrem özer

her yerde olan şeyler.

.Net Core MVC Tema Desteği

Merhaba arkadaşlar bu makalemde .net core mvc de geliştirdiğiniz projelere tema desteği nasıl ekleyebiliriz ondan bahsedeceğim. Ben örnek projede tema ismini appsettings.json dan okuyacağım siz projenize göre bu kısmı revize edebilirsiniz. Öncelikle appsettingse tema adımı okuyacağım node'u ekliyorum.

"ThemeName": "White"

Sonra projeme Infrustructure adında bir klasör ekleyip içine ThemeableViewLocationExpander adında bir class ekliyorum. Bu classı yine projenize göre istediğiniz katman veya klasöre ekleyebilirsiniz.

namespace NetCoreThemeable.Web.Infrastructure
{
    public class ThemeableViewLocationExpander : IViewLocationExpander
    {
        private const string ThemeKey = "ThemeName";
        private readonly IConfiguration _configuration;
        public ThemeableViewLocationExpander(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        public void PopulateValues(ViewLocationExpanderContext context)
        {
            if (context.AreaName?.Equals("Admin") ?? false)
                return;

            context.Values[ThemeKey] = _configuration[ThemeKey];
        }

        public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
        {
            if (context.Values.TryGetValue(ThemeKey, out var themeName))
            {
                viewLocations = new[] {
                        $"/Themes/{themeName}/Views/{{1}}/{{0}}.cshtml",
                        $"/Themes/{themeName}/Views/Shared/{{0}}.cshtml",
                    }
                    .Concat(viewLocations);
            }

            return viewLocations;
        }
    }
}

Classımı Microsoft.AspNetCore.Mvc.Razor namespace'inin altındaki  IViewLocationExpander interface'inden türetmem gerekiyor. Interface'imize bağlı iki tane implemente etmemiz gereken metodu da ekledikten sonra. string türünde ThemeKey ve appseting.json'a erişmem için IConfiguration türünde _configuration değişkenlerimi ekliyorum. Consructor metodumu ekleyerek parametre olarak IConfiguration nesnesini veriyorum ve içerisinde _configuration değişkenimi dolduruyorum. Şiimdi classın içinde ki metodladı inceleyelim.

public void PopulateValues(ViewLocationExpanderContext context)
{
	if (context.AreaName?.Equals("Admin") ?? false)
		return;

	context.Values[ThemeKey] = _configuration[ThemeKey];
}

PopulateValues metodunda context nesnemin ThemeKey value'sunu dolduruyorum. Burada area'ların etkilenmemesi için eğer contextten gelen area name boş değilse ve Admin ise return edip, bu areada tema desteğinin çalışmamasını sağlıyorum. Ben projeye örnek olması için birde Admin area ekledim. Yukarıda tanımladığımız ThemeKey değişkeni contextin valuesine erişmek ve set etmek için oluşturduğumuz bir sabir değişkendir. Bu metod context nesnemizdeki ilgili value'yu set etti.

public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
{
	if (context.Values.TryGetValue(ThemeKey, out var themeName))
	{
		viewLocations = new[] {
				$"/Themes/{themeName}/Views/{{1}}/{{0}}.cshtml",
				$"/Themes/{themeName}/Views/Shared/{{0}}.cshtml",
			}
			.Concat(viewLocations);
	}

	return viewLocations;
}

ExpandViewLocations metodunda yukarıda set ettiğim değeri contextten okuyup View klasörünün yolunu set ediyor. if bloğunda eğer context nesnedinden ThemeKey valuesini okuyabilirse set ediyor yoksa varsayılan değeri dönüyor. Burada göreceğiniz üzere temaları root dizinde oluşturduğumuz Themes klasöründen okuyacak.

Şimdi startup.cs de yapacağımız değişikliklere geçmeden önce yüklememiz gereken bir nuget package var onu yükleyelim.

Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation

Bu nuget ismindende anlaşılacağı gibi razor sayfaları çalışma zamanında derleyebilir, örneğin debug moddayken viewda yapacağınız bir değişikliği f5 yaparak göremezsiniz projeyi durdurup tekrar derlemeniz lazım. Ancak bu nuget'ı kurduktan sonra bu sorun ortadan kalkacak ve viewlarınız runtime'da da derlenebilecek.

Şimdi Startup.cs e ilgili eklemeleri yapalım.

public void ConfigureServices(IServiceCollection services)
{
	services.AddMvc();
  
	services.AddRazorPages().AddRazorRuntimeCompilation();
	services.Configure<RazorViewEngineOptions>(options =>
	{
		options.ViewLocationExpanders.Add(new ThemeableViewLocationExpander(Configuration));
	});
}

ConfigureServices metoduna önce AddRazorRuntimeCompilation() metodunu ekledim. Sonra services.Configure metoduyla RazorViewEngineOptions'ın artık benim yaptığım classı referans alıp çalışmasını sağladım. options.ViewLocationExpanders'a classımızı verirken parametre olarak Configuration'ı verdik, bunun sebebi classın constructor metodunda bu parametreyi bizden istemesi. Eğer siz tema adını appsettings'den okumayacaksanız yukarıda bahsettiğim gibi bu parametreye ihtiyacınız yok.

Şuan tema desteğimiz için herşey hazır, ama yapmamız gereken bir işlem daha var. .net core mvc projelelerinde publish aldığımızda views klasörleri publishte gelmiyor. [ProjectName].Views.dll den okuyor. Bunun önüde geçmek için projemize ait csproj dosyasında bir düzenleme yapmamız lazım. Bu dosya bizim projemizde NetCoreThemeable.Web.csproj Bu dosyayı açtığımızda xml yapısında çeşitli ayar node'ları görüyoruz. Yapmamız gereken PropertyGroup nodu'una aşağıdaki 4. 5. ve 6. satırladı eklemek.

<PropertyGroup>
	<TargetFramework>netcoreapp3.1</TargetFramework>
	<!--Publis views folders-->
	<CopyRazorGenerateFilesToPublishDirectory>true</CopyRazorGenerateFilesToPublishDirectory>
	<AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
	<AspNetCoreModuleName>AspNetCoreModule</AspNetCoreModuleName>
	<!--#Publis views folders-->
</PropertyGroup>

Bu makaleninde sonunda geldik, umarım faydalı olmuştur. Projenin kaynak kodlarına buradan ulaşabilirsiniz.

https://github.com/ekremozer/NetCoreThemeable